id | title |
---|---|
writing-presets |
Writing Presets |
Storybook presets are grouped collections of babel
, webpack
, and addons
configurations that support specific use cases in Storybook, such as typescript or MDX support.
This doc covers the presets API and how to use the presets mechanism for advanced configuration.
A preset is a set of hooks that is called by Storybook on initialization and can override configurations for babel
, webpack
, addons
, and entries
.
Each configuration has a similar signature, accepting a base configuration object and options, as in this webpack example:
export async function webpack(baseConfig, options) {
// Modify or replace config. Mutating the original reference object can cause unexpected bugs,
// so in this example we replace.
const { module = {} } = baseConfig;
return {
...baseConfig,
module: {
...module,
rules: [
...(module.rules || []),
{
/* some new loader */
},
],
},
};
}
The babel functions babel
, babelDefault
, and managerBabel
all configure babel in different ways.
All functions take a Babel configuration object as their argument and can modify it or return a new object.
For example, Storybook's Vue support uses presets internally and here's how it configures babel:
export function babelDefault(config) {
return {
...config,
presets: [...config.presets, require.resolve('babel-preset-vue')],
};
}
babel
is applied to the preview config, after it has been initialized by storybookbabelDefault
is applied to the preview config before any user presets have been appliedmanagerBabel
is applied to the manager.
The webpack functions webpack
, webpackFinal
, and managerWebpack
configure webpack.
All functions take a webpack4 configuration object.
For example, here is how Storybook automatically adopts create-react-app
's configuration if it's installed, where applyCRAWebpackConfig
is a set of smart heuristics for modifying the input config.
export function webpackFinal(config, { configDir }) {
if (!isReactScriptsInstalled()) {
logger.info('=> Using base config because react-scripts is not installed.');
return config;
}
logger.info('=> Loading create-react-app config.');
return applyCRAWebpackConfig(config, configDir);
}
webpack
is applied to the preview config after it has been initialized by storybookwebpackFinal
is applied to the preview config after all user presets have been appliedwebpackManager
is applied to the manager config
The addon config managerEntries
allows you to add addons to Storybook from within a preset. For addons that require custom webpack/babel configuration, it is easier to install the preset, and it will take care of everything.
For example, the Storysource preset contains the following code:
export function managerEntries(entry = []) {
return [...entry, require.resolve('@storybook/addon-storysource/register')];
}
This is equivalent to registering the addon manually in main.js
:
module.exports = {
managerEntries: ['@storybook/addon-storysource/register'],
};
Entries are the place to register entry points for the preview. For example it could be used to make a basic configure-storybook preset that loads all the *.stories.js
files into SB, instead of forcing people to copy-paste the same thing everywhere.
The presets API is also more powerful than the standard configuration options available in Storybook, so it's also possible to use presets for more advanced configuration without actually publishing a preset yourself.
For example, some users want to configure the webpack for Storybook's UI and addons (issue), but this is not possible using standard webpack configuration (it used to be possible before SB4.1). However, you can achieve this with a private preset.
If it doesn't exist yet, create a file .storybook/main.js
:
module.exports = {
managerWebpack: async (config, options) => {
// update config here
return config;
},
managerBabel: async (config, options) => {
// update config here
return config;
},
webpackFinal: async (config, options) => {
// change webpack config
return config;
},
babel: async (config, options) => {
return config;
},
addons: [],
};
Change your main.js
file to:
const path = require('path');
module.exports = {
presets: [path.resolve('./.storybook/my-preset')],
};
and extract the configuration to a new file ./storybook/my-preset.js
:
module.exports = {
managerWebpack: async (config, options) => {
// update config here
return config;
},
managerBabel: async (config, options) => {
// update config here
return config;
},
webpackFinal: async (config, options) => {
return config;
},
babel: async (config, options) => {
return config;
},
addons: [],
};
Place your my-preset.js
file wherever you want, if you want to share it far and wide you'll want to make it its own package.