Skip to content

Configuration Reference ​

Learn more about configuring Histoire here.

plugins ​

Array of plugins. Learn more about official plugins and how to develop them.

ts
import { HstNuxt } from '@histoire/plugin-nuxt'
import { HstVue } from '@histoire/plugin-vue'

export default defineConfig({
  plugins: [
    HstVue(),
    HstNuxt(),
  ],
})

outDir ​

string - Default: '.histoire/dist'

Output directory.

Static builds put browser files directly here. Node builds put server.mjs, package.json, public/, and private MCP content here.

ts
export default defineConfig({
  outDir: '.histoire/dist',
})

mcp 1.0-beta.2+ ​

boolean | { enabled?: boolean, port?: number } - Default: true

Enable the MCP server automatically in development. Its separate listener always binds loopback. Default port 6007 falls back to an available port if occupied; an explicitly selected occupied port fails. Port 0 requests an ephemeral port. Object form defaults enabled to true. A config port does not enable enabled: false.

ts
export default defineConfig({
  mcp: { enabled: true, port: 6010 },
})

histoire dev --no-mcp disables it. --mcp overrides config false; --mcp-port <port> selects a port and implies enabled. Combining either with --no-mcp fails. Optional HISTOIRE_MCP_TOKEN requires local bearer authentication. Stdio histoire mcp operates independently and disables dev HTTP MCP in its worker.

Node builds record the enabled policy, with runtime --mcp/--no-mcp overrides. Node ignores this dev-only port and uses the book listener. Deployed MCP requires a strong HISTOIRE_MCP_TOKEN.

embed 1.0-beta.2+ ​

{ enabled?: boolean, allowedOrigins?: string[], channels?: string[], allowOpenInEditor?: boolean, allowServerTests?: boolean } - Default: disabled, no additional origins or channels, both command opt-ins false.

Enable the browser/Vue SDK source documents and surface bridge independently of MCP:

ts
export default defineConfig({
  embed: {
    enabled: true,
    allowedOrigins: ['https://app.example.com'],
    allowOpenInEditor: false,
    allowServerTests: false,
  },
})

When enabled, book origin is allowed automatically. Additional origins must exactly match scheme/host/port; paths, trailing slash, wildcard, credentials, and opaque origins are invalid. Cross-origin editor and dev-server tests require their own explicit opt-ins; origin allowlisting alone does not enable them. Static books never advertise server test execution.

channels opts into application message names matching ^[a-z][a-z0-9-]{0,31}$, with at most 100 configured names. Names confer no command or plugin-event authority. See application channels for story/host APIs and lifecycle limits.

Built books can replace additional origins at deployment without rebuilding. Static output reads optional same-base histoire-embed-origins.json with { "version": 1, "allowedOrigins": [...] }; Node deployment reads comma-separated HISTOIRE_EMBED_ORIGINS. Invalid present override permits book origin only. See deployment headers for matching frame-ancestors/host frame-src policies.

preview 1.0-beta.2+ ​

{ globals?: Record<string, string | number | boolean | null>, textDirection?: 'ltr' | 'rtl' } - Default: empty globals, 'ltr'.

Initial isolated runtime/capture settings. Globals contain at most 32 keys beginning with a letter, followed by letters/digits/underscore/hyphen, up to 64 characters. String values are bounded to 1 KiB; numbers must be finite. Session settings.update() replaces globals reactively without remounting.

ts
export default defineConfig({
  preview: {
    globals: { density: 'compact', accent: 'violet' },
    textDirection: 'ltr',
  },
})

Vue/Nuxt stories read useHistoireGlobals() from histoire/client; Svelte/vanilla use useHistoireGlobalsStore(). Initial appearance still comes from theme.defaultColorScheme. Grid variants share session globals. See SDK settings.

browser 1.0-beta.2+ ​

({ source?: 'auto' | 'headless-shell' | 'chromium' | 'chrome' | 'msedge' } | { source: 'executable', executablePath: string }) & { reuse?: boolean, idleTimeoutMs?: number } - Default: { source: 'auto' }; reuse is disabled.

Choose the browser used by Histoire-owned isolated screenshots and rendered inspection in development, and by screenshots, rendered inspection, and compiled tests in Node artifacts. This setting does not configure the project's Vitest browser runner. Node deployments use target-machine environment variables instead; static books do not run server-side screenshots.

Browser-process reuse is opt-in and applies to development captures. Each capture still gets a fresh browser context. Set idleTimeoutMs only with reuse: true; it must be an integer from 1000 to 300000 milliseconds and defaults to 30000. A configuration refresh retires idle processes; an active capture keeps its process until cleanup completes. Node artifacts keep fresh-process behavior.

ts
export default defineConfig({
  browser: { source: 'chrome', reuse: true, idleTimeoutMs: 30_000 },
})

With Playwright 1.49 or newer, auto checks, in order, for the matching headless shell, matching cached full Chromium, stable Google Chrome, then stable Microsoft Edge. Older supported drivers use their default matching Chromium headless executable before checking Chrome and Edge; those versions do not have separate headless-shell or full-Chromium channel support. It launches each candidate headlessly in a fresh browser context. Bundled Chromium keeps the renderer version aligned with the selected driver. Branded Chrome and Edge can render differently, so pin source when reproducible browser selection matters.

Explicit sources never fall back. headless-shell and full-channel chromium require Playwright 1.49 or newer. Installing full Chromium does not change the default shell choice; use source: 'chromium' to select it. On older drivers, let auto use the default bundled Chromium or upgrade before selecting a separate renderer. A custom executable path is trusted local configuration and resolves relative to the project root:

ts
export default defineConfig({
  browser: { source: 'chrome' },
})
ts
export default defineConfig({
  browser: { source: 'executable', executablePath: './tools/chromium' },
})

The browser driver remains optional. Histoire resolves project playwright first, then project playwright-core, before its own dependency fallback. playwright-core can use existing browsers without installing the full Playwright package, but it still needs a browser automation driver and a compatible installed browser. Use the selected driver's CLI when installing a shell:

sh
pnpm add -D playwright-core
pnpm exec playwright-core install chromium --only-shell

The shell-only install flag requires Playwright 1.49 or newer. Older supported drivers install their default Chromium with pnpm exec playwright install chromium or pnpm exec playwright-core install chromium; those drivers do not provide a separate headless-shell install.

For Playwright 1.49 or newer, install full Chromium without its headless shell when explicitly selecting source: 'chromium':

sh
pnpm exec playwright install chromium --no-shell

Use pnpm exec playwright-core install chromium --no-shell for a core-only driver. Older drivers install their default Chromium without the --no-shell option.

See screenshot setup for setup checks and targeted repairs. Development tests still require the separate Vitest dependencies described in the browser testing guide.

build.target 1.0-beta.2+ ​

'static' | 'node' - Default: 'static'

Select static browser output or a standalone Node deployment. histoire build --target static|node overrides config.

ts
export default defineConfig({
  build: { target: 'node' },
})

build.node.includeSource 1.0-beta.2+ ​

boolean - Default: true

Include registered raw source in private Node MCP content. Set false to omit it from that private store. Catalog/docs remain available. This does not remove the existing book Source panel or source contained in public browser assets.

ts
export default defineConfig({
  build: { target: 'node', node: { includeSource: false } },
})

storyMatch ​

string[] - Default: ['**/*.story.vue']

Glob patterns for story files to include.

ts
export default defineConfig({
  storyMatch: [
    '**/*.story.vue',
  ],
})

Use src/**/*.story.vue to prevent search errors in binary files (e.g., in iOS or Android folders) when using Ionic Capacitor.

storyIgnored ​

string[] - Default: [ '**/node_modules/**', '**/dist/**' ]

Glob patterns to ignore files while searching for story files.

ts
export default defineConfig({
  storyIgnored: [
    '**/node_modules/**',
    '**/dist/**',
  ],
})

tree ​

Object - Default: { file: 'title', order: 'asc' }

How to generate the story tree.

Learn more: Vue 3

Properties:

  • file: 'title' | 'path' | ((file: TreeFile) => string[]): How to get the path of a story.
  • order: 'asc' | ((a: string, b: string) => number): How to sort the stories.
ts
export default defineConfig({
  tree: {
    file: 'title',
    order: 'asc',
  },
})

theme ​

Object

Customize the look of the book.

Learn more

Properties:

  • title: string: Main page title. For example: 'Acme Inc.'
  • description: string: Optional introduction on the home page.
  • fonts: { sans?: string, mono?: string }: CSS font-family overrides for interface and code text.
  • logo: Object: Logo configuration.
    • square: string: Square logo image without text.
    • light: string: Full logo for light theme.
    • dark: string: Full logo for dark theme.
  • favicon: string: Href to the favicon file (not processed by Vite). Put the file in the public directory.
  • colors: Object: Customize the colors. Each color should be an object with shades as keys.
  • logoHref: string: Add a link to the main logo
  • darkClass: string: Class added to the story preview when dark mode is enabled (default is 'dark').
  • defaultColorScheme: 'light' | 'dark' | 'auto': Default color scheme for the app. 'auto' will use the system preference.
  • hideColorSchemeSwitch: boolean: Hides the dark mode button in the toolbar.
  • storeColorScheme: boolean: Enable persistence of the color scheme in the browser's local storage.
ts
import { defaultColors } from 'histoire'

export default defineConfig({
  theme: {
    title: 'Acme Design System',
    favicon: '/my-favicon.svg',
    logo: {
      square: '/src/img/logo-square.svg',
      light: '/src/img/logo-light.svg',
      dark: '/src/img/logo-dark.svg',
    },
    colors: {
      primary: defaultColors.cyan,
    },
    logoHref: 'https://acme.com',
    defaultColorScheme: 'light',
    hideColorSchemeSwitch: true,
    storeColorScheme: false,
  },
})

setupFile ​

string | { browser: string, server: string }

Setup file exporting a default function executed when setting up each story preview.

Import custom CSS files from this file.

Learn more

ts
export default defineConfig({
  setupFile: '/src/histoire-setup.ts',
})

If you need a different version for the NodeJS server (while collecting stories), you can use an object:

ts
export default defineConfig({
  setupFile: {
    browser: '/src/histoire-setup.ts',
    server: '/src/histoire-setup.server.ts',
  },
})

This can be useful if you need to exclude some imported libraries that only works in the browser.

defaultStoryProps ​

Object

Default values for story props.

ts
export default defineConfig({
  defaultStoryProps: {
    icon: 'carbon:assembly-reference',
    iconColor: '#00c5a5',
    layout: {
      type: 'grid',
      width: 300,
    },
    responsiveDisabled: true,
    autoPropsDisabled: true,
  },
})

responsivePresets ​

Array

Predefined responsive sizes for story playgrounds.

Each object in the array is a preset with the following properties:

  • label: string: Label for the preset.
  • width: number: Width of the preset (pixels).
  • height: number: Height of the preset (pixels).

Default values are shown in the example below:

ts
export default defineConfig({
  responsivePresets: [
    {
      label: 'Mobile (Small)',
      width: 320,
      height: 560,
    },
    {
      label: 'Mobile (Medium)',
      width: 360,
      height: 640,
    },
    {
      label: 'Mobile (Large)',
      width: 414,
      height: 896,
    },
    {
      label: 'Tablet',
      width: 768,
      height: 1024,
    },
    {
      label: 'Laptop (Small)',
      width: 1024,
      height: null,
    },
    {
      label: 'Laptop (Large)',
      width: 1366,
      height: null,
    },
    {
      label: 'Desktop',
      width: 1920,
      height: null,
    },
    {
      label: '4K',
      width: 3840,
      height: null,
    },
  ],
})

backgroundPresets ​

Array

Background color of the story preview.

Each object in the array is a preset with the following properties:

  • label: string: Label for the preset.
  • color: string: CSS color, transparent, $checkerboard (light checkerboard), or $checkerboard-dark (dark checkerboard).
  • contrastColor?: string: Contrast color of preset

Default values are shown in the example below:

ts
export default defineConfig({
  backgroundPresets: [
    {
      label: 'Transparent',
      color: 'transparent',
      contrastColor: '#333'
    },
    {
      label: 'White',
      color: '#fff',
      contrastColor: '#333'
    },
    {
      label: 'Light gray',
      color: '#aaa',
      contrastColor: '#eee'
    },
    {
      label: 'Dark gray',
      color: '#333',
      contrastColor: '#ccc'
    },
    {
      label: 'Black',
      color: '#000',
      contrastColor: '#fff'
    },
  ],
})

You can use current contrast color via the css variable --histoire-contrast-color:

css
.my-class {
  color: var(--histoire-contrast-color);
}

Checkerboard backgrounds 1.0-beta.2+ ​

Toolbar always includes both checkerboard choices after configured colors. Configured checkerboard presets can override their labels and text contrast colors. Their palettes remain fixed, and image exports retain transparency.

autoApplyContrastColor ​

boolean - Default: false

Automatically apply the contrast color to the story preview text.

ts
export default defineConfig({
  autoApplyContrastColor: true,
})

markdown ​

(md: MarkdownIt) => MarkdownIt | Promise<MarkdownIt>

Customize the markdown-it renderer.

ts
export default defineConfig({
  markdown: (md) => {
    md.use(SomeMarkdownItPlugin)
  },
})

routerMode ​

'history' | 'hash' - Default: 'history'

Changes the router mode:

  • 'history': HTML 5 history mode with cleaner URLs.
  • 'hash': Use the hashtag hack in the URL to support more servers and static hosting services.
ts
export default defineConfig({
  routerMode: 'hash',
})

vite ​

ViteConfig | ((config: ViteConfig, env: ViteConfigEnv) => void | ViteConfig | Promise<void | ViteConfig>)

Vite config override.

ts
export default defineConfig({
  vite: {
    server: {
      port: 3042,
    },
  },
})

viteIgnorePlugins ​

string[]

List of Vite plugin names to exclude for Histoire.

ts
export default defineConfig({
  viteIgnorePlugins: [
    'vite-plugin-example',
  ],
})

viteNodeTransformMode ​

{ web?, ssr? }

Determine the transform method of modules

viteNodeTransformMode.ssr ​

RegExp[] - Default: [/\.([cm]?[jt]sx?|json)$/]

Use SSR transform pipeline for the specified files.
Vite plugins will receive ssr: true flag when processing those files.

viteNodeTransformMode.web ​

RegExp[] - Default: modules other than those specified in transformMode.ssr

First do a normal transform pipeline (targeting browser), then do a SSR rewrite to run the code in Node.
Vite plugins will receive ssr: false flag when processing those files.

When you use JSX as component models other than React (e.g. Vue JSX or SolidJS), you might want to config as following to make .tsx / .jsx transformed as client-side components:

ts
export default defineConfig({
  viteNodeTransformMode: {
    web: [/\.[jt]sx$/],
  },
})

viteNodeInlineDeps ​

RegExp[]

Transpile dependencies when collecting stories on Node.js.

For example, if you have a dependency that contains ESM code but Node tries to load it in CommonJS context, you might have this kind of error:

.../my-project/node_modules/.pnpm/some-library@1.0.0/lib/esm/index.js:3
export function foo() {
^^^^^^

SyntaxError: Unexpected token 'export'
    at Object.compileFunction (node:vm:352:18)
    at wrapSafe (node:internal/modules/cjs/loader:1031:15)
    ...

We can see that the some-library (made-up name) is misconfigured and Node treats it as CommonJS code (the cjs/loader is being used) but it contains ESM code. Hence the error.

ts
export default defineConfig({
  viteNodeInlineDeps: [
    /some-library/,
  ],
})

collectMaxThreads ​

number - Default: available cpus

Number of maximum threads used to collect stories (both for development and building). Threads count will never go above this limit but might be lower.

ts
export default defineConfig({
  collectMaxThreads: 4,
})

visual 1.0-beta.2+ ​

Enable built-in visual testing with visual: {}. Omit it to leave visual testing disabled.

ts
export default defineConfig({
  visual: {
    baselineDir: '.histoire-visual',
    baselineRef: 'main',
    profiles: [
      { id: 'desktop-light', width: 1024, height: 640, colorScheme: 'light' },
    ],
    threshold: 0.1,
    maxDiffPixelRatio: 0,
    includeAA: false,
  },
})

exclude filters story paths with glob patterns. Profiles can also set deviceScaleFactor, backgroundColor, textDirection, and scalar globals. See visual testing for capture requirements, baseline review, Matrix cases, and CLI/CI workflows.

test.collectTimeout 1.0-beta.2+ ​

number - Default: 120000

Maximum time in milliseconds the headless browser story collection is allowed to take before the run is aborted. Raise it for large projects where collecting every story takes more than two minutes.

ts
export default defineConfig({
  test: {
    collectTimeout: 300_000,
  },
})

test.storyCollectTimeout 1.0-beta.2+ ​

number - Default: 30000

Maximum time in milliseconds a single story is allowed to take to load and mount while collecting in browser. Same budget guards bootstrap readiness, render readiness, and generated Vitest harness mounts. One stuck story fails without stalling whole collection. Raise it for legitimately slow stories.

ts
export default defineConfig({
  test: {
    storyCollectTimeout: 60_000,
  },
})

test.runTimeout 1.0-beta.2+ ​

number - Default: 300000

Maximum time in milliseconds a histoire test run is allowed to take before it is aborted.

ts
export default defineConfig({
  test: {
    runTimeout: 900_000,
  },
})

test.maxWorkers 1.0-beta.2+ ​

number - Default: available CPU parallelism

Maximum number of tests Histoire runs concurrently. Applies to both histoire test and tests run from a book. Browser cases run in separate hidden story documents, so each gets a fresh mount and suite lifecycle.

ts
export default defineConfig({
  test: {
    maxWorkers: 4,
  },
})

theme.description 1.0-beta.2+ ​

string - Default: unset

Optional introduction on the home page in development and static builds.

ts
export default defineConfig({
  theme: { description: 'Components for our product interfaces.' },
})

theme.fonts 1.0-beta.2+ ​

{ sans?: string, mono?: string }

Default: { sans: '"Manrope", system-ui, sans-serif', mono: '"JetBrains Mono", ui-monospace, monospace' }.

CSS font-family values for interface text and code/measurements. Manrope 400–800 and JetBrains Mono 400–500 are bundled locally with Latin and Latin Extended subsets. Overrides select a family; provide any additional font assets in your own CSS. See workbench appearance.

ts
export default defineConfig({
  theme: {
    fonts: {
      sans: 'Inter, system-ui, sans-serif',
      mono: '"IBM Plex Mono", monospace',
    },
  },
})

ui.defaultArrange 1.0-beta.2+ ​

'single' | 'grid' | 'list' - Default: 'grid'

Initial canvas arrangement for stories without explicit layout. Explicit layout.type: 'single' selects Single variant; explicit grid layout selects Grid. A recognized arrange URL query and remembered story/shared view take precedence over story layout. Local defaults take precedence over project defaults for stories without explicit layout. Matrix remains available through the toolbar or URL. See workbench navigation and settings.

ts
export default defineConfig({
  ui: { defaultArrange: 'list' },
})

ui.matrix.numericValues 1.0-beta.2+ ​

  • Type: number[]
  • Default: [0, 1, 2, 5, 10]

Ordered samples used by props matrix for numeric props without a complete runtime domain or explicit story values. Require 2–64 distinct finite numbers; duplicates are removed while preserving order. Invalid lists fail config resolution. Saved app overrides take precedence over story matrix.axes arrays or ranges, which override complete runtime domains and project samples. Resetting an axis restores its current source values. Same defaults apply to dev, static, and embedded books. See props matrix.

ui.frameBudget 1.0-beta.2+ ​

number - Default: 6

Maximum live preview frames per canvas. Selected frames take priority; other frames outside the visible area or budget show placeholders. Matrix reserves one slot for its canonical preview. Use a positive integer. See props matrix.

Existing previews stay mounted during canvas movement. Pointer release updates admission immediately; wheel and zoom input update it after 150 ms without movement. Explicit selection and budget changes apply immediately.

ts
export default defineConfig({
  ui: { frameBudget: 12 },
})

agents.enabled 1.0-beta.2+ ​

boolean - Default: false

Enable local ACP agents in development. User settings can override this project default. Enabling does not download or launch an agent: the first explicit prompt starts its process. Static builds expose no agent connection. See ACP agents.

ts
export default defineConfig({
  agents: { enabled: true },
})

agents.presets 1.0-beta.2+ ​

Array<{ id: string, name: string, command: string, args?: string[], env?: Record<string, string>, cwd?: string, default?: boolean }> - Default: []

Available local launch presets. An empty list uses the built-in Claude Code (claude-agent-acp), Gemini CLI (gemini --acp), and Codex (codex-acp) presets. Executables must already be installed. default selects a preset when no agent is chosen explicitly.

env is reserved for private user-level environment overrides. Environment values are excluded from browser config and rejected by project saves; configure them in local agent settings. See ACP agents and the config codemod.

ts
export default defineConfig({
  agents: {
    presets: [
      { id: 'local-agent', name: 'Local agent', command: 'my-acp-agent', args: ['--stdio'], default: true },
    ],
  },
})

agents.permissions 1.0-beta.2+ ​

{ fileEdits?: 'ask' | 'allow-src' | 'never', terminal?: 'ask' | 'allow' | 'never' }

Default: { fileEdits: 'ask', terminal: 'ask' }.

Policy for agent-owned tool requests. ask shows a permission card; never denies that category. allow-src allows file-edit requests only when all reported paths resolve inside the project's src directory; other paths require a prompt. Terminal allow permits terminal requests. Other tool categories still require a prompt. See ACP permissions.

ts
export default defineConfig({
  agents: {
    permissions: { fileEdits: 'allow-src', terminal: 'ask' },
  },
})

comments.enabled 1.0-beta.2+ ​

boolean - Default: true

Enable canvas comments (threads pinned on the canvas) in development. When disabled, the Comment tool is hidden and new pinned threads are refused; free-form AI threads stay available. Static builds never read or expose threads. See AI threads and canvas comments.

ts
export default defineConfig({
  comments: { enabled: false },
})

comments.file 1.0-beta.2+ deprecated ​

string - Default: '.histoire/comments.json'

Earlier comment storage. Threads are now stored in .histoire/threads.json; on the next development start, comments found at this path are imported as pinned threads and the file is renamed to comments.migrated.json. The path must stay inside the project; symlink escapes are rejected. See local storage.

build.changedSince 1.0-beta.2+ ​

string - Default: unset

Git reference used to identify added or changed stories in a built book's release updates. Unavailable Git metadata or an invalid reference omits that section. Development uses working-tree changes independently of this option. See home and navigation.

ts
export default defineConfig({
  build: { changedSince: 'v1.0.0' },
})

Settings can save supported project defaults through the existing TS/JS config. See Save to project for its explicit preview flow and the config codemod reference for editable paths and computed-value refusals. Screenshots and MCP activity use development services independently of these defaults.

Released under the MIT License.