Themes
Themes control layout (HTML template), default MDX components, and optional theme pages/assets. The default theme in the Methanol repo is a good reference.
Theme Implementation Guide
- Theme guide
- Render navigation
- Render a ToC
- Prev/next links
- Language switch
- Special-purpose pages
- Blog theme
Theme Shape
export default () => ({
theme: {
root: './theme',
componentsDir: './components',
pagesDir: './pages',
publicDir: './public',
sources: {
'/theme': './sources'
},
template: ({ PageContent, ExtraHead, ctx, page, withBase, HTMLRenderer, components }) => (
<>
{HTMLRenderer.rawHTML`<!doctype html>`}
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<ExtraHead />
<title>{page.title || ctx.site.name}</title>
<link rel="icon" href="/favicon.png" />
</head>
<body>
{components.Callout ? <components.Callout>Hi</components.Callout> : null}
<PageContent />
</body>
</html>
</>
),
components: {
Callout: ({ children }) => <div class="callout">{children}</div>
}
}
})
Required
theme.rootis required and defines the base directory for theme paths.
Defaults and Validation
componentsDir,pagesDir,publicDircan be:- omitted (defaults to
./components,./pages,./publicunder the theme root; missing dirs are skipped) - set to
false(disables that feature for the theme) - set explicitly (must exist or Methanol throws)
- omitted (defaults to
Theme Layout Files
Suggested structure:
theme/
index.js # exports the theme object
page.jsx # template component
components/ # default MDX components
pages/ # optional theme pages
public/ # optional public assets
sources/ # assets mapped by theme.sources
Theme public/ is merged into the project publicDir during dev/build. If a path exists in both, the project asset wins.
Template Contract
theme.template receives:
PageContent: JSX component for the current page contentExtraHead: extra<head>content (page assets + Methanol runtime + per-page<Head>/headoutput)ctx: page/site context (ctx.page,ctx.pages,ctx.pagesTree,ctx.pagesByRoute,ctx.languages,ctx.language,ctx.site,ctx.withBase,ctx.routePath,ctx.routeHref,ctx.path,ctx.getSiblings)page: alias forctx.page, providing page infowithBase: helper for base-aware URLs (same function asctx.withBase)HTMLRenderer: refui HTML renderer (forrawHTML/serialize, or for manually rendering JSX components)components: merged components object (theme components + user components)
Return a JSX tree.
Important: put <ExtraHead /> inside <head>. Without it, per-page <Head>/head and automatic page assets won’t be injected.
When your site is deployed under a subpath (site.base !== '/'):
- Use
withBase('/...')(orctx.withBase('/...')) for URLs that must follow the configured site/Rsbuild base, such as runtime assets. - Don’t apply
withBase()to theme source URLs (theme.sources) or plain static files underpublic/; Methanol resolves those paths directly.
Theme Components
theme.components provides default MDX components. User components in the project components/ override theme components by name.
The template also receives components, which is the merged result (useful for layout-level components like nav/search).
Theme Pages
Theme pages follow the same routing rules as user pages. User routes always override theme routes.
A theme-provided /404 is used only when the user project does not define its own 404 page.
Theme Public Assets
If the theme provides a publicDir, Methanol copies its files into the user public directory at dev/build start. When the user sets publicDir, theme files are still copied into it; files with the same name are skipped. If the user does not have a public directory, Methanol falls back to the theme public directory.
Theme Sources
theme.sources maps virtual URLs to files. This is handled by Methanol’s resolver rather than Rsbuild aliases.
export default () => ({
theme: {
root: './theme',
sources: {
'/theme': './sources'
}
}
})
Theme Hooks
A theme can also provide build hooks (same shape as user config hooks):
preBuild(dev startup + before build)preBundle(after pages are rendered, before Rsbuild bundles)postBundle(after Rsbuild bundles, before HTML asset rewriting)postBuild(after build)