Storybook is the de facto workshop for building UI components in isolation. The under-appreciated part is that storybook build produces a completely static bundle — plain HTML, JS, and CSS — so you don't need a Node server in production. That means you can host it on any static CDN, get automatic SSL, and pay nothing on a free tier.
This guide covers building Storybook for production, fixing the gotchas (base paths, addons, environment values), and deploying it so designers, PMs, and QA can browse your component library from a URL.
Why static hosting is the right model for Storybook
Storybook is a build-time tool. During development you run storybook dev, which spins up a Vite or Webpack dev server. But the published artifact is just files. Hosting those files on a server-side runtime would be wasteful — you'd pay for idle compute serving immutable assets.
Static hosting gives you:
- Global CDN edges so the library loads fast wherever your team is.
- Atomic deploys — each build is a fresh immutable bundle.
- Zero server maintenance — no patching, no scaling.
- Cheap or free — static assets are inexpensive to serve.
Step 1: Build a production bundle
With Storybook 8+, the build command is:
npx storybook buildBy default this outputs to storybook-static/. You can change the directory:
npx storybook build --output-dir dist-storybookAdd it to your package.json so the platform can run it:
{
"scripts": {
"build-storybook": "storybook build --output-dir storybook-static"
}
}Step 2: Handle common build gotchas
Base path / sub-path hosting
If you host Storybook at the root of a domain (e.g. storybook.yourapp.com), no base path config is needed. If you deploy under a sub-path (yourapp.com/storybook), set the base in your builder config. For the Vite builder:
// .storybook/main.js
export default {
framework: '@storybook/react-vite',
viteFinal: async (config) => {
config.base = process.env.STORYBOOK_BASE || '/';
return config;
},
};Environment variables
Storybook only exposes variables prefixed with STORYBOOK_ to the bundle. Anything else won't be available at runtime. Reference them like:
const apiUrl = import.meta.env.STORYBOOK_API_URL;Addon assets and static dirs
If your stories reference images or fonts, register the static directory so they get copied into the build:
// .storybook/main.js
export default {
staticDirs: ['../public'],
};Step 3: Deploy on PandaStack
PandaStack auto-detects most frameworks, but Storybook is a special case because the build command is build-storybook, not the default build. You override it in the deploy settings.
- 1Push your repo to GitHub.
- 2In the [PandaStack dashboard](https://dashboard.pandastack.io), create a new Static Site and connect the repo.
- 3Set the build command and output directory:
| Setting | Value |
|---|---|
| Install command | npm ci (or pnpm install, yarn, bun install) |
| Build command | npm run build-storybook |
| Output directory | storybook-static |
- 1Deploy. PandaStack runs the build inside an ephemeral microVM, publishes the static output to its multi-region CDN, and wires up automatic SSL.
Static builds on PandaStack run in [pandastack.ai](https://pandastack.ai) microVMs, which means your build environment is isolated and reproducible.
Step 4: Lock down access (optional)
A public component library is often fine, but if your design system is proprietary you'll want auth. Options:
- Put it behind your SSO via PandaStack's team/org controls.
- Use a Cloudflare Access policy in front of the custom domain.
- Keep it on an unguessable subdomain for low-stakes internal use.
Step 5: Automate on every push
Because the deploy is git-connected, every push to your default branch rebuilds and republishes Storybook. Pair this with PR previews so reviewers can see component changes before merge. A typical workflow:
git checkout -b feature/new-button
# edit Button.stories.tsx
git commit -am "Add loading state to Button"
git push origin feature/new-button
# open PR -> preview build -> review -> merge -> production rebuildPerformance tips
- Enable test/composition pruning. If you use Storybook composition to combine multiple design systems, only build what you publish.
- Cache
node_modules. Reuse the dependency cache between builds to cut build minutes — Storybook installs are heavy. - Trim addons in CI. Dev-only addons (like the a11y panel, if you don't ship it) can be conditionally excluded to shrink the bundle.
Comparison: where to host a static Storybook
| Option | SSL | Global CDN | Git-push deploy | Free tier |
|---|---|---|---|---|
| PandaStack static site | Automatic | Yes | Yes | 5 static sites + 100GB bw |
| Generic object storage + CDN | Manual setup | Yes | No (needs CI glue) | Pay-as-you-go |
| GitHub Pages | Automatic | Yes | Yes | Public repos |
GitHub Pages is genuinely great for open-source design systems. The tradeoff is that private deployments and custom build pipelines get awkward, and you don't get the same dashboard for logs, custom domains, and rollbacks in one place.
References
- [Storybook: Publish Storybook](https://storybook.js.org/docs/sharing/publish-storybook)
- [Storybook build CLI options](https://storybook.js.org/docs/api/cli-options)
- [Vite: base public path](https://vitejs.dev/config/shared-options.html#base)
- [Storybook environment variables](https://storybook.js.org/docs/configure/environment-variables)
---
Storybook is one of the easiest things to host once you treat it as static files. PandaStack's free tier includes 5 static sites with 100GB of bandwidth and 300 build minutes a month — enough to keep a living component library online for your whole team. Spin one up at [dashboard.pandastack.io](https://dashboard.pandastack.io).