Skip to Content

Content

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

Each Markdown or MDX file under content/ 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 Sources.

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.

press.config.tsx
const docs = defineDocs({
  dir: "content",
  docs: {
    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

vite.config.ts
export default defineConfig({
  plugins: [
    press({ components: "./src/components/mdx.tsx" }), 
    fumadocsMdx(),
    tailwindcss(),
  ],
});

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>

You can also import a component directly in an MDX file.

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} />;
}

To use hooks like usePathname from fumadocs-core/framework, add "use client" to the component.

Components per Page

List the MDX renderer yourself, with components as a function of the page. It replaces the renderer that press() builds from your module:

press.config.tsx
import mdxPlugin from "fumadocs-mdx/press";
import components from "./src/components/mdx";
import { Mermaid } from "./src/components/mermaid";

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

page has the types of your content. 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)

In components, import Image:

src/pages/index.tsx
import { Image } from "fumapress/image";

<Image src="/screenshot.png" width={1200} height={630} alt="A preview of the editor" />;

Besides the usual <img> props, Image takes:

Prop

Type

Image Optimization

Markdown images and Image are optimized when your platform supports image optimization. Without site.baseUrl, the self-hosted provider leaves images as they are.

PlatformProviderNeeds
VercelVercel Image Optimizationnothing
CloudflareImage Transformationstransformations enabled on your zone
Node.jsSelf-hosted, with Sharpsharp, a server and site.baseUrl

Remote Images

Local paths are always allowed. For remote images, pass image: false to recommendedPreset() and add the provider with its options:

press.config.tsx
import { createPress, recommendedPreset } from "fumapress";
import { vercelImage } from "fumapress/plugins/image/vercel";

export const app = createPress(content).plugins(vercelImage({ domains: ["github.com"] }));

export default app.config({ preset: recommendedPreset({ image: false }) });

Disabling Optimization

On a static host with sharp installed, turn off image optimization, since the self-hosted provider needs a server:

press.config.tsx
import { createPress, recommendedPreset } from "fumapress";

export default createPress(content).config({
  preset: recommendedPreset({ image: false }),
});

For another optimizer, see Replacing a Default Plugin.

Last updated on

On this page