Configurations

Available options.

Main Config

Primary options for your app.

press.config.tsx
import { defineConfig } from "fumapress";

export default defineConfig({
  // ...
});

Site

The site information like name and linked git repository:

press.config.tsx
import { defineConfig } from "fumapress";

export default defineConfig({
  site: {
    name: "Fumapress",
    baseUrl: import.meta.env.DEV ? "http://localhost:3000" : "https://press.fumadocs.dev",
    git: {
      user: "fuma-nama",
      branch: "dev",
      repo: "fumapress",
    },
  },
});

The repository is used for the icon link in navbar, and the link to source file of pages.

Source file links are resolved against the detected git root, set git.rootDir to override it, such as when building in an environment without the .git directory.

URLs

baseUrl is required for some features to know the absolute URL of a page.

press.config.tsx
import { defineConfig } from "fumapress";

export default defineConfig({
  site: {
    baseUrl: "https://press.fumadocs.dev",
  },
});

Page URLs have no trailing slash, the deployment adapters configure hosts to serve them that way.

Locales

With i18n, every page links its translations with hreflang. Locale codes are used as is, map the ones that are not BCP 47 language tags:

press.config.tsx
import { defineConfig } from "fumapress";

export default defineConfig({
  site: {
    hreflang: {
      cn: "zh-Hans",
      tw: "zh-Hant",
    },
  },
});

Git Providers

git.provider supports github (default), gitlab, and bitbucket, the navbar icon link and source file URLs follow the given provider. For self-hosted instances, pass the instance URL with git.url:

press.config.tsx
import { defineConfig } from "fumapress";

export default defineConfig({
  site: {
    git: {
      provider: "gitlab",
      url: "https://gitlab.example.com",
      user: "fuma-nama",
      branch: "dev",
      repo: "fumapress",
    },
  },
});

Layouts

Specify or customize page UI with renderPage, renderRoot, and renderNotFound.

press.config.tsx
import { createDocsLayoutPage } from "fumapress/layouts/docs";
import { defineConfig } from "fumapress";

const DocsLayout = createDocsLayoutPage<typeof config.$context>();

const config = defineConfig({
  renderPage: (props) => <DocsLayout {...props} />,
});

export default config;

renderNotFound customizes the not found screen, shown for unknown URLs and notFound() calls:

press.config.tsx
import { defineConfig } from "fumapress";
import { Link } from "fumapress/client";

export default defineConfig({
  renderNotFound: () => (
    <main>
      <h1>Page not found</h1>
      <Link href="/">Back to home</Link>
    </main>
  ),
});

See Layouts for details.

Render Mode

mode controls how routes are rendered:

  • default: when your content source will not change after production build. It still emits API routes, such as the search API endpoint, which require a running server to host.
  • static: same as default, but will not emit API routes. Production builds emit only static files, which can be served via CDNs.
  • dynamic: when your content source will change after production build. Requires a running server to host.
press.config.tsx
import { defineConfig } from "fumapress";

export default defineConfig({
  // ...
  mode: "static",
});

See Deployment for hosting each mode.

Preset

By default, Fumapress adds recommended plugins automatically:

Plugins you add yourself take priority, e.g. adding oramaSearchPlugin() replaces the default search.

To start from an empty plugin list instead:

press.config.tsx
import { defineConfig } from "fumapress";

export default defineConfig({
  // ...
  preset: false,
});

Loader Options

loaderOptions configures the Loader API that Fumapress creates from your content sources. A common use is loader plugins, such as resolving the icon frontmatter field to Lucide icons:

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

export default defineConfig({
  // ...
  loaderOptions: {
    plugins: [lucideIconsPlugin()],
  },
});

For dynamic content sources (e.g. a CMS), alwaysRevalidate: true refetches them on every request:

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

export default defineConfig({
  // ...
  loaderOptions: {
    alwaysRevalidate: true,
  },
});

Leave it disabled to control this yourself with getPressContext().revalidateLoader().

Meta

To add meta tags, you can leverage the meta config.

press.config.tsx
import { defineConfig } from "fumapress";

export default defineConfig({
  meta: {
    root() {
      return (
        <>
          <meta property="og:type" content="website" />
          {/* you can use it for link tags as well */}
          <link rel="preconnect" href="https://fonts.googleapis.com" />
          <link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="" />
          <link
            href="https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap"
            rel="stylesheet"
          />
        </>
      );
    },
    page(page) {
      return <>{/* page-level meta tags */}</>;
    },
  },
});

UI

Tailwind CSS

Create a src/app.css file:

src/app.css
@import "tailwindcss";
@import "fumadocs-ui/css/neutral.css";
@import "fumadocs-ui/css/preset.css";
@import "fumapress/css/preset.css";

It will be automatically loaded, restart the dev server if there's one running.

You can change the color theme or add custom styles, like:

src/app.css
@import "tailwindcss";
@import "fumadocs-ui/css/neutral.css";
@import "fumadocs-ui/css/black.css";
@import "fumadocs-ui/css/preset.css";
@import "fumapress/css/preset.css";

@theme {
  --default-font-family: "Geist", sans-serif;
}

Using Radix UI

Fumapress uses the Base UI headless component library by default, same as Fumadocs. If you are using Radix UI, Fumapress also supports the Radix UI build of Fumadocs UI.

You can enable it by installing fumadocs-ui without the @fumadocs/base-ui alias:

npm i fumadocs-ui
npm uninstall @base-ui/react

Vite Plugin

The press() plugin connects Fumapress to Vite:

vite.config.ts
import { defineConfig } from "vite";
import press from "fumapress/vite";
import { fumadocsMdx } from "fumadocs-mdx/vite";
import tailwindcss from "@tailwindcss/vite";

export default defineConfig({
  plugins: [press(), fumadocsMdx(), tailwindcss()],
});

It takes the options of the app itself, like the directory layout and deployment target:

Prop

Type

Deployment Adapter

Vercel, Netlify, and Cloudflare are detected from the environment, other targets fall back to waku/adapters/node. Set adapter when the detected one isn't what you deploy to, see Deployment:

vite.config.ts
export default defineConfig({
  plugins: [
    press({
      adapter: "waku/adapters/vercel",
    }),
  ],
});

CLI

The fumapress CLI runs Vite with the environments your app needs, use it instead of calling vite directly.

package.json
{
  "scripts": {
    "dev": "fumapress dev",
    "build": "fumapress build",
    "start": "fumapress start"
  }
}

dev and start accept --host (-h) and --port (-p), the port can also be set with the PORT environment variable. dev listens on port 3000, start picks a free port from 8080.

npm run dev -- --port 4000

Server Entry

The server entry file is optional, you can create one if needed. Useful for implementing custom router.

Custom server entries use Waku.js APIs directly. Since Fumapress manages Waku as its own dependency, install it explicitly for direct API access, matching the version pinned by Fumapress:

npm i waku@1.0.0-rc.0
src/waku.server.tsx
import _adapter from "waku/adapters/default";
// your main config file
import pressConfig from "../press.config";
import { createRouter } from "fumapress/router";
import { fsRouterFn } from "fumapress/router/fs";

const router = await createRouter(pressConfig);

const modules = import.meta.glob("./pages/**/*.{ts,tsx,js,jsx}", {
  base: "/src",
});

// file-system router
const pages = router.createPages(fsRouterFn(modules));
const middlewareFns = router.createMiddlewares();
const adapter = router.patchAdapter(_adapter);

export default adapter(pages, { middlewareFns });

You can also use Waku Config-based Routing instead of the file-system router, see the linked docs for usage.

src/waku.server.tsx
import _adapter from "waku/adapters/default";
// your main config file
import pressConfig from "../press.config";
import { createRouter } from "fumapress/router";

const router = await createRouter(pressConfig);

const pages = router.createPages(async ({ createPage }) => {
  // note: root element is already created by Fumapress
  createPage({
    render: "static",
    path: "/hello-world",
    component() {
      // ...
    },
  });
});

const middlewareFns = router.createMiddlewares();
const adapter = router.patchAdapter(_adapter);

export default adapter(pages, { middlewareFns });

Last updated on

On this page