Skip to Content

Configuration

Every option of your site, from press.config.tsx to the Vite plugin.

press.config.tsx at the project root configures your site. Only createPress() is required.

press.config.tsx
import { createPress } from "fumapress";
import { linkValidationPlugin } from "fumapress/plugins/link-validation";

export const app = createPress(content, { i18n }).plugins(linkValidationPlugin());

export default app.config({
  site: { name: "Acme" },
  head: [{ tag: "link", attrs: { rel: "icon", href: "/favicon.ico" } }],
  ui: { layoutProps, renderPage },
});

Content and Plugins

createPress() takes your content sources and these options:

.plugins() adds plugins. Call it once.

To change the default plugins, set preset in .config().

Site

Name

site.name shows in the navbar and page metadata.

press.config.tsx
createPress(content).config({
  site: {
    name: "Acme Docs",
  },
});

URL

Set the URL of your deployed site, with its base path if there is one. Without it, canonical links, the sitemap, RSS and Open Graph images fall back to relative URLs.

press.config.tsx
createPress(content).config({
  site: {
    baseUrl: "https://docs.example.com",
  },
});

Repository

site.git adds a repository link to the navbar and a link to the source file of each page.

press.config.tsx
createPress(content).config({
  site: {
    git: {
      user: "acme",
      repo: "docs",
      branch: "main",
    },
  },
});

GitHub is the default. For GitLab or Bitbucket, set provider. For a self-hosted instance, also set url.

Source file links need the git root. Set rootDir when the build runs without a .git directory.

All Site Options

Prop

Type

Head Tags

head adds tags to <head> on every page, like analytics or fonts.

press.config.tsx
createPress(content).config({
  head: [{ tag: "meta", attrs: { name: "theme-color", content: "#000000" } }],
});

attrs take HTML attribute names, like crossorigin and http-equiv, not the React props.

These tags have a key: title, a meta tag by its name, property or http-equiv, the canonical link, and an alternate link by its hreflang. Your tags replace plugin tags with the same key. Other tags are kept, except exact duplicates.

Tags per Page

Use a function. When page is undefined, the tags you return apply to the whole site:

press.config.tsx
createPress(content).config({
  head({ page }) {
    if (!page)
      return [{ tag: "link", attrs: { rel: "icon", href: this.withBase("/favicon.ico") } }];
    if (page.path.startsWith("blog/"))
      return [{ tag: "meta", attrs: { property: "og:type", content: "article" } }];
  },
});

this.withBase() adds the base path for a site with a base path.

UI

These options change the look of your site. Most of them are under ui in .config().

The navbar shows site.name, linked to your home page. ui.layoutProps adds links and changes the title for every layout:

press.config.tsx
createPress(content).config({
  ui: {
    layoutProps: {
      nav: { title: "Acme" },
      links: [
        { url: "/blog", text: "Blog", active: "nested-url" },
        { url: "https://github.com/acme", text: "GitHub", external: true },
      ],
    },
  },
});

See Layout Links and Navbar for what they accept. ui.layoutProps also takes the props that every docs layout shares, like sidebar and tabs.

Links from plugins come after yours. For other options, your value takes priority.

nav.title can be any React element:

press.config.tsx
import { Image } from "fumapress/image";

createPress(content).config({
  ui: {
    layoutProps: {
      nav: {
        title: (
          <>
            <Image src="/logo.png" width={64} height={64} className="size-8" alt="" />
            Acme
          </>
        ),
      },
    },
  },
});

With internationalization, use a function. this.localizePath() adds the language prefix.

press.config.tsx
createPress(content, { i18n }).config({
  ui: {
    layoutProps({ lang }) {
      return {
        links: [
          {
            url: this.localizePath(lang, "/blog"),
            text: lang === "zh" ? "部落格" : "Blog",
          },
        ],
      };
    },
  },
});

To keep layoutProps in its own file, type it with UIOptions:

src/ui/layout-props.tsx
import type { UIOptions } from "fumapress";
import type { app } from "../../press.config";

export const layoutProps: UIOptions<typeof app>["layoutProps"] = function ({ lang }) {
  return { links: [{ url: this.localizePath(lang, "/blog"), text: "Blog" }] };
};

Colors

Swap the theme import in src/app.css. See Fumadocs UI Themes for the presets.

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";

Dark Mode

Start in dark mode with ui.providerProps:

press.config.tsx
createPress(content).config({
  ui: {
    providerProps: {
      theme: { defaultTheme: "dark" },
    },
  },
});

Fonts

Load the font in head, then set it in src/app.css:

createPress(content).config({
  head: [
    { tag: "link", attrs: { rel: "preconnect", href: "https://fonts.googleapis.com" } },
    {
      tag: "link",
      attrs: { rel: "preconnect", href: "https://fonts.gstatic.com", crossorigin: "" },
    },
    {
      tag: "link",
      attrs: {
        rel: "stylesheet",
        href: "https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap",
      },
    },
  ],
});

Root Document

ui.renderRoot replaces the <html> document, for example to add a class to <html> or <body>:

press.config.tsx
createPress(content).config({
  ui: {
    renderRoot({ lang, head, children }) {
      return (
        <html lang={lang ?? "en"} className="dark" suppressHydrationWarning>
          <head>{head}</head>
          <body className="antialiased">{children}</body>
        </html>
      );
    },
  },
});

Render head in <head>. It has the charset, the viewport and the styles of your site.

Other UI Options

Build and Dev Server

press.config.tsx doesn't configure the build or the dev server. Use these instead.

Vite Plugin

press() takes these options:

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

const components = "./src/components/mdx.tsx";

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

Prop

Type

CLI

Run the site through fumapress instead of vite.

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

dev listens on port 3000, and start uses the first free port from 8080. To choose, set PORT and HOST or pass a flag:

npm run dev -- --port 4000 --host 0.0.0.0

Environment Variables

The CLI loads .env, .env.local and mode variants like .env.production. Variables already set in the environment take priority.

  • Server code and press.config.tsx: any variable, through process.env.
  • Client code: only variables prefixed with VITE_, through import.meta.env.

Files that only the server can read, like key files, go in the private directory. Change it with the privateDir option of press().

Server Entry

Most sites don't need one. Create it to pass options to the Waku adapter or to define routes in code.

Install Waku

Install the version that Fumapress uses:

npm i waku@1.0.0-rc.4

Create the Entry

src/waku.server.tsx
import _adapter from "waku/adapters/default";
import press from "../press.config";
import { createRouter } from "fumapress/router";
import { fsRouterFn } from "fumapress/router/fs";

const router = await createRouter(press);
import.meta.hot?.dispose(router.dispose);

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

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

export default adapter(pages, { middlewareFns });

Config-Based Routing

To use config-based routing instead of src/pages, pass a callback to createPages(). Don't create a root element, since Fumapress already creates one.

src/waku.server.tsx
const pages = router.createPages(fsRouterFn(modules));
const pages = router.createPages(({ createPage }) => {
  createPage({
    render: "static",
    path: "/hello-world",
    component() {
      return <main>Hello World</main>;
    },
  });
});

Types

Export the result of .plugins() as app, and pass typeof app to the type helpers from fumapress:

src/ui/layout.tsx
import type { RenderPageProps } from "fumapress";
import type { app } from "../../press.config";

export function renderPage(props: RenderPageProps<typeof app>) {
  // ...
}
HelperFor
RenderPagePropsprops of ui.renderPage, narrowed to a source or plugin page
UIOptionsan option of ui, like UIOptions<typeof app>["layoutProps"]
PressContextthis in callbacks, the result of getPressContext()
SharedLayoutPropsthe props of every docs layout
ProviderPropsthe root provider props

Custom pages take WakuRouteProps as props, and their getConfig() returns WakuRouteConfig.

Last updated on

On this page