UI
Add UI and head tags to pages from a plugin.
Slots
This plugin adds a share button below the title of every content page:
import { ShareButton } from "./share-button";
type Types = { slots: "acme:share" };
export function sharePlugin<C extends AppShape = AppShape>(): Plugin<C, Types> {
return {
name: "acme:share",
slots({ page }) {
if (!page) return;
return [
{
name: "acme:share",
region: "actions",
component: ShareButton,
props: { url: this.absoluteUrl(page.url) },
},
];
},
};
}slots runs with page for the items of a content page, and without page for the items of every page. Each item has a name, unique on the site, written as <plugin>:<item>. List the names in the slots of your plugin types. Names starting with core: belong to Fumapress.
The built-in UI places an item in its region. A custom layout places it by name, with props.slots["acme:share"].
To translate the label, add your strings with the translations field. See the Fumadocs Translations API.
Regions
The regions marked for content pages take items from the call with page, the others from the call without. An item in a region of the other call is an error:
layout-wrapper
Around every page, inside the root provider. For providers.
banner
Above every page, custom pages and the 404 page included.
sidebar-banner, sidebar-footer
Above and below the sidebar.
actions
Content pages: the row of buttons below the title, with Copy Markdown.
body-wrapper
Content pages: around the content.
footer
Content pages: after the content.
toc-header, toc-footer
Content pages: above and below the table of contents.
ui.renderPage receives the items of every region but the wrappers and banner as props.slots. A layout without a region doesn't render its items, like the Home Layout of plugin pages, which has none of them.
Wrapper Slots
A wrapper receives what it wraps as children:
slots({ page }) {
if (page) return [{ name: "acme:prose", region: "body-wrapper", component: Prose }];
return [{ name: "acme:theme", region: "layout-wrapper", component: ThemeProvider, order: -10 }];
},Two wrappers in one region are nested by order, with the lowest on the outside. Context providers use a negative order.
Layout Props
layoutProps adds to the layout props of every layout, like links, sidebar and tabs. It runs for each page, with page on content pages. Its links come after the links of the site, and for other props the values of the site take priority.
layoutProps({ lang }) {
return {
links: [{ url: this.localizePath(lang, "/showcase"), text: "Showcase" }],
};
},providerProps does the same for the root provider.
Head Tags
head runs once for the whole site without page or route, and once for each page. A tag from the head option of the site replaces a plugin tag with the same key, like a meta tag of the same name.
head({ page, route }) {
if (page) return [{ tag: "meta", attrs: { name: "author", content: "acme" } }];
if (route) return;
return [{ tag: "meta", attrs: { name: "generator", content: "acme" } }];
},Rendering Content Pages
To show the pages of one source differently, write getPageView. This plugin gives each page of the reviews source a star rating and its own UI:
type Types = {
pages: { reviews: { stars: number } };
};
export function reviewsPlugin<C extends AppShape = AppShape>(): Plugin<C, Types> {
return {
name: "acme:reviews",
getPageView(page, body) {
if (page.type !== "reviews") return;
const stars = Number(Reflect.get(page.data, "stars") ?? 0);
return {
props: { stars },
node: (
<article>
<p>{"★".repeat(stars)}</p>
{body?.node}
</article>
),
};
},
};
}pagesmaps a source key to the props that the plugin adds.page.typeis the source of the page. Returnundefinedfor pages of other sources.body.nodeis the content of the page.propsare passed toui.renderPageas they are, andnodebecomesprops.children.
Without a ui.renderPage, node shows in the Home Layout.
Page Rendering Order
Each step receives the result of the one above. Write the hook of the step you want to change.
getPageBody(page){ node }The content. The content source provides it, so only write this hook for a custom source.
body-wrapper slotsprops.bodyWrap the content of every source.
getPageView(page, body)props.childrenAdds props and UI to the pages of one source.
ui.renderPage(props)the pageSet by the site. It renders everything above in a layout.
Page Data
transformPage changes the page data of every page: content pages, plugin pages and custom pages. A layout reads it from props.pageData, like the title or the width, and <head> gets its title and description. Return only the fields you change. Two plugins changing the same field is an error.
transformPage(data) {
return { full: data.url.startsWith("/reference/") };
},It receives page on content pages, and route with path on other pages. Read the page data of any page with this.getPageData().
Last updated on