├── .npmrc ├── server └── tsconfig.json ├── app.vue ├── .stackblitzrc ├── public ├── favicon.ico └── images │ └── sb-nuxt-logo.jpg ├── tsconfig.json ├── components ├── Pre.vue ├── MyNuxtLink.vue ├── MyComposable.vue ├── MyWelcome.vue ├── MyNuxtImage.vue ├── PinButton.vue ├── MyButton.vue └── PiniaLogo.vue ├── stories ├── types.ts ├── NuxtImage.stories.ts ├── MyNuxtWelcome.stories.ts ├── Pinia.stories.ts ├── RuntimeComposable.stories.ts └── NuxtButton.stories.ts ├── .gitignore ├── composables └── useMyComposable.ts ├── .storybook ├── preview.ts └── main.ts ├── README.md ├── nuxt.config.ts ├── package.json ├── stores └── counter.ts ├── assets └── button.css └── pages └── index.vue /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.nuxt/tsconfig.server.json" 3 | } 4 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /.stackblitzrc: -------------------------------------------------------------------------------- 1 | { 2 | "installDependencies": true, 3 | "startCommand": "storybook dev" 4 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-vue/storybook-nuxt-demo/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /components/Pre.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/images/sb-nuxt-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/storybook-vue/storybook-nuxt-demo/HEAD/public/images/sb-nuxt-logo.jpg -------------------------------------------------------------------------------- /components/MyNuxtLink.vue: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /stories/types.ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | // types.ts 4 | export type Props = { 5 | /** 6 | * description for prop "a" type definiton 7 | * in external file . 8 | * @file ./types.ts 9 | * @default "Hello World" 10 | * */ 11 | 12 | a: string 13 | } -------------------------------------------------------------------------------- /components/MyComposable.vue: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /components/MyWelcome.vue: -------------------------------------------------------------------------------- 1 | 10 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .nuxt 4 | .nitro 5 | .cache 6 | .nuxt-storybook 7 | dist 8 | 9 | # Node dependencies 10 | node_modules 11 | 12 | # Logs 13 | logs 14 | *.log 15 | 16 | # Misc 17 | .DS_Store 18 | .fleet 19 | .idea 20 | 21 | # Local env files 22 | .env 23 | .env.* 24 | !.env.example 25 | -------------------------------------------------------------------------------- /composables/useMyComposable.ts: -------------------------------------------------------------------------------- 1 | export const useMyComposable = () => { 2 | // Because your composable is called in the right place in the lifecycle, 3 | // useRuntimeConfig will also work 4 | const config = useRuntimeConfig() 5 | console.log('useMyComposable config', config) 6 | 7 | return { config } 8 | } 9 | -------------------------------------------------------------------------------- /.storybook/preview.ts: -------------------------------------------------------------------------------- 1 | import type { Preview } from "@storybook/vue3"; 2 | 3 | 4 | const preview: Preview = { 5 | parameters: { 6 | actions: { argTypesRegex: "^on[A-Z].*" }, 7 | controls: { 8 | matchers: { 9 | color: /(background|color)$/i, 10 | date: /Date$/, 11 | }, 12 | }, 13 | }, 14 | }; 15 | 16 | 17 | export default preview; 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Storybook 7 / Nuxt 3 Demo 2 | 3 | ![sb-nuxt (2)](https://github.com/storybook-vue/storybook-nuxt-demo/assets/711292/291456d2-51b2-4bb8-bbe5-f59a9c310ac8) 4 | 5 | 6 | Look at [Storybook Nuxt](https://github.com/storybook-vue/nuxt) & the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. 7 | 8 | 9 | 10 | ## Setup 11 | 12 | ```bash 13 | 14 | pnpm install 15 | pnpm storybook dev 16 | 17 | ``` 18 | 19 | -------------------------------------------------------------------------------- /.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from "@storybook-vue/nuxt"; 2 | const config: StorybookConfig = { 3 | stories: ["../**/*.mdx", "../**/*.stories.@(js|jsx|mjs|ts|tsx)"], 4 | addons: [ 5 | "@storybook/addon-links", 6 | "@storybook/addon-essentials", 7 | "@storybook/addon-interactions", 8 | ], 9 | framework: { 10 | name: "@storybook-vue/nuxt", 11 | options: {}, 12 | }, 13 | docs: { 14 | autodocs: "tag", 15 | }, 16 | }; 17 | 18 | export default config; 19 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | 3 | export default defineNuxtConfig({ 4 | devtools: { enabled: true }, 5 | modules: [ 6 | '@nuxt/image', 7 | '@pinia/nuxt', 8 | ], 9 | runtimeConfig: { 10 | app: { 11 | name: 'Nuxt', 12 | version: '1.0.0', 13 | baseURL:'/' 14 | }, 15 | }, 16 | pinia: { 17 | autoImports: ['defineStore', 'acceptHMRUpdate'], 18 | }, 19 | imports: { 20 | dirs: ['./stores'], 21 | }, 22 | }) 23 | -------------------------------------------------------------------------------- /components/MyNuxtImage.vue: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /stories/NuxtImage.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3'; 2 | 3 | 4 | 5 | import MyComponent from '~/components/MyNuxtImage.vue' 6 | 7 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 8 | 9 | const meta = { 10 | title: 'Example/NuxtImage Story', 11 | component: MyComponent, 12 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 13 | tags: ['autodocs'], 14 | 15 | } satisfies Meta; 16 | 17 | export default meta; 18 | type Story = StoryObj; 19 | /* 20 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 21 | * See https://storybook.js.org/docs/vue/api/csf 22 | * to learn how to use render functions. 23 | */ 24 | 25 | export const NuxtImage : Story = { 26 | args: { }, 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /stories/MyNuxtWelcome.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3'; 2 | 3 | 4 | 5 | import MyNuxtWelcome from '~/components/MyWelcome.vue' 6 | 7 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 8 | 9 | const meta = { 10 | title: 'Example/NuxtWelcome Story', 11 | component: MyNuxtWelcome, 12 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 13 | tags: ['autodocs'], 14 | 15 | } satisfies Meta; 16 | 17 | export default meta; 18 | type Story = StoryObj; 19 | /* 20 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 21 | * See https://storybook.js.org/docs/vue/api/csf 22 | * to learn how to use render functions. 23 | */ 24 | 25 | export const NuxtWelcomeStory : Story = { 26 | args: { }, 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /stories/Pinia.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3'; 2 | 3 | 4 | 5 | import MyComponent from '~/pages/index.vue' 6 | 7 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 8 | 9 | const meta = { 10 | title: 'Example/Pinia Story', 11 | component: MyComponent, 12 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 13 | tags: ['autodocs'], 14 | 15 | } satisfies Meta; 16 | 17 | export default meta; 18 | type Story = StoryObj; 19 | /* 20 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 21 | * See https://storybook.js.org/docs/vue/api/csf 22 | * to learn how to use render functions. 23 | */ 24 | 25 | export const Pinia : Story = { 26 | args: { msg: `Storybook ❤️‍🔥 Nuxt ❤️‍🔥 Pinia` }, 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /stories/RuntimeComposable.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/vue3'; 2 | 3 | 4 | 5 | import MyComponent from '~/components/MyComposable.vue' 6 | 7 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 8 | 9 | const meta = { 10 | title: 'Example/Composables Story', 11 | component: MyComponent, 12 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 13 | tags: ['autodocs'], 14 | 15 | } satisfies Meta; 16 | 17 | export default meta; 18 | type Story = StoryObj; 19 | /* 20 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 21 | * See https://storybook.js.org/docs/vue/api/csf 22 | * to learn how to use render functions. 23 | */ 24 | 25 | export const UserConfigComposable : Story = { 26 | args: { }, 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "scripts": { 5 | "build": "nuxt build", 6 | "dev": "nuxt dev", 7 | "generate": "nuxt generate", 8 | "preview": "nuxt preview", 9 | "postinstall": "nuxt prepare", 10 | "storybook": "storybook dev -p 6006", 11 | "build-storybook": "storybook build" 12 | }, 13 | "devDependencies": { 14 | "@nuxt/devtools": "latest", 15 | "@nuxt/image": "rc", 16 | "@pinia/nuxt": "^0.4.11", 17 | "@storybook-vue/nuxt": "0.2.7", 18 | "@storybook/addon-essentials": "8.0.5", 19 | "@storybook/addon-interactions": "8.0.5", 20 | "@storybook/addon-links": "8.0.5", 21 | "@storybook/blocks": "8.0.5", 22 | "@storybook/types": "8.0.5", 23 | "@types/node": "^18.19.31", 24 | "@vitejs/plugin-vue": "^4.6.2", 25 | "@vue/babel-plugin-jsx": "^1.2.2", 26 | "nuxt": "^3.11.2", 27 | "storybook": "8.0.5", 28 | "typescript": "^5.4.5", 29 | "vite": "^4.5.3", 30 | "vue-tsc": "^1.8.27" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /components/PinButton.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 51 | -------------------------------------------------------------------------------- /stores/counter.ts: -------------------------------------------------------------------------------- 1 | const delay = (t: number) => new Promise((r) => setTimeout(r, t)) 2 | 3 | export const useCounter = defineStore('counter', { 4 | state: () => ({ 5 | n: 2, 6 | incrementedTimes: 0, 7 | decrementedTimes: 0, 8 | numbers: [] as number[], 9 | }), 10 | 11 | getters: { 12 | double: (state) => state.n * 2, 13 | }, 14 | 15 | actions: { 16 | increment(amount = 1) { 17 | this.incrementedTimes++ 18 | this.n += amount 19 | }, 20 | 21 | changeMe() { 22 | console.log('change me to test HMR') 23 | }, 24 | 25 | async fail() { 26 | const n = this.n 27 | await delay(1000) 28 | this.numbers.push(n) 29 | await delay(1000) 30 | if (this.n !== n) { 31 | throw new Error('Someone changed n!') 32 | } 33 | 34 | return n 35 | }, 36 | 37 | async decrementToZero(interval: number = 300) { 38 | if (this.n <= 0) return 39 | 40 | while (this.n > 0) { 41 | this.$patch((state) => { 42 | state.n-- 43 | state.decrementedTimes++ 44 | }) 45 | await delay(interval) 46 | } 47 | }, 48 | }, 49 | }) 50 | 51 | if (import.meta.hot) { 52 | import.meta.hot.accept(acceptHMRUpdate(useCounter, import.meta.hot)) 53 | } 54 | -------------------------------------------------------------------------------- /components/MyButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 58 | -------------------------------------------------------------------------------- /assets/button.css: -------------------------------------------------------------------------------- 1 | .storybook { 2 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 3 | font-size: 14px; 4 | line-height: 1.5; 5 | color: #333; 6 | background-color: white; 7 | } 8 | 9 | .storybook-button { 10 | font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; 11 | font-weight: 700; 12 | border: 0; 13 | border-radius: 3em; 14 | cursor: pointer; 15 | display: inline-block; 16 | line-height: 1; 17 | } 18 | .storybook-button--primary { 19 | color: white; 20 | background-color: #1ea7fd; 21 | } 22 | .storybook-button--secondary { 23 | color: #333; 24 | background-color: transparent; 25 | box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; 26 | } 27 | .storybook-button--small { 28 | font-size: 12px; 29 | padding: 10px 16px; 30 | } 31 | .storybook-button--medium { 32 | font-size: 14px; 33 | padding: 11px 20px; 34 | } 35 | .storybook-button--large { 36 | font-size: 16px; 37 | padding: 12px 24px; 38 | } 39 | .with-border { 40 | border: 1px solid #d8adad; 41 | border-radius: 6px; 42 | padding: 10px; 43 | } 44 | .sb-row { 45 | display: flex; 46 | gap: 20px; 47 | align-items: center; 48 | border: 1px solid #d8adad; 49 | border-radius: 6px; 50 | padding: 10px; 51 | } 52 | 53 | .sb-column { 54 | display: flex; 55 | flex-direction: column; 56 | align-items: flex-start; 57 | justify-content: center; 58 | } 59 | -------------------------------------------------------------------------------- /stories/NuxtButton.stories.ts: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj, VueRenderer } from '@storybook/vue3'; 2 | import type { DecoratorFunction } from "@storybook/types" 3 | 4 | import MyPre from '~/components/Pre.vue' 5 | import MyButton from '~/components/MyButton.vue' 6 | 7 | // More on how to set up stories at: https://storybook.js.org/docs/vue/writing-stories/introduction 8 | const decorators:DecoratorFunction[] = [] 9 | const meta = { 10 | title: 'Example/NuxtLink Story', 11 | component: MyButton, 12 | // This component will have an automatically generated docsPage entry: https://storybook.js.org/docs/vue/writing-docs/autodocs 13 | tags: ['autodocs'], 14 | argTypes: { 15 | backgroundColor: { control: 'color' }, 16 | onClick: { action: 'clicked' }, 17 | }, 18 | args: { primary: false }, // default value 19 | decorators 20 | } satisfies Meta; 21 | 22 | export default meta; 23 | type Story = StoryObj; 24 | /* 25 | *👇 Render functions are a framework specific feature to allow you control on how the component renders. 26 | * See https://storybook.js.org/docs/vue/api/csf 27 | * to learn how to use render functions. 28 | */ 29 | 30 | export const Primary : Story = { 31 | args: { primary: true , label:'Primary' }, 32 | } 33 | 34 | export const MyStory : Story = { 35 | args: { primary: true , label:'My Story' }, 36 | render:(args) => ({ 37 | components: { MyPre }, 38 | template: ` Hii`, 39 | }), 40 | } 41 | 42 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 70 | 71 | 85 | 86 | 97 | -------------------------------------------------------------------------------- /components/PiniaLogo.vue: -------------------------------------------------------------------------------- 1 | 198 | 199 | 254 | 255 | 306 | 307 | 327 | --------------------------------------------------------------------------------