Storybook Addon for Vue Components in MDX
storybook-addon-vue-mdx facilitates the integration and rendering of Vue 3 components directly within MDX documentation files in Storybook. The current stable version is 3.0.0, which targets Storybook v10. This addon's release cadence appears to be closely tied to major Storybook releases, requiring updates to support newer Storybook versions. Its primary differentiator is enabling Vue component usage in an MDX context, leveraging `veaury` to bridge the Vue/React rendering environments within Storybook's predominantly React-based MDX compiler. It supports customization of the Vue app context via Storybook globals, allowing for the registration of Vue plugins or other app-level configurations. While powerful, it has limitations such as requiring local imports of components and a known bug affecting initial local page loads for MDX files with Vue components.
Common errors
-
Cannot read properties of undefined (reading 'beforeVueAppMount')
cause The `vueMdx` global object or `beforeVueAppMount` property is not correctly defined in `.storybook/preview.js`.fixEnsure that the `globals` export in `.storybook/preview.js` includes `vueMdx` with the `beforeVueAppMount` function, if you intend to customize the Vue app context. Example: ```javascript // .storybook/preview.js const globals = { vueMdx: { beforeVueAppMount(app) { // your custom logic here }, }, }; export default { globals, }; ``` -
Syntax Error: Cannot use JSX syntax in .mdx file with Vue components.
cause The Storybook builder is not correctly configured to process Vue JSX within MDX, or the Vue component itself has invalid JSX syntax.fixVerify that `storybook-addon-vue-mdx` is correctly added to the `addons` array in your `.storybook/main.js` file. Also, double-check the Vue JSX syntax used for your components and slots as documented by Vue. -
Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
cause The Vue component imported into the MDX file is not being correctly resolved or passed to the React rendering context, often due to an incorrect import path or module resolution issues.fixEnsure that the path to your Vue component in the MDX `import` statement is correct and that the component itself is properly exported (e.g., using `defineComponent` or `<script setup>`). Make sure Storybook's `builder-vite` is correctly configured and handling `.vue` files.
Warnings
- breaking Version 3.0.0 of `storybook-addon-vue-mdx` requires Storybook 10 as the minimal supported version. Previous versions will not be compatible.
- breaking Version 1.0.0 was a breaking change, porting the addon to support Storybook 8.2.4. Subsequent versions (1.0.6) targeted Storybook 8.5 and React 19.
- gotcha A known bug exists when running Storybook locally where the initial page load of an MDX page with Vue components might crash. This is likely due to Storybook's preview file execution.
- gotcha Components must be locally imported into MDX files. Remote imports or global component registration patterns may not be supported or have not been thoroughly tested.
- breaking Version 2.0.0 included breaking changes related to build targets, security fixes (false positives), and a switch to pnpm for CI/CD workflow, potentially affecting local development setups relying on specific build behaviors or package managers.
Install
-
npm install storybook-addon-vue-mdx -
yarn add storybook-addon-vue-mdx -
pnpm add storybook-addon-vue-mdx
Imports
- Addon Configuration
import storybookAddonVueMdx from 'storybook-addon-vue-mdx';
// .storybook/main.js export default { addons: ['storybook-addon-vue-mdx'], } - Vue Component in MDX
// Sample.mdx import MyComponent from './path-to-components/MyComponent.vue' <MyComponent>bla bla</MyComponent>
- Customizing Vue App Context
// .storybook/preview.js const globals = { vueMdx: { beforeVueAppMount(app) { app.use(myCustomPlugin) }, }, } export default { globals, }
Quickstart
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
// .storybook/main.js
export default {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx|vue)'],
addons: [
'@storybook/addon-essentials',
'@storybook/addon-docs',
'storybook-addon-vue-mdx',
],
framework: {
name: '@storybook/vue3-vite',
options: {},
},
docs: {
autodocs: 'tag',
},
// .storybook/preview.js
// For customizing Vue app context (optional)
// globals: {
// vueMdx: {
// beforeVueAppMount(app) {
// app.use(yourVuePlugin);
// },
// },
// },
};
// src/components/MyButton.vue
<template>
<button @click="onClick">{{ label }}</button>
</template>
<script setup>
import { defineProps } from 'vue';
const props = defineProps({
label: { type: String, default: 'Click me' },
});
const onClick = () => {
console.log('Button clicked!');
};
</script>
// src/stories/MyButton.mdx
import { Meta, Story } from '@storybook/addon-docs';
import MyButton from '../components/MyButton.vue';
<Meta title="Components/MyButton" component={MyButton} />
# MyButton
This is a custom button component.
<MyButton label="Hello from MDX" />
<Story name="Default">
<MyButton label="Story in MDX" />
</Story>