Only supports for Storybook 7
Install the package
yarn add -D storybook-addon-remix-react-router
Add it to your storybook configuration:
// .storybook/main.ts
module.exports = {
addons: ['storybook-addon-remix-react-router'],
};
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-remix-react-router';
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-remix-react-router';
export default {
title: 'User Profile',
component: UserProfile,
decorators: [withRouter],
};
export const Example = () => <UserProfile />;
Example.story = {
parameters: {
reactRouter: {
routePath: '/users/:userId',
routeParams: { userId: '42' },
routeHandle: 'Profile',
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 you use the data routers of react-router 6.4+
, such as <BrowserRouter />
, you can use the following properties :
export const Example = () => <Articles />;
Example.story = {
parameters: {
reactRouter: {
routePath: '/articles',
loader: fetchArticlesFunction,
action: articlesActionFunction,
errorElement: <FancyErrorComponent />,
},
},
};
If your component renders an outlet, you can set the outlet
property :
export const Example = () => <Articles />;
Example.story = {
parameters: {
reactRouter: {
routePath: '/articles',
outlet: {
element: <Article />,
handle: 'Article',
path: ':articleId',
loader: yourLoaderFunction,
action: yourActionFunction,
errorElement: <FancyErrorComponent />,
},
// Or simply
outlet: <MostRecentArticles />,
},
},
};
<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',
},
},
};
Navigation events, loader and actions are logged, for you to better understand the lifecycle of your components.
Every parameter is optional. In most cases they follow the same type used by Route Router itself, sometimes they offer a sugar syntax.
Parameter | Type | Description |
---|---|---|
routePath | string |
i.e: /users/:userId |
routeParams | Record<string, string> |
i.e: { userId: "777" } |
routeState | any |
Available through useLocation() |
routeHandle | any |
Available through useMatches() |
searchParams | string[][] | Record<string, string> | string | URLSearchParams |
Location query string useSearchParams() |
outlet | React.ReactNode | OutletProps |
Outlet rendered by the route. See type OutletProps below. |
browserPath | string |
Useful when you have descendant routes. |
loader | LoaderFunction |
|
action | ActionFunction |
|
errorElement | React.ReactNode | null |
|
hydrationData | HydrationState |
|
shouldRevalidate | ShouldRevalidateFunction |
|
routeId | string |
Available through useMatches() |
type OutletProps = {
element: React.ReactNode;
path?: string;
handle?: unknown;
loader?: LoaderFunction;
action?: ActionFunction;
errorElement?: React.ReactNode | null;
};
✅ Storybook 7.0
✅ React 16
✅ React 17
✅ React 18
If you face an issue with any version, open an issue.
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.