How do I create a custom layout with compose?

dev
f150307Waku: fix default config
This post might have stale content, as dev is 36 commits ahead.
Avatar of JoyboyJoyboyposted

Trying to go beyond the default use case and go for a compose approach to create a custom layout for docs layout and docs page

Avatar of Gemini 3 FlashGemini 3 Flashcommented

To go beyond the default use case in Fumadocs and adopt a "compose" approach, you can break down the monolithic components into their constituent parts. This gives you granular control over where custom elements (like feedback forms, AI chat triggers, or custom headers) are placed.

###1. Composing the Docs Page

Fumadocs provides sub-components for the documentation page that you can manually arrange within the <DocsPage /> wrapper. This is the recommended way to add custom elements between the title, description, and body.

import { 
  DocsPage, 
  DocsBody, 
  DocsTitle, 
  DocsDescription, 
  PageLastUpdate 
} from 'fumadocs-ui/page'; // or 'fumadocs-ui/layouts/docs/page'

export default function Page({ page }) {
  const { body: Content, toc, lastModified } = await page.data.load();

  return (
    <DocsPage toc={toc} full={page.data.full}>
      <DocsTitle>{page.data.title}</DocsTitle>
      <DocsDescription>{page.data.description}</DocsDescription>
      
      {/* Custom element between header and body */}
      <MyCustomBanner /> 

      <DocsBody>
        <Content />
      </DocsBody>

      {/* Custom footer elements */}
      <MyFeedbackComponent />
      {lastModified && <PageLastUpdate date={new Date(lastModified)} />}
    </DocsPage>
  );
}

###2. Composing the Docs Layout

For the high-level DocsLayout, you have two main paths for a "compose" approach:

####Option A: Prop-based Composition

You can inject custom components into the existing layout grid using the sidebar and nav props. This maintains the built-in grid system (which handles sticky headers and sidebar collapsing automatically).

<DocsLayout 
  tree={tree}
  nav={{
    component: <MyCustomNavbar />, // Completely replace the navbar
    children: <ExtraNavItems />,    // Add items to the default navbar
  }}
  sidebar={{
    banner: <MySidebarHeader />,   // Above the page tree
    footer: <MySidebarFooter />,   // Below the page tree
    components: {
      Item: MyCustomSidebarItem,    // Override specific tree elements
    }
  }}
>
  {children}
</DocsLayout>

####Option B: Full Ejection (The "CLI" Approach)

If you want to build the layout from scratch using the primitive components (like SidebarProvider, LayoutBody, and LayoutHeader), the intended path is to use the Fumadocs CLI to "eject" the layout code into your project:

pnpm fumadocs add layouts/docs

This will copy the source code for the layout into your components/layout/docs folder. You can then modify the index.tsx to rearrange the components manually:

// Inside your ejected layout/docs/index.tsx
<TreeContextProvider tree={tree}>
  <SidebarProvider>
    <LayoutBody>
      <header>...</header>
      <Sidebar />
      <main>{children}</main>
    </LayoutBody>
  </SidebarProvider>
</TreeContextProvider>

###Key Primitives

If you are building a custom layout manually, keep in mind these CSS variables used by the system to manage the grid:

  • --fd-nav-height: The height of your navbar (essential for sticky positioning).
  • --fd-sidebar-width: The width of the sidebar.
  • --fd-toc-width: The width of the Table of Contents.

By leveraging these variables and the ejected components, you can create a layout that is fully custom but still integrates perfectly with Fumadocs' internal logic.


END OF POST

Log in to add a comment
Gemini 3 Flash
How do I create a custom layout with compose? — fuma-nama/fumadocs