Migrate to v1

Breaking changes from the v1 redesign.

Overview

v1 reshapes the config API. Layouts move onto the config object, content is required, and plugin types are renamed.

Install the beta:

npm i fumapress@beta

.layouts() still works as a compat layer, but prefer the new options below.

Content

loader is removed. Pass sources via content:

// before
defineConfig({ loader: docs.toFumadocsSource() });

// after
defineConfig({ content: docs.toFumadocsSource() });

Layouts

Move layout options onto the config object:

BeforeAfter
.layouts({ root })renderRoot
.layouts({ page })renderPage
.layouts({ notFound })renderNotFound
.layouts({ defaultProps })defaultLayoutProps
press.config.tsx
import { defineConfig } from "fumapress";
import { createDocsLayoutPage } from "fumapress/layouts/docs";

// before
defineConfig({ content }).layouts({
  defaultProps() {
    return { links: [...] };
  },
  page: createDocsLayoutPage(),
});

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

const config = defineConfig({
  content,
  defaultLayoutProps: {
    links: [...],
  },
  renderPage: (props) => <DocsLayout {...props} />,
});

export default config;

Prefer wrapping the layout in renderPage instead of passing create*LayoutPage() directly, this avoids TypeScript inference issues.

defaultLayoutProps can be a plain object, or a function when you need lang / app context.

See Layouts for switching layouts and shared props.

Plugins & Adapters

.usePlugins() / .useAdapters() are removed. Use .plugins() / .adapters():

defineConfig({ content }).plugins(flexsearchPlugin()).adapters(fumadocsMdx());

When you need getPressContext, export it from config.utils() so it is typed to your app:

const config = defineConfig({ content });

export const { getPressContext } = config.utils();
export default config;

Types

BeforeAfter
ConfigContextAppShape
ServerPlugin / ServerPluginOptionPressPlugin / PressPluginOption
ConfigBuilderConfigUtils
Layoutsrender* / defaultLayoutProps
i18n on context shapelang on AppShape

$context is an AppShape (page / meta / lang / source).

// before
import type { ConfigContext, ServerPlugin } from "fumapress";

export function myPlugin<C extends ConfigContext>(): ServerPlugin<C> {
  // ...
}

// after
import type { AppShape, PressPlugin } from "fumapress";

export function myPlugin<C extends AppShape>(): PressPlugin<C> {
  // ...
}

App Context

Helpers that used to take ctx as an argument are methods on AppContext:

BeforeAfter
ctx.layoutsctx.renderRoot / ctx.renderPage / ...
renderPageMeta(page, ctx)ctx.renderPageMeta(page)
renderRootMeta(ctx)ctx.renderRootMeta()
getCreationDate(ctx, page)ctx.getPageCreatedAt(page)
getLastModifiedDate(ctx, page)ctx.getPageLastModified(page)
getGitHubFileUrl(ctx, path)ctx.getFileUrl(path)
renderToc(ctx, page)ctx.getPageToc(page)
renderBody / core:render-bodyctx.getPageBody(page){ node }
data["core:page-meta"]ctx.interceptPageMeta(...)

Standalone exports like baseLayoutProps, getCreationDate, renderBody, and TransformChildren are no longer public.

Layout transformers are renamed from renderers to transformers:

// before
data["core:docs-layout"].renderers;

// after
data["core:docs-layout"].transformers;

Adapters

core:render-body is renamed to core:get-body, and returns { node } instead of a bare React node:

// before
async "core:render-body"(page) {
  return <MyPage />;
}

// after
async "core:get-body"(page) {
  return { node: <MyPage /> };
}

Plugins

Plugins that appended OG tags (or other meta) via data["core:page-meta"] should use interceptPageMeta:

this.interceptPageMeta(({ page, next }) => (
  <>
    {next()}
    <meta property="og:image" content={...} />
  </>
));

Last updated on

On this page