#
Setup Storybook
Squide provides helpers to set up a Storybook story file for rendering components using Squide. This guide assumes that you already have a working Storybook environment.
#
Install the packages
To set up Storybook, first, open a terminal at the root of the Storybook application and install the following packages:
pnpm add msw msw-storybook-addon
#
Register the MSW addon
Then, update the standard .storybook/preview.tsx file and register the Mock Service Worker (MSW) addon:
import { initialize as initializeMsw, mswLoader } from "msw-storybook-addon";
import { Suspense } from "react";
import type { Preview } from "storybook-react-rsbuild";
initializeMsw({
onUnhandledRequest: "bypass"
});
const preview: Preview = {
decorators: [
Story => {
return (
<Suspense fallback="UNHANDLED SUSPENSE BOUNDARY, should be handled in your components...">
<Story />
</Suspense>
);
}
],
loaders: [mswLoader]
};
export default preview;
Then, update the standard .storybook/main.ts file and set the staticDirs option to ["public"]:
import { createRequire } from "node:module";
import { dirname, join } from "node:path";
import type { StorybookConfig } from "storybook-react-rsbuild";
const require = createRequire(import.meta.url);
const storybookConfig: StorybookConfig = {
framework: getAbsolutePath("storybook-react-rsbuild"),
staticDirs: ["public"]
};
export default storybookConfig;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function getAbsolutePath(value: string): any {
return dirname(require.resolve(join(value, "package.json")));
}
#
Initialize MSW
Finally, ensure that MSW is correctly initialized. Confirm that a mockServiceWorker.js file exists in the /public folder. If it's missing, open a terminal at the root of the Storybook application and execute the following command:
pnpm dlx msw init
#
Configure a project
#
Install the packages
To set up a project, first, open a terminal at the project root and install the following packages:
pnpm add @squide/firefly-rsbuild-storybook
#
Create a runtime instance
Then, update the story files to create a runtime instance using the initializeFireflyForStorybook function:
import { initializeFireflyForStorybook } from "@squide/firefly-rsbuild-storybook";
import type { Decorator, Meta, StoryObj } from "storybook-react-rsbuild";
import { Page } from "./Page.tsx";
import { registerModule } from "./registerModule.tsx";
const runtime = await initializeFireflyForStorybook({
localModules: [registerModule]
});
const meta = {
title: "Page",
component: Page
} satisfies Meta<typeof Page>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default = {} satisfies Story;
If you encounter a RuntimeError: factory is undefined when starting Storybook, try disabling lazyCompilation both in your project's Rsbuild configuration file and in the Storybook main.ts file under the builder options. This happens because lazyCompilation does not work correctly with async functions.
#
Setup a decorator
Then, set up a decorator using the withFireflyDecorator function:
import { initializeFireflyForStorybook, withFireflyDecorator } from "@squide/firefly-rsbuild-storybook";
import type { Decorator, Meta, StoryObj } from "storybook-react-rsbuild";
import { Page } from "./Page.tsx";
import { registerModule } from "./registerModule.tsx";
const runtime = await initializeFireflyForStorybook({
localModules: [registerModule]
});
const meta = {
title: "Page",
component: Page,
decorators: [
withFireflyDecorator(runtime)
]
} satisfies Meta<typeof Page>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default = {} satisfies Story;
Or embed the FireflyDecorator component in an existing decorator:
import { initializeFireflyForStorybook, FireflyDecorator } from "@squide/firefly-rsbuild-storybook";
import type { Decorator, Meta, StoryObj } from "storybook-react-rsbuild";
import { Page } from "./Page.tsx";
import { registerModule } from "./registerModule.tsx";
const runtime = await initializeFireflyForStorybook({
localModules: [registerModule]
});
const meta = {
title: "Page",
component: Page,
decorators: [
story => (
<FireflyDecorator runtime={runtime}>
{story()}
</FireflyDecorator>
)
]
} satisfies Meta<typeof Page>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default = {} satisfies Story;
#
Setup MSW
Next, forward the MSW request handlers registered by the modules to the Storybook addon:
import { initializeFireflyForStorybook, FireflyDecorator } from "@squide/firefly-rsbuild-storybook";
import type { Decorator, Meta, StoryObj } from "storybook-react-rsbuild";
import { Page } from "./Page.tsx";
import { registerModule } from "./registerModule.tsx";
const runtime = await initializeFireflyForStorybook({
localModules: [registerModule]
});
const meta = {
title: "Page",
component: Page,
decorators: [
story => (
<FireflyDecorator runtime={runtime}>
{story()}
</FireflyDecorator>
)
],
parameters: {
msw: {
handlers: runtime.requestHandlers
}
}
} satisfies Meta<typeof Page>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default = {} satisfies Story;
#
Setup environment variables
Then, if the components included in the stories rely on environment variables, mock the environment variables using the initializeFireflyForStorybook function:
import { initializeFireflyForStorybook, withFireflyDecorator } from "@squide/firefly-rsbuild-storybook";
import type { Decorator, Meta, StoryObj } from "storybook-react-rsbuild";
import { Page } from "./Page.tsx";
import { registerModule } from "./registerModule.tsx";
const runtime = await initializeFireflyForStorybook({
localModules: [registerModule],
environmentVariables: {
apiBaseUrl: "https://my-api.com"
}
});
const meta = {
title: "Page",
component: Page,
decorators: [
withFireflyDecorator(runtime)
]
} satisfies Meta<typeof Page>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default = {} satisfies Story;
#
Setup feature flags
Finally, if the components included in the stories rely on feature flags, mock the feature flags using the initializeFireflyForStorybook function:
import { initializeFireflyForStorybook, withFireflyDecorator } from "@squide/firefly-rsbuild-storybook";
import type { Decorator, Meta, StoryObj } from "storybook-react-rsbuild";
import { Page } from "./Page.tsx";
import { registerModule } from "./registerModule.tsx";
const runtime = await initializeFireflyForStorybook({
localModules: [registerModule],
featureFlags: {
"render-summary": true
}
});
const meta = {
title: "Page",
component: Page,
decorators: [
withFireflyDecorator(runtime)
]
} satisfies Meta<typeof Page>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default = {} satisfies Story;
To test different variations of a feature flag, use the withFeatureFlagsOverrideDecorator hook:
import { initializeFireflyForStorybook, withFireflyDecorator, withFeatureFlagsOverrideDecorator } from "@squide/firefly-rsbuild-storybook";
import type { Decorator, Meta, StoryObj } from "storybook-react-rsbuild";
import { Page } from "./Page.tsx";
import { registerModule } from "./registerModule.tsx";
const runtime = await initializeFireflyForStorybook({
localModules: [registerModule],
featureFlags: {
"render-summary": true
}
});
const meta = {
title: "Page",
component: Page,
decorators: [
withFireflyDecorator(runtime)
]
} satisfies Meta<typeof Page>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default = {
decorators: [
withFeatureFlagsOverrideDecorator({ "render-summary": false })
]
} satisfies Story;
#
Try it 🚀
Start the Storybook application using the development script. Then open a story that uses Squide components. It should render without errors. Make a change to the story and confirm that it re-renders correctly.
#
Troubleshoot issues
If you are experiencing issues with this guide:
- Set the initializeTelemetry function
verboseoption totrue. - Open the DevTools console and look for any relevant logs or errors.
- Refer to a working example on GitHub.
- Refer to the troubleshooting page.