Skip to main content

Configuration

By default, Styleguidist will look for styleguide.config.js file in your project’s root folder. You can change the location of the config file using --config CLI option.

Config file formats

The config file may be a CommonJS module (module.exports = {…}) or an ES module (export default {…}), written in JavaScript or TypeScript. Styleguidist looks for these names, in this order, in the current folder and its parents:

  1. styleguide.config.js
  2. styleguide.config.mjs
  3. styleguide.config.cjs
  4. styleguide.config.ts
  5. styleguide.config.mts
  6. styleguide.config.cts

Which module system a file is written in follows Node’s own rules: .mjs and .mts are always ES modules, .cjs and .cts are always CommonJS, and a .js or .ts file is an ES module when the nearest package.json has "type": "module".

warning

Config files are loaded synchronously, top-level await isn’t supported in them.

TypeScript config files need no ts-node, tsx or any other loader: Styleguidist strips the types itself with Sucrase, the same compiler it uses for examples in the browser. Two things follow from that:

  • only the config file itself is compiled, so a TypeScript module it imports is left to Node.js, which can strip types from version 22.18 on. Import a JavaScript module instead if you support older versions;
  • the types are stripped, not checked. Nothing at load time reads an annotation, so tsc and your editor are the only things that check one. Styleguidist still validates the resulting object against its own schema, so a wrong type is caught either way — later, and with a different message.

Sucrase has no type information, so it decides what to erase by use: an import whose bindings never appear in a value position is dropped together with its import statement, whether or not it says import type. Write import type anyway — it states the intent, tsc enforces it under verbatimModuleSyntax, and it does not depend on a heuristic. Side-effect imports (import './setup.js') have no bindings to judge and are always kept.

The compiled file is written next to the original one, loaded, and deleted again, so the folder holding your config file has to be writable. Because it is a sibling of your config, it belongs to the same package.json and the same module system — __dirname and import.meta.dirname are the folder you expect. The file name is not: __filename, import.meta.filename and import.meta.url name the temporary file. Most configs need neither, because Styleguidist resolves the relative paths in a config against the folder the config file is in.

Your config file can always import vite-styleguidist itself — for defineConfig, for a type, or for anything else the package exports — even from a folder the package is not installed in: an example folder with no node_modules of its own, a monorepo whose hoisting put the package where your config cannot see it, a config passed with --config from outside the project. Styleguidist is the thing loading the file, so while it does, a vite-styleguidist specifier (or a subpath of it) that Node cannot resolve on its own resolves to the copy doing the loading. Nothing else is redirected, and a project that does have the package installed keeps resolving to its own copy. The one exception is Node 22.12–22.14, which have no synchronous ES module resolution hook: on those three versions an ES module config file still needs the package resolvable from its own folder, while a CommonJS one (.cjs, .cts, or a .ts in a package without "type": "module") works everywhere.

tip

examples/typescript is the worked example: a styleguide.config.ts with defineConfig, a sections tree typed as ConfigSection[] in a module of its own, and a walkthrough of the discovery order, the loading, what exists inside the file, and the JavaScript alternative.

Type checking your config

defineConfig types a TypeScript config file. It returns the object untouched — the point is that a mistyped option is an error in your editor instead of one the next time you start the style guide:

// styleguide.config.ts
import { defineConfig } from 'vite-styleguidist'

export default defineConfig({
title: 'My Style Guide',
components: 'src/components/**/*.tsx'
})

A JavaScript config file gets the same checking from a type comment, with no import and nothing to run:

// styleguide.config.js
/** @type {import('vite-styleguidist').StyleguidistConfig} */
export default {
title: 'My Style Guide',
components: 'src/components/**/*.js'
}

Every type a config file needs is exported from the package: StyleguidistConfig for the config itself, ConfigSection for a section, Theme and RecursivePartial for a theme of your own, Styles for styles. See the Node.js API for the whole list, and examples/typescript for both forms side by side — including what to annotate once a sections array moves out of defineConfig into a module of its own, where the type no longer flows in from the parameter.

Restarting on a change

The dev server watches the config file it was started from. When you save it, the style guide reloads the config and restarts itself; when the config you saved has a mistake in it, the error is printed and the style guide keeps running with the last config that worked. See CLI commands.

What is watched is that one file, not the modules it imports: a config split across several files restarts when the config file itself is saved.

The rest of this page is the config options, in alphabetical order.

assetsDir

Type: String or Array, optional

Your application static assets folder will be accessible as / in the style guide dev server, and its files are copied into the styleguideDir folder by styleguidist build.

cache

Type: Boolean, default: true

Reuse the component and example parses of previous runs.

Parsing is the expensive half of building a style guide — react-docgen for every component, remark plus a JavaScript parse for every Markdown example — and almost none of it changes between two runs. With this option on, each parse is written to a cache inside Vite's cacheDir, keyed by the SHA-256 of the file's own content, and the next run reads it back instead of parsing again. On a 350-component design system, a rebuild in which nothing changed goes from 2460 ms to 1080 ms.

The cache is shared by styleguidist build and styleguidist server, so a build right after a dev-server session starts warm, and it is content-addressed rather than timestamp-addressed, so switching branches or re-cloning the project still hits it.

An entry is only used when everything it could depend on is unchanged: the file's content, the version of Vite Styleguidist and of the packages that do the parsing (react-docgen, @mdx-js/mdx, remark-gfm), and every config option a parse can read — context, defaultExample, getExampleFilename, handlers, mdx, propsParser, resolver, sortProps, updateDocs and updateExample, functions included, by their source text. Change any of them and the cache starts empty. Options that cannot affect a parse — theme, serverPort, styleguideDir and the rest — deliberately do not invalidate it.

“The file's content” means more than the component's own file. Documenting a component reads the files it imports — the module its propTypes come from, the file its props interface is declared in — so each entry also remembers those files and the content each had, and is thrown away as soon as one of them differs. The dev server watches them too: editing a shared types.ts updates the props table of every component that reads it, without a restart.

module.exports = {
cache: false
}

Where it lives, and how to delete it. node_modules/.vite/vite-styleguidist/parse-cache.json, next to Vite's own caches; a project that moves Vite's cacheDir moves this with it. Deleting that folder, or node_modules, clears it — Vite's own --force does not, because that only clears its dependency optimizer. styleguidist build --no-cache and styleguidist server --no-cache ignore it (and write nothing) for one run. Entries that no run has touched for five runs are dropped, and the file is capped at 96 MB; it is about 8 MB for 350 components.

Nothing in it is secret that the style guide does not already publish, but it is a build artefact: keep it out of version control, like the rest of node_modules.

note

A propsParser written as a function turns off caching of component documentation (examples are still cached). A function has no identity across processes — two runs can pass different closures with the same source — so a cached answer could not be trusted. Write the parser as a module and point the option at its path instead; that form is cacheable, and it is what the cookbook recipe uses.

note

With a propsParser of your own, in either form, Styleguidist cannot observe which files the parser read, so it follows the component's own relative imports instead — transitively, type-only imports included. That covers the ordinary shape (import type { CardProps } from './types'), and it does not cover a type reached through a path alias such as @/types, or one that lives in an installed package. Run styleguidist build --no-cache once after editing a file in that last group.

note

styleguidist doctor prints where the cache is, how large it is, and whether component documentation is being cached.

note

The location follows Vite's root, which is the folder your config file is in — so two style guide configs sitting side by side in the same folder share one cache file. If they differ in one of the options listed above, each run empties what the other one wrote: correct, but never warm. Give one of them its own viteConfig.cacheDir if that is your layout.

colorScheme

Type: String, default: system

Colour scheme of the style guide UI (not of your components):

  • system: follow the visitor’s operating system (prefers-color-scheme) and show a system / light / dark toggle in the sidebar header. The visitor’s choice is remembered in localStorage.
  • light or dark: always use that scheme and hide the toggle.
module.exports = {
colorScheme: 'dark'
}

The scheme is applied to the <html> element as the data-rsg-theme attribute (light, dark, or absent for system) by an inline script in the generated page, before the first paint, so there is no flash of the wrong scheme. See dark mode in the cookbook for how the colours work, and theme for the rule about overridden colours.

note

A custom template function receives colorScheme in its context and has to include the <meta name="color-scheme"> tag and the inline script itself; colorSchemeScript(colorScheme) from vite-styleguidist/lib/vite/html.js returns the script.

note

The inline script needs 'unsafe-inline' in a Content-Security-Policy script-src, or a nonce that a custom template adds to the <script> tag. When the policy blocks it the page still works: it renders in the light scheme first and switches to the stored or forced scheme once the bundle runs.

compilerConfig

Type: Object, default:

{
transforms: ['jsx', 'typescript'],
// Examples get `React` injected, so the classic runtime just works
jsxRuntime: 'classic',
// Skip development-only __source/__self props (React 19 warns about them)
production: true,
// Leave modern syntax alone, all supported browsers understand it
disableESTransforms: true,
// Never strip imports: side-effect imports are common in examples and
// import statements are rewritten to require() calls by Styleguidist
keepUnusedImports: true
}

Styleguidist uses Sucrase to compile examples (JSX and TypeScript) in the browser. This config object will be passed as the second argument for sucrase.transform().

warning

The option replaces the default value, it isn’t merged with it. Start from the defaults, which you can import from vite-styleguidist/lib/client/utils/compileCode.js as DEFAULT_COMPILER_CONFIG.

components

Type: String, Function or Array, default: src/@(components|Components)/**/*.{js,jsx,ts,tsx} (see Locating components for the Windows fallback)

  • when String: a glob pattern that matches all your component modules.
  • when Function: a function that returns an array of module paths.
  • when Array: an array of module paths.

All paths are relative to config folder.

note

Patterns are case-sensitive on every platform: [A-Z]*.js won’t match index.js.

See examples in the Components section.

context

Type: Object, optional

Modules that will be available for examples. You can use it for utility functions like Lodash or for data fixtures.

module.exports = {
context: {
map: 'lodash/map',
users: path.resolve(__dirname, 'fixtures/users')
}
}

Then you can use them in any example:

<Message>{map(users, 'name').join(', ')}</Message>

contextDependencies

Type: String[], optional

Array of absolute paths that allow you to specify absolute paths of directories to watch for additions or removals of components.

By default Styleguidist uses common parent directory of your components.

module.exports = {
contextDependencies: [path.resolve(__dirname, 'lib/components')]
}

configureServer

Type: Function, optional

Function that allows you to add endpoints to the underlying Vite dev server:

module.exports = {
configureServer(app, env, server) {
// `app` is the Connect middleware stack of the Vite dev server
// running Styleguidist, `server` is the ViteDevServer instance
app.use('/custom-endpoint', (req, res) => {
res.setHeader('Content-Type', 'application/json')
res.end(JSON.stringify({ response: 'Server invoked' }))
})
}
}

Your components will be able to invoke the URL http://localhost:6060/custom-endpoint from their examples.

The middleware stack is a Connect instance (app.use()), not an Express app: req and res are plain Node.js request and response objects. Middlewares added here run before Vite’s own.

dangerouslyUpdateViteConfig

Type: Function, optional

danger

You may break Styleguidist by using this option, try to use viteConfig option instead.

Allows you to modify the final Vite config without any restrictions:

module.exports = {
dangerouslyUpdateViteConfig(viteConfig, env) {
// WARNING: inspect Styleguidist Vite config before modifying it, otherwise you may break Styleguidist
console.log(viteConfig)
viteConfig.build.chunkSizeWarningLimit = 5000
return viteConfig
}
}

defaultExample

Type: Boolean or String, default: false

For components that do not have an example, a default one can be used. When set to true, the DefaultExample.md is used, or you can provide the path to your own example Markdown file.

When writing your own default example file, __COMPONENT__ will be replaced by the actual component name at compile time.

envPrefix

Type: String or Array, default: [] (nothing is exposed)

Prefixes of the environment variable names your components and examples may read as process.env.NAME. This is the option that makes code brought over from webpack or Create React App work as it did:

module.exports = {
envPrefix: ['REACT_APP_']
}
// in a component or an example
export default function Header() {
return <h1>{process.env.REACT_APP_TITLE}</h1>
}

A single prefix may be written as a string, envPrefix: 'REACT_APP_'.

The values are read with Vite’s own loader, so they come from two places at once:

  • the environment of the command, REACT_APP_TITLE='Pizza' npx styleguidist build;
  • the project’s .env, .env.local, .env.development / .env.production and .env.<mode>.local files, read from the folder of the config file (or from viteConfig.envDir when you moved them). The mode is development for the dev server and production for a build.

The environment of the command wins over the files, exactly as in a Vite application.

danger

every matching value is inlined, in clear, into the JavaScript bundle of the style guide, which is usually deployed publicly. Expose the variables you would be happy to print on the page, never an API key or a token. This is why nothing is exposed by default, why an empty prefix ('') is refused, and why Styleguidist prints the names it inlines when it starts or builds.

NODE_ENV and STYLEGUIDIST_ENV are never taken from the environment, whatever the prefixes match: Styleguidist and Vite define those two themselves.

This option is about process.env. Vite’s own envPrefix is a different setting with the same name: it governs import.meta.env and is untouched here — set it in viteConfig as well if your examples read import.meta.env.REACT_APP_TITLE too.

Without the option, a process.env.SOMETHING in a component isn’t an error in the browser: Vite replaces process.env with an empty object, so the value is silently undefined and the page renders a blank spot. If a variable doesn’t appear where you expect it, check the prefix first.

exampleMode

Type: String, default: collapse

Defines the initial state of the example code tab:

  • collapse: collapses the tab by default.
  • hide: hide the tab and it can´t be toggled in the UI.
  • expand: expand the tab by default.

getComponentPathLine

Type: Function, default: component filename

Function that returns a component path line (displayed under the component name).

For example, instead of components/Button/Button.js you can print import Button from 'components/Button';:

const path = require('path')
module.exports = {
getComponentPathLine(componentPath) {
const name = path.basename(componentPath, '.js')
const dir = path.dirname(componentPath)
return `import ${name} from '${dir}';`
}
}

getExampleFilename

Type: Function, default: finds Readme.md, Readme.mdx, ComponentName.md or ComponentName.mdx in the component folder

Function that returns examples file path for a given component path. The extension of the path you return selects the pipeline: .md is Markdown, .mdx is MDX.

For example, instead of Readme.md you can use ComponentName.examples.md:

module.exports = {
getExampleFilename(componentPath) {
return componentPath.replace(/\.jsx?$/, '.examples.md')
}
}

handlers

Type: Function, optional, default: react-docgen’s defaultHandlers

Function that returns an array of react-docgen handlers used to process the discovered components and generate documentation objects. Default behaviors include discovering component documentation blocks, prop types, defaults, methods and display names. If setting this property, it is best to build from the default handler list, such as in the example below.

A handler is a function (documentation, componentDefinition) => void, see the react-docgen handler documentation.

const { defaultHandlers } = require('react-docgen')
module.exports = {
handlers: componentPath => [
...defaultHandlers,
// Record the source file of every component
(documentation, componentDefinition) => {
documentation.set('sourceFile', componentPath)
}
]
}
note

When react-docgen can’t infer a display name, Styleguidist uses the file name (or the folder name for index.js files), so react-docgen-displayname-handler isn’t needed.

ignore

Type: String[], default: ['**/__tests__/**', '**/*.test.{js,jsx,ts,tsx}', '**/*.spec.{js,jsx,ts,tsx}', '**/*.d.ts']

Array of glob pattern that should not be included in the style guide.

warning

You should pass glob patterns, for example, use **/components/Button.js instead of components/Button.js.

lazyDocs

Type: Boolean, default: true

Load each component’s documentation when the page needs it, instead of putting all of it in the first script the browser downloads.

module.exports = {
lazyDocs: false
}

With this on — it is on by default — the style guide’s entry script carries the section tree: every component’s name, slug, path line and anchor, which is what the sidebar, the routes, the headings and the deep links are drawn from. The documentation itself — the props table, the JSDoc description, the examples, and the component’s own module — is imported per component, when that component is what the page shows (an isolated #!/Button view, a pagePerSection page, the target of a deep link) or when it comes near the viewport on the all-in-one page. On a style guide of 350 components this takes the entry chunk from 4.7 MB to 1.2 MB and the number of modules the dev server serves before the first render from 1130 to 67.

A component whose documentation has not arrived yet renders its container and its heading, and — once the wait has lasted more than 200 ms — a small spinner labelled “Loading documentation…” where its body will be. It shows no props table and no examples, and the “add examples to this component” hint only for a component the style guide already knows has none. A component whose documentation could not be fetched says so in the same place, with a button that reloads the page. Both are the DocsLoading component, which styleguideComponents can replace like any other.

Turn it off to put every component’s documentation back into the entry chunk, which is how style guides were built before this option existed. Two reasons to: a deployment that would rather serve one big file than many small ones, and a replaced ReactComponent (see styleguideComponents) that cannot cope with a component whose documentation is not there yet — see the Cookbook for what a replaced component sees and how to group the chunks differently.

logger

Type: Object, by default will use console.* in CLI or nothing in Node.js API

Custom logger functions:

module.exports = {
logger: {
// One of: info, debug, warn
// Suppress messages
info: () => {},
// Override display function
warn: message => console.warn(`NOOOOOO: ${message}`)
}
}

machineReadable

Type: Boolean, default: true

Emit a machine-readable copy of the style guide next to index.html, for AI assistants, editor integrations and scripts:

  • docs.json: every section and component with its description, props (name, type, required, default value, description, JSDoc tags), public methods and usage examples, as JSON;
  • llms.txt: an index in the llms.txt format, one line per component with a link to it in the style guide;
  • llms-full.txt: the whole style guide as one Markdown document.

The files are generated from the same sources as the style guide itself (react-docgen output, Markdown examples), in the order of the sidebar. styleguidist build writes them into styleguideDir; the dev server serves them at /docs.json, /llms.txt and /llms-full.txt, regenerated on every request. Set the option to false to skip them. docs.json carries a generatedAt timestamp; set the SOURCE_DATE_EPOCH environment variable (seconds since the Unix epoch) to pin it for reproducible builds.

module.exports = {
machineReadable: false
}
warning

The files are plain, unprotected downloads: when the style guide is deployed, everything in them (descriptions, examples, file paths relative to the project) is public, exactly like the style guide page is. Turn the option off if the style guide is served from somewhere you don’t want to expose that way.

See How do I make my style guide readable by AI tools? for the details of each file.

mdx

Type: Object, optional

Options for the MDX pipeline, passed to @mdx-js/mdx’s compile():

  • remarkPlugins: remark plugins, default [remarkGfm];
  • rehypePlugins: rehype plugins, default none;
  • recmaPlugins: recma plugins, default none.

Each is a unified plugin list: a plugin, or a [plugin, options] pair, per entry. Setting remarkPlugins replaces the default, so keep remark-gfm in the list if you still want GFM tables, task lists and strikethrough:

// styleguide.config.mjs — remark plugins are ES modules
import remarkGfm from 'remark-gfm'
import remarkFrontmatter from 'remark-frontmatter'

export default {
mdx: {
remarkPlugins: [remarkGfm, remarkFrontmatter]
}
}

The option has no effect on .md files, which are parsed by the Markdown pipeline and are not affected by MDX plugins.

mdxComponents

Type: Object, optional

Extra components available to every MDX page, as a map of name to the module that default-exports the component. They are merged over the default element map, so an entry can either add a shortcode that any .mdx file may use without importing it, or replace how an HTML element of the prose is rendered:

module.exports = {
mdxComponents: {
// Usable as <Callout kind="info"> in any .mdx file, no import needed
Callout: 'styleguide/components/Callout',
// Every table of every MDX page is rendered by this component
table: 'styleguide/components/Table'
}
}

Each value is a module path and is resolved like a styles or theme path: relative to the style guide config file, so the entries above are styleguide/components/Callout and styleguide/components/Table next to the config. An absolute path (path.join(__dirname, 'styleguide/components/Callout')) works too, and the extension may be omitted — Vite resolves the rest like any import. The module is imported into the style guide’s browser bundle and must default-export the component.

Lowercase keys are HTML element names; capitalised keys are components an .mdx file can use as JSX elements. Without this option a page imports what it needs itself, which is the usual way — reach for mdxComponents when the same component belongs on many pages.

minimize

Type: Boolean, default: true

If false, the production build will not be minimized.

moduleAliases

Type: object, optional

Define aliases for modules, that you can import in your examples, to make example code more realistic and copypastable:

const path = require('path')
module.exports = {
moduleAliases: {
'rsg-example': path.resolve(__dirname, 'src')
}
}
// ```jsx inside Markdown
import React from 'react'
import Button from 'rsg-example/components/Button'
import Placeholder from 'rsg-example/components/Placeholder'

Aliases are passed to Vite as resolve.alias entries: an alias matches the module name itself (rsg-example) and any path under it (rsg-example/components/Button).

mountPointId

Type: string, default: rsg-root

The ID of a DOM element where Styleguidist mounts.

Type: Boolean, default: false

Add an “on this page” list of the current page’s own headings.

module.exports = {
pagePerSection: true,
pageNav: true
}

The list is built from the headings the page actually renders — every h2 and h3 that has an id, in document order — so it works the same for Markdown and MDX documentation and for a custom Heading component. A page with fewer than two of them gets no list at all.

Where it appears depends on the width of the window: from 1480 px up it is a rail beside the content column, which sticks below the header as you scroll and highlights the heading you are reading; below that the same list is a collapsible block above the content, closed until you open it. The content column keeps its width and its position either way — the space the rail takes is reserved on every page of the style guide, so the text does not move sideways when you open a page that has no list. The breakpoint is the mq.large theme key, and the width of the rail is pageNavWidth.

It only appears on pages that show a single component or section: the pagePerSection pages, the #/Section routes and the isolated #!/Component view. On the default all-in-one page, where every component of the style guide is on one page, the sidebar is the page navigation — it already follows the scroll, see scrollSync — and a list of every heading of every component would only repeat it.

If you replace StyleGuideRenderer through styleguideComponents, render the pageNav prop it receives where you want the list; without that the option does nothing for your style guide. The list itself is PageNav / PageNavRenderer and can be replaced the same way, see the Cookbook.

pagePerSection

Type: Boolean, default: false

Render one section or component per page.

If true, each section will be a single page.

The style guide needs named sections to page by: a configuration that only lists components has a single unnamed root section, so there is nothing to split and every component stays on one page whatever this option says.

The value may depend on a current environment:

module.exports = {
pagePerSection: process.env.NODE_ENV !== 'production'
}

To isolate section’s children as single pages (subroutes), add sectionDepth into each section with the number of subroutes (depth) to render as single pages.

For example:

module.exports = {
pagePerSection: true,
sections: [
{
name: 'Documentation',
sections: [
{
name: 'Files',
sections: [
{
name: 'First File'
},
{
name: 'Second File'
}
]
}
],
// Will show "Documentation" and "Files" as single pages, filtering its children
sectionDepth: 2
},
{
name: 'Components',
sections: [
{
name: 'Buttons',
sections: [
{
name: 'WrapperButton'
}
]
}
],
// Will show "Components" as single page, filtering its children
sectionDepth: 1
},
{
name: 'Examples',
sections: [
{
name: 'Case 1',
sections: [
{
name: 'Buttons'
}
]
}
],
// There is no subroutes, "Examples" will show all its children on a page
sectionDepth: 0
}
]
}

printBuildInstructions

Type: Function, optional

Function that allows you to override the printing of build messages to console.log.

module.exports = {
printBuildInstructions(config) {
console.log(
`Style guide published to ${config.styleguideDir}. Something else interesting.`
)
}
}

printServerInstructions

Type: Function, optional

Function that allows you to override the printing of local dev server messages to console.log. The second argument tells whether the server uses HTTPS and lists its URLs.

module.exports = {
printServerInstructions(config, { isHttps, urls }) {
// urls.local and urls.network are arrays of URLs
console.log(`Local style guide: ${urls.local[0]}`)
}
}

parallel

Type: Boolean, Number or 'auto', default: 'auto'

Parse components and examples in worker threads.

react-docgen and the Markdown pipeline are synchronous CPU work, so without this they run one after another on the main thread while the rest of the machine idles. With four workers, a 350-component design system builds in 1610 ms instead of 2570 ms — at the price of about 500 MB more peak memory, which is why the default is a decision rather than “on”.

  • 'auto' (default): use workers when the style guide resolved 150 components or more and there is real parsing to do — at least 25 files the cache could not answer. Two to four workers, never more (measured: six is the same speed, eight is slower, and each one costs about 100 MB).
  • true: always use workers, whatever the size of the guide.
  • A number: that many workers.
  • false: parse everything on the main thread, as Styleguidist always did.
module.exports = {
parallel: 4
}

What cannot go to a worker. A worker thread receives a copy of plain data, and a function is not copyable — so a parse that has to call one of your config functions runs on the main thread instead, whatever this option says. For component documentation that is propsParser (in either form), resolver, handlers, sortProps, updateDocs and getExampleFilename; for examples it is updateExample. The two halves are decided separately, so a custom sortProps still leaves your Markdown examples parsed in parallel. styleguidist doctor names whichever of them applies to your config.

Everything a worker produces is byte-for-byte what the main thread would have produced; the option changes when the work happens, never what comes out of it.

note

Workers are started on demand and stopped when the build finishes or the dev server closes. A guide below the threshold, or a rebuild in which almost nothing changed, never starts one and never pays for one.

previewDelay

Type: Number, default: 500

Debounce time in milliseconds used before rendering the changes from the editor. While typing code the preview will not be updated.

propsParser

Type: Function or String, optional

Override the mechanism used to parse props from a source file. The default mechanism is react-docgen. The parser receives the file path, its source code, and the resolver and handlers from the config, and returns a react-docgen documentation object or an array of them (only the first one is used).

It can be the function itself:

const { parse } = require('react-docgen')
module.exports = {
propsParser(filePath, source, resolver, handlers) {
return parse(source, { resolver, handlers, filename: filePath })
}
}

or — recommended — the path of a module whose default export is that function, resolved from the config file's folder (a package name works too):

// styleguide.config.js
module.exports = {
propsParser: './styleguide.parser.js'
}
// styleguide.parser.js
const { parse } = require('react-docgen')

module.exports = function propsParser(filePath, source, resolver, handlers) {
return parse(source, { resolver, handlers, filename: filePath })
}

The two forms call exactly the same parser; the difference is what Styleguidist can say about it. A module has an identity — its resolved path plus its content, or its package's version — so the parse cache can tell one run's parser from another's and skip components that have not changed. A function has no identity across processes, so a function parser turns component-documentation caching off, which is a shame precisely where it costs most: a react-docgen-typescript parser is the slowest thing a style guide does. The cookbook recipe is written in the module form for that reason.

note

The module is loaded once per process, so anything expensive it sets up at module scope — a TypeScript program, a compiler host — is set up once, exactly as it would be at the top of your config file.

note

Only that one file's content is part of the cache key. If your parser module imports helpers of its own, changing a helper does not invalidate the cache: run styleguidist build --no-cache once, or delete node_modules/.vite/vite-styleguidist/.

note

What your parser reads cannot be observed either — the parsers this option exists for resolve types through a TypeScript program of their own. The cache therefore assumes a parse depends on the component and on the files the component imports relatively, transitively; a type reached through a path alias or from an installed package is outside that assumption, and --no-cache is the answer after such an edit. The default parser has no such limit: react-docgen is given an importer Styleguidist owns, so the files it follows are known exactly.

note

Either form always runs on the main thread, never in a parallel worker. Four workers would build four copies of whatever the parser sets up — for react-docgen-typescript, four TypeScript programs of about a gigabyte each. See decision 0018.

TypeScript: you probably don’t need this option. The default parser reads TypeScript type annotations, so .tsx components are documented with no configuration at all — types, required flags, default values and JSDoc descriptions, including prop types imported from a neighbouring module. It documents the props a component declares, so a component whose props extend React.ButtonHTMLAttributes gets a table of its own props rather than the ~290 attributes the DOM interface adds. See the TypeScript example and the cookbook recipe.

Reach for propsParser when a component is resolved through the type system instead of written in the file being parsed — above all one re-exported from another package, which the default parser cannot follow. The cookbook has a verified react-docgen-typescript recipe for exactly that case, and decision 0017 has the measurements behind the split.

note

the function is called once per component and per build, so whatever it sets up should be set up outside of it. This matters most with react-docgen-typescript, whose parse() creates a new TypeScript program on every call: the cookbook recipe shares one program instead, which builds a 50-component style guide more than five times faster.

require

Type: String[], optional

Modules that are required for your style guide. Useful for third-party styles or polyfills.

module.exports = {
require: [
'core-js/stable',
path.join(__dirname, 'styleguide/styles.css')
]
}
note

These modules are imported at the top of the style guide bundle, before Styleguidist’s own code. Installed packages and absolute paths work; CSS, Sass, images and other file types Vite understands don’t need any extra configuration.

See Configuring Vite for more details.

resolver

Type: Object (resolver instance) or Function, optional

A react-docgen resolver that identifies the components to document in a file. Default behavior is to find all exported components in each file, plus anything exported with a @component JSDoc annotation (which makes styled-components and other non-standard components work). You can configure it to find all components or use a custom detection method.

const { builtinResolvers } = require('react-docgen')
module.exports = {
// Document all components found in a file, not only the exported ones
resolver: new builtinResolvers.FindAllDefinitionsResolver()
}

The default is a ChainResolver of Styleguidist’s own FindAnnotatedExportsResolver (available as vite-styleguidist/lib/loaders/utils/FindAnnotatedExportsResolver.js) and react-docgen’s FindAnnotatedDefinitionsResolver and FindExportedDefinitionsResolver.

ribbon

Type: Object, optional

Show a link to your repository: in the sidebar footer, next to the colour-scheme toggle, or as a small pill in the top-right corner when the style guide has no sidebar (showSidebar: false, isolated views). The default text is “GitHub”.

module.exports = {
ribbon: {
// Link to open on the ribbon click (required)
url: 'http://example.com/',
// Text to show on the ribbon (optional)
text: 'Fork me on GitHub'
}
}

Use the theme config option to change ribbon style.

scrollSync

Type: String or false, default: selection

Keep the sidebar (and optionally the URL) on the section the reader has scrolled to, on a style guide that shows everything on one page:

  • selection: the highlighted sidebar entry follows the scroll; the URL is never touched.
  • hash: the same, and the fragment of the address is rewritten to the section on screen with history.replaceState, so a copied link points at what the reader was looking at.
  • false: the selection only changes when the reader clicks an entry or opens a link, which is what Styleguidist did before 1.0.
module.exports = {
scrollSync: 'hash'
}

Only the default one-page layout has anything to follow: with pagePerSection the sidebar links are routes rather than anchors, and an isolated view has no sidebar, so the option has no effect in either. Nothing is ever pushed onto the history stack and no hashchange event is fired, so the back button and any code that listens for navigation behave exactly as before; code that polls location.hash will see it change while the reader scrolls in hash mode.

sections

Type: Array, optional

Allows components to be grouped into sections with a title and overview content. Sections can also be content only, with no associated components (for example, a textual introduction). Sections can be nested. A section’s content may be a .md or an .mdx file, see MDX.

See examples of sections configuration.

serverHost

Type: String, default: 0.0.0.0

Dev server hostname.

serverPort

Type: Number, default: process.env.NODE_PORT or 6060

Dev server port. Can also be set via command line --port=6060.

note

Styleguidist fails to start when the port is already in use instead of picking another one.

showSidebar

Type: Boolean, default: true

Toggle sidebar visibility. The sidebar will be hidden when opening components or examples in isolation mode even if this value is set to true. When set to false, the sidebar will always be hidden.

skipComponentsWithoutExample

Type: Boolean, default: false

Ignore components that don’t have an example file (as determined by getExampleFilename). These components won’t be accessible from other examples unless you manually require them.

sortProps

Type: Function, optional

Function that sorts component props. By default props are sorted such that required props come first, optional props come second. Props in both groups are sorted by their property names.

To disable sorting, use the identity function:

module.exports = {
sortProps: props => props
}

styleguideComponents

Type: Object, optional

Override React components used to render the style guide:

module.exports = {
styleguideComponents: {
Wrapper: path.join(__dirname, 'styleguide/components/Wrapper'),
StyleGuideRenderer: path.join(
__dirname,
'styleguide/components/StyleGuide'
)
}
}

Keys are component names (Wrapper, StyleGuideRenderer, SectionsRenderer, DocsLoadingRenderer), check the source to see what components are available.

Values are written like imports and Vite resolves them like imports: an absolute path, a path relative to the config file (./styleguide/components/Wrapper), or a module name from your dependencies. The extension (.js, .jsx, .ts, .tsx, etc.) may be omitted.

See an example of customized style guide.

To wrap, rather than replace a component, import the default implementation by its full path inside vite-styleguidist, for example vite-styleguidist/lib/client/rsg-components/Sections/SectionsRenderer — with or without the .js extension, both resolve. See an example of wrapping a Styleguidist component.

Note: these components are not guaranteed to be safe from breaking changes in Styleguidist updates, except Editor, whose props are a stable contract (see below).

Editor

The code editor shown under an example when you click “View Code” is CodeMirror 6 with JavaScript, JSX and TypeScript highlighting, undo history, bracket matching and closing, basic autocompletion and search (Ctrl/Cmd+F inside the editor). It has no line numbers. Its chunk is loaded on demand: a style guide page fetches CodeMirror only the first time an editor opens, and shows the code as plain text meanwhile.

Keyboard: Tab indents the current line, Shift+Tab outdents. To move the focus out of the editor with the keyboard, press Escape and then Tab (or Shift+Tab): after Escape, Tab moves the focus like anywhere else on the page for two seconds. For assistive technology the editor is labelled with the component’s name and the example’s index, “Code editor for Button example 2” (a custom editor receives them as exampleName and exampleIndex, see the table below).

Colors follow the theme option: the same theme.color.code* keys that style static code blocks style the editor, see How to change syntax highlighting colors? in the cookbook.

You can replace the editor with your own component:

// styleguide.config.js
import path from 'node:path'
import { fileURLToPath } from 'node:url'

const dirname = path.dirname(fileURLToPath(import.meta.url))

export default {
styleguideComponents: {
Editor: path.join(dirname, 'src/styleguide/Editor')
}
}

The CommonJS form (require, __dirname, module.exports) works in a project without "type": "module", and in a styleguide.config.cjs file in any project.

When you do, CodeMirror is not part of your bundle at all. The component receives these props (the EditorProps type exported from vite-styleguidist/lib/typings/index.d.ts), and this list is a public contract: keys are only ever added, never removed or renamed:

PropTypeDescription
codestringCurrent source of the example. Controlled: Styleguidist owns it and passes it back after onChange (debounced) and when the Markdown file changes on hot reload.
onChange(code: string) => voidCall it with the whole source after every change. Calls are debounced by the previewDelay option (500 ms by default) before the preview re-renders.
evalInContext(code: string) => () => any, optionalCompiles and runs example code the way the preview does. The built-in editor doesn’t use it.
namestring, optionalId of the slot fill, rsg-code-editor for the built-in code tab. Not the example name.
activeboolean, optionalWhether the tab is the active one; always true when the editor is rendered, since only the active tab is.
onClickfunction, optionalTab click handler of the slot (its id is bound already). Not needed by an editor.
exampleNamestring, optionalName of the component or section the example belongs to. The built-in editor uses it, with exampleIndex, for its accessible label.
exampleIndexnumber, optionalIndex of the example in its Markdown file, the same number the isolated example URL uses.
langstring, optionalFence language of the example (jsx, tsx, …), absent for a bare fence. The built-in editor shows it as the badge in the corner of the code area, and labels a bare fence JSX, since every playground example is compiled with the JSX and TypeScript transforms.

See How to replace the code editor? in the cookbook for a minimal implementation.

styleguideDir

Type: String, default: styleguide

Folder for static HTML style guide generated with styleguidist build command. The page is written to index.html and the bundle to the build subfolder, which is the only thing cleaned before a build.

styles

Type: Object, String or Function, optional

Customize styles of any Styleguidist’s component using an object, a function returning said object or a file path to a file exporting said styles.

See examples in the cookbook.

tip

Using a function allows access to theme variables like in the example below. See available theme variables and the theme option for what the colour tokens contain. The returned object follows the same format as when configured as a literal.

module.exports = {
styles: function (theme) {
return {
Logo: {
logo: {
// we can now change the color used in the logo item to use the theme's `link` color
color: theme.color.link
}
}
}
}
}

Note: If using a file path, it has to be absolute or relative to the config file. The file is bundled for the browser and must be an ES module (export default {…} or export default theme => ({…})).

Note: Component names and the keys inside them (Logo, logo) are part of the public contract: keys are only ever added, never renamed, so a styles config keeps working across minor releases. The generated class names look like rsg--logo-1234567890; the number is derived from the component and its keys, it is not something to target.

template

Type: Object or Function, optional.

Change HTML for the style guide app.

An object with options to add a favicon, meta tags, inline JavaScript or CSS, etc.:

module.exports = {
template: {
lang: 'en',
favicon: 'https://assets-cdn.github.com/favicon.ico',
head: {
meta: [{ name: 'description', content: 'My style guide' }],
links: [
{ rel: 'stylesheet', href: 'https://example.com/fonts.css' }
],
scripts: [
{ src: 'https://example.com/analytics.js', async: true }
],
raw: '<style>body { margin: 0 }</style>'
},
body: {
raw: '<div id="modal"></div>',
scripts: [{ src: 'https://example.com/app.js' }]
},
// Extra attributes for the bundle’s <script> and <link> tags
attrs: {
js: { defer: true },
css: { media: 'all' }
},
trimWhitespace: true
}
}

All fields are optional. head.meta, head.links, head.scripts and body.scripts are arrays of attribute objects; head.raw and body.raw accept a string or an array of strings of raw HTML.

A function that returns an HTML string. It receives publicPath (an empty string on the dev server, whose asset URLs are root-absolute, and './' in static builds), lang, title, container (the mountPointId), js and css (arrays of asset URLs):

module.exports = {
template({ publicPath, lang, title, container, js, css }) {
return `<!DOCTYPE html>
<html lang="${lang}">
<head>
<meta charset="utf-8">
<title>${title}</title>
${css
.map(file => `<link rel="stylesheet" href="${publicPath}${file}">`)
.join('\n')}
</head>
<body>
<div id="${container}"></div>
${js
.map(
file =>
`<script type="module" src="${publicPath}${file}"></script>`
)
.join('\n')}
</body>
</html>`
}
}
warning

Scripts must be loaded with type="module": the bundle is an ES module.

theme

Type: Object or String, optional

Customize style guide UI fonts, colors, etc. using a theme object or the path to a file exporting such object.

The path is relative to the config file or absolute. The file is bundled for the browser and must be an ES module (export default {…}).

See examples in the cookbook.

info

See available theme variables. The light and dark values of the colour tokens live in colorSchemes.ts.

Colour tokens and dark mode

Every theme.color.* token is a CSS custom property with the light value as fallback, var(--rsg-color-<name>, <light value>), where <name> is the token name in kebab-case: color.baseBackground is --rsg-color-base-background. The style guide defines the light values on :root, the dark values on [data-rsg-theme="dark"] and, for the system colorScheme, inside @media (prefers-color-scheme: dark). Its own definitions are wrapped in :where(), which has no specificity, so a rule of yours with the same selectors wins wherever it is loaded, even though the style guide attaches its variables last.

This has one consequence for the theme option: overriding a colour token opts that token out of dark mode. theme: { color: { link: 'firebrick' } } replaces the whole var() expression with a literal that no longer switches, so you own both schemes for that token. To change a colour in both schemes, override the custom property instead of the token, for example with template head.raw or a stylesheet listed in require:

:root {
--rsg-color-link: firebrick;
}
[data-rsg-theme='dark'] {
--rsg-color-link: salmon;
}
@media (prefers-color-scheme: dark) {
:root:not([data-rsg-theme='light']) {
--rsg-color-link: salmon;
}
}

Or set colorScheme to light if your theme only has one scheme. Numeric tokens (space, fontSize, borderRadius, maxWidth, sidebarWidth) stay numbers and are the same in both schemes.

Besides the text and code colours there are two surface tokens added with the 1.0 design, both with a light and a dark value: selectedBackground (the selected sidebar item and the active tab; base and link sit on it) and errorBackground (the playground error panel; error is the text on it). Every text colour clears WCAG AA (4.5:1) on the surface it is used on in both schemes, and a unit test keeps it that way, so if you override one side of a pair, check the other.

Tokens that components used to hard-code and that you can now override: lineHeight.base (1.55), lineHeight.heading (1.2) and lineHeight.code (1.6, the editor and static code blocks); fontWeight.normal (400) and fontWeight.bold (600; numbers, but the normal / bold keywords work too); transition.fast (150ms ease-in) and transition.slow (750ms ease-out, duration and easing only); shadow.tooltip and shadow.ribbon (complete box-shadow / text-shadow values); mq.small (@media (max-width: 600px)) and mq.medium (@media (max-width: 1024px)). Token names are only ever added, never renamed.

tip

Use React Developer Tools to find component and style names. For example a component <LogoRenderer><h1 className="rsg--logo-1234567890"> corresponds to the Logo / logo example above; the number is derived from the component and is the same for all its classes.

title

Type: String, default: <app name from package.json> Style Guide

Style guide title.

tocMode

Type: String default: expand

Defines if the table of contents sections will behave like an accordion:

  • collapse: All sections are collapsed by default
  • expand: Sections cannot be collapsed in the Table Of Contents

Collapse the sections created in the sidebar to reduce the height of the sidebar. This can be useful in large codebases with lots of components to avoid having to scroll too far.

With collapse, a section whose contents are hidden is highlighted itself while you are reading something inside it, so the sidebar still says where you are — see scrollSync.

updateDocs

Type: Function, optional

Function that modifies props, methods, and metadata after parsing a source file. For example, load a component version from a JSON file:

module.exports = {
updateDocs(docs, file) {
if (docs.doclets.version) {
const versionFilePath = path.resolve(
path.dirname(file),
docs.doclets.version
)
const version = require(versionFilePath).version

docs.doclets.version = version
docs.tags.version[0].description = version
}

return docs
}
}

With this component JSDoc comment block:

/**
* Component is described here.
*
* @version ./package.json
*/
export default class Button extends React.Component {
// ...
}
export default

updateExample

Type: Function, optional

Function that modifies code example (a fenced code block of a .md or .mdx file — it runs for both, before the block is classified as a playground or as static code). For example, you can use it to load examples from files:

module.exports = {
updateExample(props, exampleFilePath) {
const { settings, lang } = props
if (typeof settings.file === 'string') {
const filepath = path.resolve(
path.dirname(exampleFilePath),
settings.file
)
const { file, ...restSettings } = settings
return {
content: fs.readFileSync(filepath, 'utf8'),
settings: restSettings,
lang
}
}
return props
}
}

Use it like this in your Markdown files:

You can also use this function to dynamically update some of your fenced code blocks that you do not want to be interpreted as React components by using the static modifier.

module.exports = {
updateExample(props) {
const { settings, lang } = props
if (lang === 'javascript' || lang === 'js' || lang === 'jsx') {
settings.static = true
}
return props
}
}

usageMode

Type: String, default: collapse

Defines the initial state of the props and methods tab:

  • collapse: collapses the tab by default.
  • hide: hide the tab and it can´t be toggled in the UI.
  • expand: expand the tab by default.

verbose

Type: Boolean, default: false

Print debug information. Same as --verbose command line switch.

version

Type: String, optional

Style guide version, displayed under the title in the sidebar.

viteConfig

Type: Object or Function, optional

Custom Vite config options: plugins, aliases, CSS preprocessor options, defines, etc. required for your project. Vite compiles JSX, TypeScript, CSS, CSS modules, JSON and static assets out of the box, so most projects don’t need this option at all.

Can be an object:

module.exports = {
viteConfig: {
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
},
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "@/styles/variables" as *;'
}
}
}
}
}

Or a function:

module.exports = {
viteConfig(env) {
if (env === 'development') {
return {
// custom options
}
}
return {}
}
}
warning

This option disables config load from vite.config.js, load your config manually.

danger

These options will be ignored because Styleguidist controls them: root, base, appType, configFile, build.outDir, build.emptyOutDir, build.lib, build.ssr, build.manifest, build.ssrManifest, server.host, server.port, server.strictPort, server.middlewareMode, and — under both build.rolldownOptions and build.rollupOptionsinput, external, output and preserveEntrySignatures. Styleguidist owns the entry, the output location and the dev server address, and your library-build settings (like external: ['react']) would make the style guide bundle unloadable. Run with --verbose to see which options were dropped. (The list is IGNORED_OPTIONS in src/vite/mergeViteConfig.ts.)

note

Styleguidist adds @vitejs/plugin-react unless your plugins already include it.

note

The modules Styleguidist generates for your components (virtual:rsg-props?…, virtual:rsg-examples?…, virtual:rsg-mdx?…) hold no user code, and their ids are shaped so that plugins filtering by file extension skip them. Should a plugin of yours process them anyway, exclude every virtual module with /^\0/ — see the cookbook.

tip

Run style guide in verbose mode to see the actual Vite config used by Styleguidist: npx styleguidist server --verbose.

See Configuring Vite for examples.

Removed options

webpackConfig, dangerouslyUpdateWebpackConfig and updateWebpackConfig were removed together with webpack, Styleguidist throws an error when it finds them in a config. See the migration guide.