Skip to Content

Content

Write pages, organize the sidebar, and add components and images.

Each MDX file in your content collection is a page, and the sidebar lists them for you. Callouts, code block titles, tabs and other syntax work as in Fumadocs Markdown.

Pages

The URL of a page comes from its file path:

FileURL
content/index.mdx/
content/getting-started.mdx/getting-started
content/guide/install.mdx/guide/install

To put pages under a prefix like /docs, see Multiple Collections.

Frontmatter

content/getting-started.mdx
---
title: Getting Started # required, the main heading of the page
description: Install and run the app. # below the title, and in the page metadata
icon: Rocket # a Lucide icon name for the sidebar
full: true # use the full width
---

## Install

Start the body at `##`, since `title` is the main heading.

To resolve icon names, add the Lucide plugin of Fumadocs:

press.config.tsx
import { lucideIconsPlugin } from "fumadocs-core/source/plugins/lucide-icons"; 

export default createPress(content, {
  loaderPlugins: [lucideIconsPlugin()], 
});

To add fields, extend the schema.

Link to another page with its file path. Fumapress resolves it to the URL of that page:

Read the [install guide](./guide/install.mdx).

Last Modified Date

Show the date of the last commit on each page. The sitemap and RSS feed also use this date.

astro.config.mjs
press({
  lastModified: true, 
});

The date comes from git history at build time, so uncommitted pages have no date. A shallow clone, common in CI, gives every page the date of the latest commit.

The sidebar lists your files by name. Add a meta.json next to them to set the order and labels:

{
  "pages": ["index", "---Guides---", "getting-started", "guide"]
}

See Page Slugs and Page Tree for separators, links, root folders and other syntax.

Components

Create the Module

Create a module that default-exports the components your pages can use:

src/components/mdx.tsx
import defaultMdxComponents from "fumadocs-ui/mdx";
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
import type { MDXComponents } from "mdx/types";

export default {
  ...defaultMdxComponents,
  Tab,
  Tabs,
} satisfies MDXComponents;

Register the Module

astro.config.mjs
export default defineConfig({
  integrations: [
    press({ components: "./src/components/mdx.tsx" }), 
  ],
});

Use the Components

content/install.mdx
<Tabs items={["npm", "pnpm"]}>
  <Tab value="npm">Install with npm.</Tab>
  <Tab value="pnpm">Install with pnpm.</Tab>
</Tabs>

Rules for MDX Components

The content of a page is a separate React island, so:

  • Props must be serializable, since components hydrate by name. <Chart data={[1, 2]} /> runs in the browser, and <Chart onClick={() => {}} /> renders on the server only.
  • Providers from your layout aren't available in the content. Add a wrapper to the MDX components instead.
  • Imports are also loaded in the browser, so don't import server-only code.

No cycles

Don't import press.config.tsx or @fumapress/astro/runtime from the module. This creates an import cycle with your content, which slows down the build.

MDX Components per Collection

astro.config.mjs
press({
  components: "./src/components/mdx.tsx",
  collections: {
    docs: { dir: "content/docs" },
    blog: { dir: "content/blog", components: "./src/components/blog-mdx.tsx" }, 
  },
});

Fumapress resolves relative links before your a component receives them, so your component only renders the link.

import type { ComponentProps } from "react";

export function DocsLink(props: ComponentProps<"a">) {
  const external = props.href?.startsWith("http");

  return <a {...props} target={external ? "_blank" : undefined} />;
}

Astro renders your a component separately, like in Astro MDX, without hydration or React context from the page. It can't use state, effects or hooks like usePathname. The same goes for an MDX component inside a link.

Components per Page

List the content renderer yourself, with components as a function of the page. They go over the components of your module:

press.config.tsx
import { contentSourcePlugin } from "@fumapress/astro/plugins/content-source";
import { Mermaid } from "./src/components/mermaid";

createPress(content).plugins(
  contentSourcePlugin({
    components: (page) => (page.path.startsWith("guides/") ? { Mermaid } : {}),
  }),
);

page has the types of your content, and the rules for MDX components apply to these components too. The plugins of the Notion, Sanity and Obsidian sources take components as a function too.

Images and Files

Files in public/ are copied to the build as they are. public/screenshot.png is available at /screenshot.png. For a favicon, reference it from head.

Reference images from Markdown with an absolute path:

![A preview of the editor](/screenshot.png)

On custom pages, use Astro Images.

src/pages/index.astro
---
import { Image } from "astro:assets";
import screenshot from "../assets/screenshot.png";
---

<Image src={screenshot} alt="A preview of the editor" />

Last updated on

On this page