Skip to main content

Building a Shared Design System Documentation Site

A design system lives in code but is understood through documentation. A well-built documentation site shows components, their variants, design tokens, and usage guidelines. Teams reference it when building features, and new developers learn your design language from it. This article covers setting up Storybook or Docusaurus to document components and tokens, embedding live component previews, and generating API documentation automatically.

Why Design System Documentation Matters

Documentation makes your design system usable. Without it, developers copy components incorrectly, override styles, or build inconsistent UIs. A documentation site serves as the single source of truth: "How do I use a Button?" Answer: visit the docs. "What colors are available?" Answer: see the tokens page. Documentation also enables design-to-code handoff: designers share component specs, developers refer to docs, and everyone uses the same component.

Setting Up Storybook for Component Documentation

Storybook is a tool for developing and documenting components in isolation. Create a new Storybook workspace in your monorepo:

mkdir -p packages/storybook
cd packages/storybook
pnpm init
pnpm add -D storybook @storybook/react @storybook/react-webpack5

Generate Storybook boilerplate:

npx storybook init

This creates a .storybook directory with configuration. Update .storybook/main.ts:

import type { StorybookConfig } from '@storybook/react-webpack5';

const config: StorybookConfig = {
framework: '@storybook/react-webpack5',
stories: [
'../../../packages/ui-library/src/**/*.stories.tsx',
],
addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-interactions',
],
};

export default config;

This tells Storybook to look for component stories in your UI library.

Writing Component Stories

In your component library, create a story file for each component:

// packages/ui-library/src/components/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './Button';

const meta = {
title: 'Components/Button',
component: Button,
argTypes: {
variant: {
control: { type: 'select' },
options: ['primary', 'secondary', 'danger'],
},
size: {
control: { type: 'select' },
options: ['sm', 'md', 'lg'],
},
},
} satisfies Meta<typeof Button>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Primary: Story = {
args: {
variant: 'primary',
children: 'Click Me',
},
};

export const Secondary: Story = {
args: {
variant: 'secondary',
children: 'Secondary Button',
},
};

export const Danger: Story = {
args: {
variant: 'danger',
children: 'Delete',
},
};

export const AllSizes: Story = {
render: () => (
<div style={{ display: 'flex', gap: '1rem' }}>
<Button size="sm" variant="primary">Small</Button>
<Button size="md" variant="primary">Medium</Button>
<Button size="lg" variant="primary">Large</Button>
</div>
),
};

Start Storybook:

pnpm exec storybook dev

Open http://localhost:6006 to see components rendered with interactive controls. Each story represents a component state; you can adjust props with interactive controls.

Documenting Design Tokens

Create a dedicated story or documentation page for tokens. Add a tokens.stories.mdx file:

<!-- packages/ui-library/src/tokens.stories.mdx -->
import { Meta, ColorPalette, ColorItem } from '@storybook/blocks';
import * as Colors from '@myapp/design-tokens';

<Meta title="Design Tokens/Colors" />

# Colors

Our color palette is derived from our brand identity. Use these colors consistently across all interfaces.

<ColorPalette>
<ColorItem
title="Primary"
subtitle="Primary action, links, highlights"
colors={{ Primary: Colors.colors.primary }}
/>
<ColorItem
title="Secondary"
subtitle="Secondary actions, supporting elements"
colors={{ Secondary: Colors.colors.secondary }}
/>
<ColorItem
title="Danger"
subtitle="Destructive actions, errors"
colors={{ Danger: Colors.colors.danger }}
/>
</ColorPalette>

## Usage

Use color tokens in CSS via CSS variables:

```css
.button {
background-color: var(--color-primary);
}

Or in TypeScript:

import { colors } from '@myapp/design-tokens';

const buttonStyle = {
backgroundColor: colors.primary,
};

This generates an interactive colors page in Storybook showing all tokens.

## Building a Documentation Site with Docusaurus

For comprehensive design system documentation, use Docusaurus (what this series uses). Create a documentation workspace:

```bash
mkdir -p packages/docs
cd packages/docs
npx create-docusaurus@latest . --typescript

Configure Docusaurus to import your component library. Edit docusaurus.config.js:

const config = {
title: 'MyCompany Design System',
tagline: 'Shared components and design tokens for all products',
url: 'https://design-system.mycompany.com',
baseUrl: '/',
docLayoutComponent: '@theme/DocLayout',
docs: {
path: 'docs',
include: ['**/*.md', '**/*.mdx'],
},
};

module.exports = config;

Create documentation pages that reference your components. Example:

<!-- packages/docs/docs/components/button.mdx -->
---
sidebar_position: 1
---

import { Button } from '@myapp/ui-library';

# Button Component

Buttons trigger actions or navigate users.

## Basic Usage

<Button variant="primary">Click Me</Button>

## Variants

<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Danger</Button>

## API

- `variant`: 'primary' | 'secondary' | 'danger'
- `size`: 'sm' | 'md' | 'lg'
- `children`: ReactNode

Generating API Documentation

Use TypeDoc to auto-generate API docs from your TypeScript components. Install it:

pnpm add -D typedoc typedoc-plugin-markdown

Create a typedoc.json config:

{
"entryPoints": ["packages/ui-library/src/index.ts"],
"out": "packages/docs/docs/api",
"plugin": ["typedoc-plugin-markdown"],
"format": "markdown",
"hideInPageTOC": true
}

Generate docs:

npx typedoc

This creates markdown files in packages/docs/docs/api/ documenting all exported types and components.

Embedding Live Component Previews

Use docusaurus-theme-live-codeblock to let readers edit and preview components inline:

pnpm add @docusaurus/theme-live-codeblock

In your docs:

```jsx live
<Button variant="primary" size="lg">Try Me</Button>
```

Readers can edit the component code in the browser and see changes instantly.

Publishing Documentation

Add a build script to your docs package.json:

{
"scripts": {
"build": "docusaurus build",
"start": "docusaurus start",
"deploy": "docusaurus build && netlify deploy"
}
}

Build and deploy:

cd packages/docs
pnpm build
pnpm deploy

Your design system documentation is now live at your domain.

Key Takeaways

  • Use Storybook to document components in isolation with interactive controls and multiple variants.
  • Write stories for each component variant to show developers how to use the component.
  • Document design tokens with visual examples (colors, typography, spacing).
  • Use Docusaurus for comprehensive design system documentation with guides, API references, and best practices.
  • Integrate live code previews so readers can experiment with components directly.
  • Auto-generate API documentation from TypeScript interfaces to keep docs in sync with code.

Frequently Asked Questions

Should I use Storybook or Docusaurus for design system docs?

Both. Storybook is great for interactive component development and testing. Docusaurus is great for comprehensive guides, usage examples, and design philosophy. Use Storybook for developers, Docusaurus for a public design system site.

How do I keep documentation in sync with components?

Auto-generate API docs from TypeScript types. Write stories alongside components and commit them to the same package. Use a CI check to ensure all components have stories.

Can I embed Storybook in Docusaurus?

Yes, use the @storybook/addon-docs addon or embed an iframe pointing to your Storybook URL. This keeps documentation centralized in Docusaurus while referencing live Storybook previews.

How do I document component behavior and interactions?

Use Storybook's play function to show interactions:

export const WithInteraction: Story = {
play: async ({ canvasElement }) => {
const button = canvasElement.querySelector('button');
await userEvent.click(button);
},
};

Or write narrative documentation in Docusaurus explaining the component lifecycle.

How often should I update documentation?

Update documentation when you change component APIs, add new components, or update design tokens. Make it part of your PR process: no component changes without doc updates.

Further Reading