Storybook
@praxisjs/storybook — Storybook framework adapter for PraxisJS. Write stories with full reactivity, HMR, and source preview.
Storybook
@praxisjs/storybook is a Storybook framework adapter that mounts PraxisJS components into the canvas with full reactivity, HMR, and source preview. Requires Storybook ≥ 8 and @storybook/builder-vite.
npm install -D @praxisjs/storybookpnpm add -D @praxisjs/storybookyarn add -D @praxisjs/storybookbun add -d @praxisjs/storybookSetup
.storybook/main.ts
import type { StorybookConfig } from '@praxisjs/storybook'
const config: StorybookConfig = {
stories: ['../src/**/*.stories.tsx'],
addons: ['@storybook/addon-essentials'],
framework: {
name: '@praxisjs/storybook',
},
}
export default configThe adapter automatically wires up @praxisjs/vite-plugin with HMR enabled. You do not need to add the Vite plugin separately in .storybook/main.ts.
.storybook/preview.ts
No extra setup needed. The preset registers @praxisjs/storybook/preview, which provides renderToCanvas and a default render, so a preview.ts only holds your own parameters and decorators.
A story without a render function mounts the meta's component with the story's args as props:
const meta: Meta = { title: 'Components/Badge', component: Badge }
export default meta
export const Primary: StoryObj = { args: { label: 'New' } } // <Badge label="New" />A meta with neither component nor a story render fails with an error that names the story's title.
Writing stories
Import Meta and StoryObj from @praxisjs/storybook for full type safety:
import { StatefulComponent } from '@praxisjs/core'
import { Component, State } from '@praxisjs/decorators'
import type { Meta, StoryObj } from '@praxisjs/storybook'
@Component()
class Counter extends StatefulComponent {
@State() count = 0
render() {
return (
<div>
<p>{() => this.count}</p>
<button onClick={() => { this.count++ }}>+</button>
</div>
)
}
}
const meta: Meta = {
title: 'Components/Counter',
tags: ['autodocs'],
}
export default meta
type Story = StoryObj
export const Default: Story = {
render: () => <Counter />,
}
export const StartAt10: Story = {
render: () => <Counter />,
// Pass props via render args:
// render: (args) => <Counter initialCount={args.count} />,
}Composition
Storybook Composition lets you view stories from other published Storybooks — including ones built with other frameworks — inside your PraxisJS Storybook's sidebar. Add refs to .storybook/main.ts:
import type { StorybookConfig } from '@praxisjs/storybook'
const config: StorybookConfig = {
stories: ['../src/**/*.stories.tsx'],
framework: {
name: '@praxisjs/storybook',
},
refs: {
'design-system': {
title: 'Design System',
url: 'https://your-design-system.example.com',
expanded: false,
sourceUrl: 'https://github.com/your-org/design-system',
},
},
}
export default configEach key becomes an entry in the sidebar. expanded controls whether the ref's stories are expanded by default (defaults to true), and sourceUrl is an optional link back to the ref's repository. Use { disable: true } instead of { title, url } to turn off a ref that a shared preset registered for you.
Type reference
| Export | Description |
|---|---|
Meta | Type for the default story export (ComponentAnnotations) |
StoryObj | Type for named story exports (StoryAnnotations) |
StorybookConfig | Type for .storybook/main.ts |
StorybookRefs | Type for the refs field — see Composition |
PraxisRenderer | Low-level renderer type (rarely used directly) |
How it works
renderToCanvasmounts the story with@praxisjs/runtime'srender(). PraxisJS has no re-render pass, so every render of a canvas (new args from the controls,forceRemount, HMR) first disposes what that canvas holds: its effects stop andonUnmountruns. Each canvas is tracked on its own, so stories rendered side by side on a docs page don't affect each other. It returns a teardown that Storybook calls when the story leaves the canvas.viteFinalmerges@praxisjs/vite-plugin(with HMR) into Vite's config and adds a plugin that embeds the raw source of each*.stories.*/*.story.*file for the Code panel.previewAnnotationsregisters@praxisjs/storybook/preview(renderToCanvasplus the defaultrender).managerEntriesregisters the custom manager panel.