“Storybook is React-first. <Angular|Vue|Svelte> is a second-class citizen.”
One area where this complaint rings true is reactivity.
Storybook is based on the core construct of a “render function” which is a JS function that receives “args” and produces a renderable element. Each framework comes with a default render function that fills in your component with args. However, as soon as you want to render your component in a custom way (e.g. to show how different components compose together), it exposes the React-ness of the API.
Consider a story that wraps your component in a div
. In React, your custom render function can either be a React component or a function that returns a React component:
export const MyStory = {
render: (args, context) => <div><MyComponent {...args} /></div>,
}
This is a natural programming model. However, most non-React renderers have a signal-based reactivity model, and it is up to the render functions have to bridge the gap. For example, here’s the equivalent story in Vue:
export const MyStory = {
render: (args, context) => ({
components: { MyComponent },
setup() { return { args }; },
template: `<div><MyComponent v-bind="args"></MyComponent></div>`,
}),
}
This code is verbose, non-intuitive for Vue users, and brings up lots of questions about when the function is called, how it gets updated, and so forth.
Furthermore, it doesn’t explicitly handle slots or other non-prop data that can come from args
or other data that’s passed to the render function via the StoryContext
, which is the second argument to the render function.
Each renderer already has its own rich models, syntaxes, and idioms for how to write components, so rather than creating and documenting patterns like the above, why can’t we directly support renderer-specific component APIs to write stories?
This proposal generalizes to all renderers (Angular, Vue, Svelte, etc.). To focus the discussion, it focuses on Vue specifically. However, this is a larger problem. If we can agree on this path forward for Storybook and Component Story Format (CSF) for Vue, we should have RFCs for how to apply the pattern to the other renderers:
To solve this problem, we propose: