├── .gitignore ├── .npmrc ├── .storybook ├── main.ts ├── preview-head.html └── preview.ts ├── README.md ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src ├── Counter.tsx ├── index.jsx └── stories │ └── Counter.stories.tsx ├── tsconfig.json └── vite.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | storybook-static 4 | *storybook.log -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | legacy-peer-deps=true 2 | -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from "storybook-solidjs-vite"; 2 | 3 | const config: StorybookConfig = { 4 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], 5 | addons: [ 6 | "@storybook/addon-essentials", 7 | "@storybook/addon-interactions", 8 | ], 9 | framework: { 10 | name: "storybook-solidjs-vite", 11 | options: {}, 12 | }, 13 | docs: { 14 | autodocs: "tag", 15 | }, 16 | }; 17 | export default config; 18 | -------------------------------------------------------------------------------- /.storybook/preview-head.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import type { Preview } from "storybook-solidjs"; 2 | 3 | const preview: Preview = { 4 | parameters: { 5 | controls: { 6 | matchers: { 7 | color: /(background|color)$/i, 8 | date: /Date$/i, 9 | }, 10 | }, 11 | }, 12 | }; 13 | 14 | export default preview; 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Storybook for Solid-js example 2 | 3 | This repo is the example of adoption storybook for solid-js. 4 | 5 | Thanks to guys from this thread: https://github.com/solidjs/solid-docs-next/issues/35 6 | 7 | ## Instructions 8 | 9 | ### Storybook v8 10 | 11 | ~~Everything works out of the box~~. Don't forget to render JSX component in your story file to make HMR work (see `Counter.stories.tsx` file). 12 | 13 | Solid works the best with storybook with @storybook/html-vite configuration. So init your storybook project with HTML type and add the following code: 14 | 15 | preview.js 16 | ```ts 17 | export const decorators = [ 18 | (Story) => { 19 | const solidRoot = document.createElement("div"); 20 | 21 | render(Story, solidRoot); 22 | 23 | return solidRoot; 24 | }, 25 | ]; 26 | ``` 27 | 28 | main.js 29 | ```ts 30 | export const config = { 31 | // some default fields 32 | // ... 33 | viteFinal(config) { 34 | // make solid work 35 | config.plugins?.unshift(solid({ hot: false })); 36 | 37 | return config; 38 | } 39 | }; 40 | ``` 41 | 42 | ### Storybook v7 43 | 44 | You need to change the following files. 45 | 46 | **1. .storybook/main.js** 47 | 48 | ```js 49 | module.exports = { 50 | stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 51 | addons: [ 52 | "@storybook/addon-links", 53 | "@storybook/addon-essentials", 54 | "@storybook/addon-interactions", 55 | ], 56 | framework: { 57 | name: "@storybook/html-vite", 58 | options: {}, 59 | }, 60 | docs: { 61 | autodocs: "tag", 62 | }, 63 | }; 64 | ``` 65 | 66 | **2. .storybook/preview.js** 67 | 68 | If you want HMR works for solid you need to add `/* @refresh reload */` to the beginning of this file however it's not the only change. 69 | See the details below. 70 | 71 | ```js 72 | /* @refresh reload */ 73 | /** 74 | * Don't forget the line above for HMR! 75 | * 76 | * Note: for some reason HMR breaks if you change .stories file, 77 | * however reloading the page fixes this issue 78 | */ 79 | 80 | import { render } from "solid-js/web"; 81 | 82 | export const decorators = [ 83 | (Story) => { 84 | const solidRoot = document.createElement("div"); 85 | 86 | render(Story, solidRoot); 87 | 88 | return solidRoot; 89 | }, 90 | ]; 91 | 92 | /** Autogenerated by Storybook */ 93 | export const parameters = { 94 | actions: { argTypesRegex: "^on[A-Z].*" }, 95 | controls: { 96 | matchers: { 97 | color: /(background|color)$/i, 98 | date: /Date$/, 99 | }, 100 | }, 101 | }; 102 | 103 | ``` 104 | 105 | **That's it!** 106 | 107 | #### HMR 108 | 109 | To make HMR work for your component you need to render it as JSX: 110 | 111 | ```tsx 112 | // Correct! HMR works! 113 | // Let's assume that this is storybook meta object 114 | export default { 115 | // ... 116 | render: (props) => , 117 | // ... 118 | } as Meta>; 119 | ``` 120 | 121 | If you write the code like this, it won't work: 122 | 123 | ```tsx 124 | // Wrong! HMR doesn't work! 125 | // Let's assume that this is storybook meta object 126 | export default { 127 | // ... 128 | render: Counter, 129 | // ... 130 | } as Meta>; 131 | ``` 132 | 133 | Here's an example story for `Counter` component. 134 | 135 | ```tsx 136 | import { Counter, CounterProps } from "../Counter"; 137 | 138 | import type { Meta, StoryObj } from "@storybook/html"; 139 | import type { ComponentProps } from "solid-js"; 140 | 141 | type Story = StoryObj; 142 | 143 | export const Default: Story = { 144 | args: { 145 | initialValue: 12, 146 | theme: "default", 147 | }, 148 | }; 149 | 150 | export default { 151 | title: "Example/Counter", 152 | tags: ["autodocs"], 153 | /** 154 | * Here you need to render JSX for HMR work! 155 | * 156 | * render: Counter won't trigger HMR updates 157 | */ 158 | render: (props) => , 159 | argTypes: { 160 | initialValue: { control: "number" }, 161 | theme: { 162 | options: ["default", "red"], 163 | control: { type: "radio" }, 164 | }, 165 | }, 166 | } as Meta>; 167 | 168 | ``` 169 | 170 | ### Storybook v6 171 | 172 | To see the files for the storybook v6 see [THIS](https://github.com/elite174/storybook-solid-js/tree/16466f35def5ebe4b28603211d8d825d690fbe40). 173 | 174 | #### 1. Initialize storybook in your repo as [html project](https://storybook.js.org/docs/html/get-started/install): 175 | 176 | ``` 177 | npx storybook init --type html 178 | ``` 179 | 180 | #### 2. Update storybook config files in `.storybook` folder as follows: 181 | 182 | *main.js* 183 | 184 | Add `vite-plugin-solid` to storybook config. 185 | ```js 186 | const Solid = require("vite-plugin-solid"); 187 | 188 | module.exports = { 189 | stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"], 190 | addons: [ 191 | "@storybook/addon-links", 192 | "@storybook/addon-essentials", 193 | "@storybook/addon-interactions", 194 | ], 195 | framework: "@storybook/html", 196 | core: { 197 | builder: "@storybook/builder-vite", 198 | }, 199 | features: { 200 | storyStoreV7: true, 201 | }, 202 | 203 | // Add solid plugin here 204 | async viteFinal(config, { configType }) { 205 | config.plugins.unshift(Solid({ hot: false })); 206 | 207 | return config; 208 | }, 209 | }; 210 | ``` 211 | 212 | *preview.js* 213 | 214 | Customize your `preview.js` file as follows. 215 | 216 | ```js 217 | import { render } from "solid-js/web"; 218 | 219 | export const parameters = { 220 | actions: { argTypesRegex: "^on[A-Z].*" }, 221 | controls: { 222 | matchers: { 223 | color: /(background|color)$/i, 224 | date: /Date$/, 225 | }, 226 | }, 227 | }; 228 | 229 | export const decorators = [ 230 | (Story) => { 231 | 232 | const root = document.getElementById("root"); 233 | const solidRoot = document.createElement("div"); 234 | 235 | solidRoot.setAttribute("id", "solid-root"); 236 | root.appendChild(solidRoot); 237 | 238 | render(Story, solidRoot); 239 | 240 | return solidRoot; 241 | // return createRoot(() => Story()); // do not work correctly https://github.com/solidjs/solid/issues/553 242 | }, 243 | ]; 244 | ``` 245 | 246 | ## Comments 247 | 248 | - _.npmrc_ file is necessary because I use npm v8, however storybook doesn't support it, and that's why it requires this file. 249 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Solid App 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-template-solid", 3 | "version": "0.0.0", 4 | "description": "", 5 | "scripts": { 6 | "start": "vite", 7 | "dev": "vite", 8 | "build": "vite build", 9 | "serve": "vite preview", 10 | "storybook": "storybook dev -p 6006", 11 | "build-storybook": "storybook build", 12 | "preview-storybook": "vite ./storybook-static" 13 | }, 14 | "license": "MIT", 15 | "devDependencies": { 16 | "@storybook/addon-essentials": "8.0.5", 17 | "@storybook/addon-interactions": "8.0.5", 18 | "@storybook/addon-links": "8.0.5", 19 | "@storybook/blocks": "8.0.5", 20 | "storybook": "8.0.5", 21 | "storybook-solidjs": "1.0.0-beta.2", 22 | "storybook-solidjs-vite": "1.0.0-beta.2", 23 | "typescript": "5.4.3", 24 | "vite": "5.2.7", 25 | "vite-plugin-solid": "2.10.2" 26 | }, 27 | "dependencies": { 28 | "solid-js": "1.8.16" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Counter.tsx: -------------------------------------------------------------------------------- 1 | import { createSignal } from "solid-js"; 2 | import type { Component } from "solid-js"; 3 | 4 | export interface CounterProps { 5 | initialValue?: number; 6 | theme?: "default" | "red"; 7 | } 8 | 9 | export const Counter: Component = (props) => { 10 | const [count, setCount] = createSignal(props.initialValue ?? 0); 11 | 12 | const getColor = () => (props.theme === "red" ? "red" : "black"); 13 | 14 | return ( 15 |
16 | 22 |
23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /src/index.jsx: -------------------------------------------------------------------------------- 1 | /* @refresh reload */ 2 | import { render } from "solid-js/web"; 3 | 4 | render(() =>
Test app
, document.getElementById("root")); 5 | -------------------------------------------------------------------------------- /src/stories/Counter.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from "@storybook/html"; 2 | import type { ComponentProps } from "solid-js"; 3 | 4 | import { Counter } from "../Counter"; 5 | import type { CounterProps } from "../Counter"; 6 | 7 | type Story = StoryObj; 8 | 9 | export const Default: Story = { 10 | args: { 11 | initialValue: 12, 12 | theme: "default", 13 | }, 14 | }; 15 | 16 | export const Second: Story = { 17 | args: { 18 | initialValue: 35, 19 | }, 20 | }; 21 | 22 | export const Thrid: Story = { 23 | args: { 24 | initialValue: 3225, 25 | }, 26 | }; 27 | 28 | export default { 29 | title: "Example/Counter", 30 | tags: ["autodocs"], 31 | /** 32 | * Here you need to render JSX for HMR work! 33 | * 34 | * render: Counter won't trigger HMR updates 35 | */ 36 | render: (props) => , 37 | argTypes: { 38 | initialValue: { control: "number" }, 39 | theme: { 40 | options: ["default", "red"], 41 | control: { type: "radio" }, 42 | }, 43 | }, 44 | } as Meta>; 45 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": true, 4 | "target": "ESNext", 5 | "module": "ESNext", 6 | "moduleResolution": "node", 7 | "allowSyntheticDefaultImports": true, 8 | "esModuleInterop": true, 9 | "jsx": "preserve", 10 | "jsxImportSource": "solid-js", 11 | "types": ["vite/client"], 12 | "noEmit": true, 13 | "isolatedModules": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import solidPlugin from 'vite-plugin-solid'; 3 | 4 | export default defineConfig({ 5 | plugins: [solidPlugin()], 6 | server: { 7 | port: 3000, 8 | }, 9 | build: { 10 | target: 'esnext', 11 | }, 12 | }); 13 | --------------------------------------------------------------------------------