Use React Router v6 in your stories.
✅ Support for descendant <Routes>
✅ withRouter
decorator parameters now accept { outlet: React.ReactNode }
Install the package
yarn add -D storybook-addon-react-router-v6
Add it to your storybook configuration:
// .storybook/main.js
module.exports = {
addons: ["storybook-addon-react-router-v6"],
};
To add the router to all the stories of a component, simply add it to the decorators
array.
Note that the parameters.reactRouter
property is optional, by default the router will render the component at /
.
import { withRouter } from 'storybook-addon-react-router-v6';
export default {
title: 'User Profile',
component: UserProfile,
decorators: [withRouter],
parameters: {
reactRouter: {
routePath: '/users/:userId',
routeParams: { userId: '42' },
}
}
};
export const Example = () => <UserProfile />;
If you want to change the router config just for one story you can do the following :
import { withRouter } from 'storybook-addon-react-router-v6';
export default {
title: 'User Profile',
component: UserProfile,
decorators: [withRouter],
};
export const Example = () => <UserProfile />;
Example.story = {
parameters: {
reactRouter: {
routePath: '/users/:userId',
routeParams: { userId: '42' },
searchParams: { tab: 'activityLog' },
routeState: { fromPage: 'homePage' },
}
}
};
If you want you can wrap all your stories inside a router by adding the decorator in your preview.js
file.
// preview.js
export const decorators = [withRouter];
// you can also define global defaults parameters
export const parameters = {
reactRouter: {
// ...
}
}
If your component renders an outlet, you can use composition to render your outlet manually, or you can set the outlet
property :
Composition (manually)
const CompositionTemplate = ({outlet, ...args}) => (
<Routes>
<Route path={"/"} element={<Menu {...args} />}>
<Route index element={outlet} />
</Route>
</Routes>
);
export const Composition = CompositionTemplate.bind({});
Composition.args = {
outlet: <OrdersSubMenu />
}
Usage of the outlet
property :
export const MenuWithOrdersContext = () => <Menu />;
MenuWithOrdersContext.story = {
parameters: {
reactRouter: {
routePath: '/account',
outlet: <OrdersSubMenu />,
}
}
};
<Route>
can be nested to handle layouts & outlets.
But components can also render a <Routes>
component with its set of <Route>
, leading to a deep nesting called Descendant Routes
.
In this case, in order for the whole component tree to render in your story with matching params, you will need to set the browserPath
property :
export default {
title: 'Descendant Routes',
component: SettingsPage, // this component renders a <Routes> with several <Route> with path like `billing` or `privacy`
decorators: [withRouter],
};
Default.story = {
parameters: {
reactRouter: {
browserPath: '/billing',
}
}
};
// If you want to render at a specific path, like `/settings`, React Router requires that you add a trailing wildcard
SpecificPath.story = {
parameters: {
reactRouter: {
routePath: '/settings/*',
browserPath: '/settings/billing',
}
}
}
This package aims to support Storybook > 6.4
and React > 16
.
Storybook versions prior 6.4
are very likely to work, I just didn't test them.
If you have an issue with any version, open an issue.
✅ Storybook 6.4
✅ Storybook 6.5
✅ React 16
✅ React 17
✅ React 18
Contributions are welcome.
Before writing any code, file an issue to showcase the bug or the use case for the feature you want to see in this addon.