# Vue setup
Follow the setup workflow in `SKILL.md`. Implement the gallery per `references/gallery-spec.md`, exposing `window.mount`/`window.unmount` — in Vue, mount with `app = createApp(h(story, props)); app.mount('#root')` and unmount with `app.unmount()`; this page covers Vue-specific details.
## Files
- `playwright/gallery/` — the gallery you implement to `references/gallery-spec.md` (an `index.html` + a `main.ts` module). Requires Vue 3.
- Stories: `src/**/*.story.{ts,js,vue}`; example in `templates/vue/Button.story.ts`.
## Two ways to write stories
**Render-function stories** (`Button.story.ts`) — several scenarios per file, one named export each; see `templates/vue/Button.story.ts`. Uses `defineComponent` + `h()`, no SFC compilation involved.
**Single-file-component stories** (`Button.primary.story.vue`) — one story per file, full template syntax:
```vue
```
An SFC story is addressed by its path without the extension: `mount('components/Button.primary')`. Prefer SFC stories when the scenario needs slots or non-trivial templates.
## Global plugins
Apps that rely on plugins (Pinia, vue-router, i18n) should wrap components with a decorator story helper that creates a fresh instance per story:
```ts
// src/stories/decorators.ts
import { defineComponent, h, type Component } from 'vue';
import { createPinia } from 'pinia';
export function withStore(story: Component) {
return defineComponent(() => {
const pinia = createPinia();
return () => h(story, { pinia });
});
}
```
For plugins that must be installed on the app instance (`app.use(...)`), add them in your gallery right after `createApp(...)` — that is the equivalent of the app's own bootstrap.
## Typed props
A story that takes per-test props declares them twice: in the setup signature (for the type) and in the `props` option (so Vue delivers them as props rather than `attrs`):
```ts
// src/components/Button.story.ts
export const WithTitle = defineComponent(
(props: { title?: string }) => () => h(Button, { title: props.title ?? 'Default' }),
{ props: ['title'] },
);
```
`mount` is generic over the story: pass the story type as a template argument to type-check the props (and `update()`):
```ts
// src/components/button.spec.ts
import type { WithTitle } from './Button.story';
const component = await mount('components/Button/WithTitle', { title: 'Hello' });
```
Options-API stories (`defineComponent({ props: { ... } })`) infer props the same way. For `.story.vue` SFC stories, prop types are only inferable when the setup generates SFC types (Volar/vue-tsc); otherwise pass the props type directly: `mount<{ title?: string }>('components/Button.primary', { title: 'Hello' })`.
## CSS
Import global stylesheets in your gallery entry (`playwright/gallery/main.ts`, e.g. `import '../../src/assets/main.css'`), mirroring the app's entry point.