This is a framework to allow using Storybook with Qwik.
- This has only been tested with brand new Qwik applications and component libraries.
- Story is completely reloaded when component is changed (no hot module replacement)
- There is no automation yet for easily scaffolding storybook in a Qwik project.
- Stories are run in dev mode - no SSR, or serialization happens
In an existing Qwik project, run npx storybook@next init
(Storybook 7 is required)
See the Storybook Docs for the best documentation on getting started with Storybook.
A basic story will look like this:
import Header, { HeaderProps } from "./header";
import { StoryObj } from "storybook-framework-qwik";
export default {
title: "Header",
component: Header, // component value may be a `component$`, or a "Lite component" (function component)
} as Meta<HeaderProps>;
export const Default: StoryObj<HeaderProps> = {};
You can include a custom renderer for each Meta, or for each story. However, the renderer cannot use Qwik features (such as useStore
). If you need Qwik features in the story, make a wrapper for the component. This is useful when a component expects its props to be reactive state (such as useStore
or useSignal
)
import {
ReactiveComponent,
ReactiveComponentProps,
} from "./reactive-component";
import { Meta, StoryObj } from "storybook-framework-qwik";
import { component$, useStore } from "@builder.io/qwik";
const ReactiveComponentWrapper = component$<ReactiveComponentProps>((args) => {
const state = useStore(args.state);
return <ReactiveComponent state={state} />;
});
export default {
title: "Reactive Component",
component: ReactiveComponent,
render: (args) => <ReactiveComponentWrapper state={args.state} />,
args: { state: { number: 1 } },
} as Meta<ReactiveComponentProps>;
export const Default: StoryObj<ReactiveComponentProps> = {};
To make a story decorator, create a function that returns JSX, including the StoryFn passed to the decorator as a parameter
import { JSXNode } from "@builder.io/qwik";
import { MyComponent } from "./my-component";
import { Decorator } from "storybook-framework-qwik";
export const myDecorator: Decorator = (Story) =>
// Cast is needed because something is out of sync with the JSXNode generated in tsx files and the type expected by Decorator
(<MyComponent>{Story()}</MyComponent>) as JSXNode;
If using QwikCity features in your components, you may want to import the qwikCityDecorator, which wraps stories in MockQwikCityProvider
. This can be added to all stories by exporting the decorator in a decorators array in .storybook/preview.ts
:
import { qwikCityDecorator } from "storybook-framework-qwik/qwik-city-decorator";
export const decorators = [qwikCityDecorator];
You can also add the decorator to individual stories or story files.
Because this framework is shipped only as an ESM module, this may require that you add "type": "module"
to your package.json
(or create a package.json inside your .storybook folder to only make this setting apply to storybook).
There is a simple example Storybook using the latest version of this package here
Many parts of this package are based on code that I got from this PR, which got some ideas from this discussion.