Back to integrations
vuetify

Integrate Vuetify with Storybook

Vuetify is a Vue-based component library designed with Google's Material Design spec.
Prerequisites

This recipe assumes that you have a Vue 3 app using Vuetify v3 and have just set up Storybook 7.0 or higher using the getting started guide. Don’t have this? Follow Vuetify’s installation instructions then run:

# Add Storybook:
npx storybook@latest init

1. Register Vuetify in Storybook

To get started, you'll need to add Vuetify’s fontloader and plugin to your Storybook configuration. To do this, add the following to your .storybook/preview.js file:

// .storybook/preview.js
 
import { setup } from '@storybook/vue3';
import { registerPlugins } from '../src/plugins';
 
setup((app) => {
  // Registers your app's plugins into Storybook
  registerPlugins(app);
});

Here, registerPlugins loads Vuetify’s fonts and registers all of its components with Storybook’s Vue app.

2. Create a story wrapper component

Next, you will need to wrap your stories in Vuetify's v-app component in order to use some of its larger layout components like v-app-bar. To do this, create a component in .storybook/ called StoryWrapper.vue

<!-- .storybook/StoryWrapper.vue -->
 
<template>
  <v-app>
    <v-main>
      <slot name="story"></slot>
    </v-main>
  </v-app>
</template>
 
<script></script>

3. Create a withVuetifyTheme decorator

Now create a Storybook decorator to wrap your stories in your StoryWrapper component.

Below I created a new file in .storybook called withVuetifyTheme.decorator.js.

// .storybook/withVeutifyTheme.decorator.js
 
import { h } from 'vue';
import StoryWrapper from './StoryWrapper.vue';
 
export const withVuetifyTheme = (storyFn, context) => {
  const story = storyFn();
 
  return () => {
    return h(
      StoryWrapper,
      {}, // Props for StoryWrapper
      {
        // Puts your story into StoryWrapper's "story" slot with your story args
        story: () => h(story, { ...context.args }),
      }
    );
  };
};

Now, give this decorator to Storybook in your preview.js file.

// .storybook/preview.js
 
import { setup } from '@storybook/vue3';
import { registerPlugins } from '../src/plugins';
import { withVuetifyTheme } from './withVuetifyTheme.decorator';
 
setup((app) => {
  registerPlugins(app);
});
 
/* snipped for brevity */
 
export const decorators = [withVuetifyTheme];

4. Add a theme switcher tool

Vuetify comes out of the box with a light and dark theme that you can override or add to. To get the most out of your stories, you should have a way to toggle between all of your themes.

To add our switcher, declare a global type named theme in .storybook/preview.js and give it a list of supported themes to choose from.

// .storybook/preview.js
 
export const globalTypes = {
  theme: {
    name: 'Theme',
    description: 'Global theme for components',
    toolbar: {
      icon: 'paintbrush',
      // Array of plain string values or MenuItem shape
      items: [
        { value: 'light', title: 'Light', left: '🌞' },
        { value: 'dark', title: 'Dark', left: '🌛' },
      ],
      // Change title based on selected value
      dynamicTitle: true,
    },
  },
};

This code will create a new toolbar menu to select your desired theme for your stories.

5. Add a theme provider

There needs to be a way to tell Vuetify to use the theme selected in the toolbar. This can be done by updating our StoryWrapper component and withVuetifyTheme decorator

Firstly, give StoryWrapper a themeName prop that it can give to v-app

<template>
  <v-app :theme="themeName">
    <v-main>
      <slot name="story"></slot>
    </v-main>
  </v-app>
</template>
 
<script>
export default {
  props: {
    themeName: String,
  },
};
</script>

Now pass our global theme variable to our StoryWrapper component as a prop with our decorator

// .storybook/withVeutifyTheme.decorator.js
 
import { h } from 'vue';
import StoryWrapper from './StoryWrapper.vue';
 
export const DEFAULT_THEME = 'light';
 
export const withVuetifyTheme = (storyFn, context) => {
  // Pull our global theme variable, fallback to DEFAULT_THEME
  const themeName = context.globals.theme || DEFAULT_THEME;
  const story = storyFn();
 
  return () => {
    return h(
      StoryWrapper,
      // give themeName to StoryWrapper as a prop
      { themeName },
      {
        story: () => h(story, { ...context.args }),
      }
    );
  };
};

Get involved

Now you're ready to use Vuetify with Storybook. 🎉 Check out the example repo for a quick start.

If you use Vuetify at work, we'd love your help making an addon that automatically applies the configuration above. Join the maintainers in Discord to get involved, or jump into addon docs.

Tags
Contributors
  • ShaunEvening
    ShaunEvening