Configuration
|
This content was generated with the assistance of AI and should be verified against the official documentation of each tool before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
This page was written against Docusaurus 3.10.2 (the last release of the 3.x line) and checked on 2026-10-26. Every
site has one configuration file, docusaurus.config.ts, that is read in Node.js when you run docusaurus start,
build or deploy. This page walks through each part of that file using the Pocket Units example site (a tiny
units-conversion library with C# and Python implementations), shows the validation errors you get when a value is
wrong, and explains the fields that are deprecated or about to change in v4.
What the configuration file is
The file lives at the site root. Docusaurus looks for docusaurus.config with one of these extensions, in this
order: .ts, .mts, .cts, .js, .mjs, .cjs. A different file can be selected with the --config option of the
CLI commands. The file runs only in Node.js, so it must not use browser APIs or JSX.
The default export can be a config object, a promise of one, or a function (sync or async) that returns the object. The async form is useful when you must read a file or call an API before the site is built.
The loaded object goes through a schema validation (Joi). It is then normalised and some defaults are resolved from
the future flags. Unknown top-level fields are rejected; custom data goes in customFields.
(object, promise or function)"] B --> C["Validate with schema
unknown fields rejected"] C --> D["Normalise
url, baseUrl, flag defaults"] D --> E["Presets expand into
plugins and themes"] E --> F["start / build / deploy"]
Figure: the path of the configuration file from disk to the commands that use it.
The required fields and site metadata
Three fields are required: title, url and baseUrl. The rest have defaults.
| Field | Meaning |
|---|---|
|
Site title, used in the browser tab title and metadata. |
|
Short description of the site. Default is an empty string. |
|
Path of the favicon, relative to |
|
Separator in the generated |
|
When |
|
Directories copied as-is into the build output. Default is |
url and baseUrl
url is the top-level origin of the site and baseUrl is the path under which it is served. For the page
https://example-org.github.io/pocket-units/docs/intro, url is https://example-org.github.io and baseUrl is
/pocket-units/.
The validation is strict, and I ran each of the following in a scratch Docusaurus 3.10.2 site:
-
urlwithout a protocol fails with"example-org.github.io" does not look like a valid URL. Make sure it has a protocol; for example, "https://example.com". -
urlwith a path fails withThe url is not supposed to contain a sub-path like "/pocket-units". Please use the baseUrl field for sub-paths. -
A trailing slash on
urlis accepted and removed. AbaseUrlofpocket-unitsis normalised to/pocket-units/(a leading and a trailing slash are added). Loading the configuration{url: 'https://example-org.github.io/', baseUrl: 'pocket-units'}producedurlequal tohttps://example-org.github.ioandbaseUrlequal to/pocket-units/.
A wrong baseUrl is the most common reason for a deployed site that loads without CSS or JavaScript. The option
baseUrlIssueBanner (default true) shows a banner on the page when the site cannot load its own assets, to make
the cause easier to find.
trailingSlash
trailingSlash controls whether links end with a slash and how HTML files are written:
| Value | Effect |
|---|---|
not set (default) |
Links are left as written. The page |
|
Trailing slashes are added to links. Files are written as |
|
Trailing slashes are removed. Files are written as |
With trailingSlash: false in the example site, the build produced build/docs/intro.html. The value matters for the
host: GitHub Pages adds trailing slashes itself, so the deployment guide recommends setting it explicitly to true or
false, not leaving it unset. A mismatch between the setting and the host can produce redirects or 404 pages on some
hosts; the official docs link a third-party trailing-slash guide for the details.
Deployment fields
organizationName and projectName are the GitHub user or organisation and the repository name. deploymentBranch
is the branch that docusaurus deploy pushes to, and githubHost and githubPort support GitHub Enterprise. They
are only used by the deploy command, not by the build. See
Deployment for the full workflow.
Broken link and duplicate route reporting
Four fields choose what happens when Docusaurus finds a problem. Each accepts ignore, log, warn or throw.
| Field | Default | What it checks |
|---|---|---|
|
|
Links between pages found while building. Checked in production builds only. |
|
|
Links to a heading anchor that does not exist. |
|
|
Two pages that resolve to the same route. |
|
|
Markdown file links ( |
|
|
Markdown images whose file cannot be found. |
An invalid value is reported by the schema: onBrokenLinks: 'error' fails with
"onBrokenLinks" must be one of [ignore, log, warn, throw].
The deprecated top-level onBrokenMarkdownLinks
The top-level onBrokenMarkdownLinks field is deprecated since 3.9 and will be removed in v4. Its replacement is
markdown.hooks.onBrokenMarkdownLinks, next to the new markdown.hooks.onBrokenMarkdownImages. For 3.x
compatibility, if the old field is set it wins over the new one. Running a build with onBrokenMarkdownLinks: 'throw'
printed this warning (shown without its log level prefix):
The `siteConfig.onBrokenMarkdownLinks` config option is deprecated and will be removed in Docusaurus v4. Please migrate and move this option to `siteConfig.markdown.hooks.onBrokenMarkdownLinks` instead.
With the hook set to throw and a document that contains [missing](./nope.md), the build failed with the message
that the Markdown link with URL ./nope.md could not be resolved and must reference a local Markdown file that exists
within the current plugin. The message suggests two ways out: change the hook, or use the pathname:// protocol on
links that are not Markdown files. A hook can also be a function that receives sourceFilePath, url and node and
may return a replacement URL.
Presets, plugins and themes
presets, plugins and themes are arrays. A preset is a bundle of plugins and themes with shared options; the
classic preset contains the docs, blog and pages plugins, the sitemap and other plugins, and the classic theme.
Each entry has one of these forms:
-
a string:
'classic' -
a pair of name and options:
['classic', { ... }] -
for plugins and themes, a function, or a pair of function and options
-
falseornullto disable an entry (presets, plugins and themes)
A malformed entry fails with Bad Docusaurus plugin value plugins[0] followed by examples of the valid forms. Plugin
and theme names are resolved as npm packages or local paths. See
Plugins and presets for how to write one.
In a TypeScript config, satisfies Preset.Options on the options object gives type checking and completion for the
preset without widening the type.
themeConfig
themeConfig is an object with options for the theme: color mode, navbar, footer, Prism code highlighting, the
announcement bar, the search and so on. The base type is an open object ([key: string]: unknown), and each theme
documents its own keys. With the classic theme, satisfies Preset.ThemeConfig provides the types. See
Styling, theming and swizzling for the visual
options.
customFields
Unknown top-level fields make the build fail:
[ERROR] Error: These field(s) ("tagLine",) are not recognized in cfgtests/unknown.mjs.
If you still want these fields to be in your configuration, put them in the "customFields" field.
See https://docusaurus.io/docs/api/docusaurus-config/#customfields
customFields is the place for your own data. It is available to React components through the site config:
import type {ReactNode} from 'react';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
export default function Packages(): ReactNode {
const {siteConfig} = useDocusaurusContext();
const {nugetPackage, pypiPackage} = siteConfig.customFields as {
nugetPackage: string;
pypiPackage: string;
};
return (
<ul>
<li>NuGet: <code>{nugetPackage}</code></li>
<li>PyPI: <code>{pypiPackage}</code></li>
</ul>
);
}
Used from an MDX page with import Packages from '@site/src/components/Packages'; and <Packages />, the build
rendered NuGet: PocketUnits and PyPI: pocket-units. Because customFields is typed as an open object, the cast is
needed. The object is serialised into the client bundle, so never put secrets in it. Environment variables read with
process.env work only in the configuration file itself (Node.js), and customFields is the way to pass them to the
browser.
Head and asset fields
| Field | Meaning |
|---|---|
|
Array of objects with |
|
Scripts to load, as a string (the |
|
Stylesheets to load, as a string (the |
|
Modules executed on the client at startup, for global code or CSS. |
|
An Eta HTML template used to render the application shell, for custom body attributes or extra meta tags. The template must keep the structure Docusaurus expects. |
|
Selects the JavaScript loader, |
i18n
i18n declares defaultLocale, locales, an optional path (default i18n) and localeConfigs with per-locale
label, htmlLang, direction, calendar, path, translate, and, since 3.9, url and baseUrl. A site with one
locale still needs defaultLocale: 'en' and locales: ['en'], as the generated config shows. The details are in
Internationalization.
markdown
The markdown object configures how Markdown and MDX are compiled.
| Key | Default | Meaning |
|---|---|---|
|
|
|
|
|
Renders |
|
|
Converts emoji shortcodes to Unicode (added in 3.9). |
|
|
Keeps the case of the heading text when generating anchor ids. |
|
|
Severity or function for unresolved Markdown links. |
|
|
Severity or function for unresolved Markdown images. |
|
|
Accept HTML comments in MDX. |
|
|
Accept the |
|
|
Accept |
|
none |
A function of one argument (file path and content) that returns the content to compile. |
|
built in |
A function to read front matter from another source or to post-process it. |
|
none |
Options passed to remark-rehype. |
The three mdx1Compat defaults are true in v3 and become false when future.v4.mdx1CompatDisabledByDefault is on;
explicit values always win. See MDX and React
components for the consequences.
storage
storage chooses where the site keeps small browser state such as the theme and the selected tab:
-
type:localStorage(default) orsessionStorage; any other value fails with"storage.type" must be one of [localStorage, sessionStorage]. -
namespace:falseby default, a string to choose the prefix, ortrue. The default becomestruewhenfuture.v4.siteStorageNamespacingis on.
Namespacing matters when several Docusaurus sites share an origin (for example, sites under different base URLs of the same GitHub Pages host), because they would otherwise overwrite each other’s keys. Turning it on for an existing site resets the stored choices of current visitors once.
future
future holds opt-in flags for features that will become defaults. In 3.10.2 the object has these members:
-
v4:trueenables all v4 flags; or an object withremoveLegacyPostBuildHeadAttribute,useCssCascadeLayers,siteStorageNamespacing,fasterByDefaultandmdx1CompatDisabledByDefault. -
faster:trueenables all Docusaurus Faster flags; or an object withswcJsLoader,swcJsMinimizer,swcHtmlMinimizer,lightningCssMinimizer,mdxCrossCompilerCache,rspackBundler,rspackPersistentCache,ssgWorkerThreadsandgitEagerVcs. It requires the@docusaurus/fasterpackage. -
experimental_vcsandexperimental_router(browserorhash), which keep theexperimental_prefix.
The key future.faster replaced future.experimental_faster, and storage was promoted from
future.experimental_storage. The old names now fail at startup with a message saying that
future.experimental_faster has been renamed to future.faster, and that future.experimental_storage has been
promoted to the top-level storage attribute.
Two dependencies between flags are enforced when the configuration loads:
-
future.faster.ssgWorkerThreadsrequiresfuture.v4.removeLegacyPostBuildHeadAttribute. -
future.faster.rspackPersistentCacherequiresfuture.faster.rspackBundler.
Setting future: {v4: true}, as the generated site does, has a larger effect than it looks, because
fasterByDefault turns every Faster flag on. I loaded the Pocket Units configuration below and printed the
normalised result: all nine faster flags were true, the three mdx1Compat flags were false, and storage.namespace
was the string set in the file. The same file with future removed gave all faster flags false, the mdx1Compat
flags true and storage.namespace false. See
Docusaurus Faster and v4 readiness for what
each flag does.
TypeScript types
The Config type from @docusaurus/types describes the object as you write it: a deep partial of the validated
configuration, where title, url and baseUrl stay required. future.v4 and future.faster also accept a boolean.
The preset package exports Preset.Options and Preset.ThemeConfig. A new site from the TypeScript template ships a
tsconfig.json that extends @docusaurus/tsconfig and sets strict and ignoreDeprecations: "6.0" (needed with
TypeScript 6 for now), and a typecheck script that runs tsc. The tsconfig.json is not used by start or
build; the config file is compiled by Docusaurus itself.
Complete example: Pocket Units
This is the full configuration used for the checks above. With npx tsc it type-checked without errors, and
npx docusaurus build completed with Generated static files in "build".
import {themes as prismThemes} from 'prism-react-renderer';
import type {Config} from '@docusaurus/types';
import type * as Preset from '@docusaurus/preset-classic';
// This file runs in Node.js: no browser APIs and no JSX here.
const config: Config = {
title: 'Pocket Units',
tagline: 'Convert lengths in C# and Python',
favicon: 'img/favicon.ico',
url: 'https://example-org.github.io',
baseUrl: '/pocket-units/',
trailingSlash: false,
organizationName: 'example-org',
projectName: 'pocket-units',
deploymentBranch: 'gh-pages',
onBrokenLinks: 'throw',
onBrokenAnchors: 'warn',
i18n: {
defaultLocale: 'en',
locales: ['en'],
},
storage: {
type: 'localStorage',
namespace: 'pocket-units',
},
future: {
v4: true,
},
markdown: {
format: 'mdx',
emoji: false,
anchors: {maintainCase: false},
hooks: {
onBrokenMarkdownLinks: 'throw',
onBrokenMarkdownImages: 'throw',
},
},
customFields: {
nugetPackage: 'PocketUnits',
pypiPackage: 'pocket-units',
},
headTags: [
{
tagName: 'meta',
attributes: {name: 'author', content: 'Pocket Units contributors'},
},
],
presets: [
[
'classic',
{
docs: {
sidebarPath: './sidebars.ts',
editUrl: 'https://github.com/example-org/pocket-units/tree/main/',
},
blog: false,
theme: {
customCss: './src/css/custom.css',
},
} satisfies Preset.Options,
],
],
themeConfig: {
colorMode: {respectPrefersColorScheme: true},
navbar: {
title: 'Pocket Units',
items: [
{
type: 'docSidebar',
sidebarId: 'tutorialSidebar',
position: 'left',
label: 'Docs',
},
{
href: 'https://github.com/example-org/pocket-units',
label: 'GitHub',
position: 'right',
},
],
},
footer: {
style: 'dark',
copyright: `Copyright ${new Date().getFullYear()} Pocket Units contributors.`,
},
prism: {
theme: prismThemes.github,
darkTheme: prismThemes.dracula,
additionalLanguages: ['csharp', 'python'],
},
} satisfies Preset.ThemeConfig,
};
export default config;
The built page for docs/intro had the title Tutorial Intro | Pocket Units and the canonical link
https://example-org.github.io/pocket-units/docs/intro, which combines url, baseUrl and the route without a
trailing slash. The docs/intro.html file name follows from trailingSlash: false. The blog option of the preset is
false here, so the blog plugin is not loaded and the blog/ folder of the template is unused.
Pitfalls and version notes
-
Unknown top-level keys fail the build; use
customFields. -
urlhas no path andbaseUrlhas both slashes. Fix a deployed site without styles by checkingbaseUrlfirst. -
Keep
onBrokenLinks: 'throw'for CI; it applies to production builds, not todocusaurus start. -
Do not use
onBrokenMarkdownLinksat the top level in new sites (deprecated in 3.9, removed in v4). -
Do not use
future.experimental_fasterorfuture.experimental_storage; both fail at startup in 3.10. -
future.v4: truealso enables Faster and turns off themdx1Compatleniency, so a site that relied on old comment or admonition syntax must be fixed or must set those options explicitly. -
TypeScript 6 and strict mode are the default for new TypeScript sites since 3.10.
-
Do not put secrets into
customFields,headTagsorscripts: they are shipped to every visitor.