Skip to content

Configuration

All options are passed via the options key in your .storybook/main.ts addon entry.

Full options reference

patternsRoot (required)

Type: string

Absolute path to your primary patterns root. The addon walks each namespace subdirectory under this root and registers template files matching templateGlob.

ts
options: {
  patternsRoot: path.join(ROOT, 'src/patterns'),
}

namespaces (required)

Type: Record<string, string>

Maps namespace names (used as @namespace prefixes in Twig) to subdirectory names under patternsRoot.

ts
options: {
  namespaces: {
    atoms:     'atoms',
    molecules: 'molecules',
    organisms: 'organisms',
  },
}

Subdirectory names can be anything — numbered prefixes like 01-atoms are supported but not required.

A template at src/patterns/atoms/button/_button.tpl.twig is registered as @atoms/button/_button.tpl.twig and can be included with:

twig
{% include '@atoms/button/_button.tpl.twig' with { button_text: 'Click me' } %}

extraPatternRoots

Type: string[]

Additional pattern roots to merge over patternsRoot. Templates at the same relative namespace path in a later root override those from earlier roots — last entry wins.

Use this for parent and child theme overrides:

ts
options: {
  patternsRoot:      path.join(PARENT_THEME, 'src/patterns'),
  extraPatternRoots: [
    path.join(ROOT, 'src/patterns'), // child overrides parent
  ],
}

See Pattern Override Cascade for the full setup.

macrosRoot

Type: string

Absolute path to the primary macros root. All .twig files found here are registered under the @macros/ namespace prefix.

ts
options: {
  macrosRoot: path.join(PARENT_THEME, 'src/macros'),
}

Include macros in templates with:

twig
{% import '@macros/helpers.twig' as helpers %}

extraMacrosRoots

Type: string[]

Additional macros roots. Same last-wins merge semantics as extraPatternRoots.

ts
options: {
  macrosRoot:       path.join(PARENT_THEME, 'src/macros'),
  extraMacrosRoots: [
    path.join(ROOT, 'src/macros'),
  ],
}

scssVariablesPath

Type: string

Absolute path to a JSON file containing compiled SCSS variable values (typically generated by a webpack build step). The file is parsed at build time and injected into every story's render context under the key set by sassDataKey (default: sass_data).

ts
options: {
  scssVariablesPath: path.join(ROOT, 'dist/scss-variables.json'),
}

Access in Twig:

twig
{{ sass_data.scssColors.primary }}

If the file doesn't exist at build time, the injected value is an empty object — no error is thrown.

sassDataKey

Type: string Default: 'sass_data'

The variable name under which the parsed scssVariablesPath JSON is injected into every story's render args. Change this if your Twig templates use a different variable name.

ts
options: {
  scssVariablesPath: path.join(ROOT, 'dist/theme-tokens.json'),
  sassDataKey: 'theme_vars',
}
twig
{{ theme_vars.colors.primary }}

sassAdditionalData

Type: string

An additionalData string prepended to every .scss file processed by the Vite CSS pipeline. Use this to forward the same @use barrel your webpack build uses so that mixins, functions, and variables are available in pattern SCSS files.

ts
options: {
  sassAdditionalData: `@use "${path.join(ROOT, 'src/patterns/_variables.scss').replace(/\\/g, '/')}" as *;`,
}

Windows paths

Always call .replace(/\\/g, '/') on Windows absolute paths before embedding them in a @use string — backslashes are invalid in Sass import paths.

templateGlob

Type: string Default: '_*.tpl.twig'

Glob pattern controlling which template files are registered in the twig.js registry. Only the filename segment is matched — the recursive directory walk is handled internally.

ts
options: {
  // Default: only files starting with _ and ending with .tpl.twig
  templateGlob: '_*.tpl.twig',

  // Alternative: any .html.twig file
  templateGlob: '*.html.twig',

  // Alternative: any .twig file
  templateGlob: '*.twig',
}

Supports * as a wildcard. Other regex metacharacters are escaped literally.

globalContext

Type: Record<string, unknown> | false

The WordPress/Timber context (site, options, menus, widgets) is injected into every story by default with empty values. You only need to configure globalContext if you want to customize the values or disable injection entirely.

Configure it in main.ts to provide real values for your project:

ts
options: {
  globalContext: {
    site: { name: 'My Theme', url: 'http://localhost', description: '', language: 'en-US' },
    options: {},
    menus: {},
    widgets: {},
  },
}

Override any field at the story level via args — story args always win:

ts
export const FrenchLocale: StoryObj = {
  args: {
    site: { name: 'Mon Thème', url: 'http://localhost', language: 'fr-FR', description: '' },
  },
};

To disable the context mock for a non-WordPress project:

ts
options: {
  globalContext: false,
}

Vite fs.allow for symlinked repos

If your addon or a theme dependency is installed as a symlinked local package (e.g. file:../ in package.json), Vite's server.fs.strict will block requests for files outside the project root. Add the real paths to server.fs.allow in your viteFinal:

ts
// .storybook/main.ts
async viteFinal(config) {
  config.server = config.server ?? {};
  config.server.fs = config.server.fs ?? {};
  config.server.fs.allow = [
    ...(config.server.fs.allow ?? []),
    ROOT,
    PARENT_THEME,
    // If the addon itself is a symlink, add its real path:
    fs.realpathSync(path.join(ROOT, 'node_modules/@pdrittenhouse/storybook-addon-twig-wordpress')),
  ];
  return config;
},

Released under the MIT License.