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.
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.
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:
{% 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:
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.
options: {
macrosRoot: path.join(PARENT_THEME, 'src/macros'),
}Include macros in templates with:
{% import '@macros/helpers.twig' as helpers %}extraMacrosRoots
Type: string[]
Additional macros roots. Same last-wins merge semantics as extraPatternRoots.
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).
options: {
scssVariablesPath: path.join(ROOT, 'dist/scss-variables.json'),
}Access in 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.
options: {
scssVariablesPath: path.join(ROOT, 'dist/theme-tokens.json'),
sassDataKey: 'theme_vars',
}{{ 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.
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.
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:
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:
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:
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:
// .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;
},