Vue.js Workflow Recipes
PLVue.js workflow recipes are copy-paste prompts that get production Vue 3.5 code from Claude Code, Codex or Cursor. Each recipe pairs a prompt with the check that proves it: vue-tsc, eslint-plugin-vue reactivity-loss rules, Vitest with Vue Test Utils, and a Playwright accessibility scan. Pinning versions matters most, because popular Vue packages disagree about which versions they support.
This page is for Vue developers and the tech leads who set their team’s agent conventions. You ask the agent for a store and it writes Vuex with the Options API. You ask for a form and it installs @vee-validate/zod, which declares Zod 3 as its peer while your project runs Zod 4. The component renders, a destructured store property silently stops updating, and nothing fails until a user changes a quantity in the cart.
What these Vue recipes give you
Section titled “What these Vue recipes give you”- A version-pin block for
CLAUDE.md,AGENTS.mdor a Cursor rule that keeps the agent on<script setup>, Pinia 4 and the packages your lockfile actually holds - One
verifycommand the agent runs before it reports success, so you review a green gate instead of every line - Eight prompts for the Vue work agents are asked for most: typed components, Pinia stores, composables, multi-step forms, long lists, lazy routes, i18n, and Options API migrations, plus Storybook setup
- A table of which gate catches which class of bug, and who signs the change off
- The failure modes of agent-written Vue and how to get out of each one
Pin the Vue stack in the agent’s context first
Section titled “Pin the Vue stack in the agent’s context first”Agents learned Vue from years of Vue 2, Vuex and Options API tutorials, and from package READMEs that predate this year’s major releases. Put the versions, the banned patterns and the done command in the instructions your tool reads: CLAUDE.md for Claude Code, AGENTS.md for Codex, and a project rule for Cursor. Claude Code v2.1.277 and later also reads AGENTS.md when a project has no CLAUDE.md.
## Frontend stack (Vue)- Vue 3.5 with <script setup lang="ts"> only. TypeScript strict. Vite 8, Vitest 5, Vue Test Utils 2, Playwright for e2e.- State: Pinia 4 setup stores. Routing: Vue Router 5. i18n: vue-i18n 11 with legacy: false. Utilities: check @vueuse/core before writing a composable (useFetch, useWebSocket, useColorMode, useRefHistory already exist).- Validation: Zod 4 schemas. Do not install @vee-validate/zod (its peer range is Zod 3).- Never write: Options API in new files, Vuex, `this.$store`, destructuring a store or a reactive() object without storeToRefs/toRefs, `as any`, `<Suspense>` for route loading.- Before you say a task is done, run `npm run verify` and paste the last 20 lines of output.- Tests find elements by role and label text, never by CSS class.The traps below were checked against the published packages on 2026-09-26. Each one is a case where the agent’s default, or the first README it finds, gives you something that installs and then misbehaves:
| Agent writes | What the registry says on 2026-09-26 | Write instead |
|---|---|---|
createI18n({ locale: 'en' }) | legacy defaults to true in vue-i18n 11.4.12 and is marked “will be removed at vue-i18n v12” | createI18n({ legacy: false, ... }) and useI18n() |
npm i @vee-validate/zod with Zod 4 | @vee-validate/zod 4.15.1 declares peerDependencies: { zod: "^3.24.0" } | Validate with the Zod 4 schema’s safeParse in a small composable, as in the form recipe below |
import { expect } from '@storybook/test' | The package stopped at 8.6.15; Storybook 10.6.0 ships it as storybook/test | import { expect } from 'storybook/test' |
@storybook/addon-vitest next to Vitest 5 | Its 10.6.0 peer range is vitest ^3.0.0 || ^4.0.0 | Check the peer range first, as in the Storybook recipe below |
vite-plugin-vue-mcp for a Vite 8 app | Its 0.3.2 peer range ends at Vite 6 | Chrome DevTools MCP or Playwright MCP against the dev server |
const { items } = useCartStore() | Loses reactivity: items is a snapshot | const { items } = storeToRefs(useCartStore()) |
Give the agent one command that proves the change
Section titled “Give the agent one command that proves the change”Add one script that chains every gate, and make the agent run it before it reports done:
{ "scripts": { "verify": "vue-tsc --noEmit && eslint . --max-warnings 0 && vitest run && playwright test --grep @a11y" }}The last step runs only tests whose title carries @a11y, so on a fresh project it matches nothing until you add one. Start with a single axe scan of a page and add a test per view as you build it:
import { test, expect } from '@playwright/test';import AxeBuilder from '@axe-core/playwright';
test('home page has no axe violations @a11y', async ({ page }) => { await page.goto('/'); const results = await new AxeBuilder({ page }).analyze(); expect(results.violations).toEqual([]);});eslint-plugin-vue 10 ships flat/recommended, but the two rules that catch the most common agent bug in Vue, lost reactivity, are not in that preset. Turn them on yourself:
import pluginVue from 'eslint-plugin-vue';
export default [ ...pluginVue.configs['flat/recommended'], { rules: { 'vue/no-ref-object-reactivity-loss': 'error', 'vue/no-setup-props-reactivity-loss': 'error', 'vue/require-explicit-emits': 'error', 'vue/no-v-html': 'error', }, },];Add your TypeScript parser setup (for example typescript-eslint) to the same file. How you make the agent run verify differs by tool:
Put the rule in CLAUDE.md as in the block above. To enforce it rather than ask for it, add a Stop hook that runs npm run verify and blocks the stop when it fails; see hooks automation. For a headless run in CI, claude -p takes the same prompt.
Put the rule in AGENTS.md, which Codex loads at the start of a session. In CI, codex exec runs the prompt headless; run npm run verify as the next job step so the job fails when the gate does, whatever the agent reported.
Put the pin block in a project rule under Cursor Rules. Cursor Hooks can run a script at points in the agent loop, so the gate does not depend on the agent remembering the rule. Bugbot then reviews the pull request as a second opinion, not as the gate.
Load the Vue skills before the first prompt
Section titled “Load the Vue skills before the first prompt”The vuejs-ai/skills repository packages Vue guidance as agent skills: vue-best-practices, vue-pinia-best-practices, vue-router-best-practices, vue-testing-best-practices, vue-debug-guides and create-adaptable-composable. Its README calls it an “early experiment” and a community project, and its router skill targets Vue Router 4, so treat it as a reviewer’s checklist, not as the gate.
claude plugin marketplace add vuejs-ai/skillsclaude plugin install vue-best-practices@vue-skillsnpx skills add vuejs-ai/skills --skill vue-best-practicesnpx skills add vuejs-ai/skills --skill vue-best-practicesThe README advises starting a prompt with “Use vue skill” because the skill does not always trigger on its own. For a Vite-and-Nuxt-wide set, antfu/skills generates vue, pinia, vite and vitest skills from the official docs. Load one framework set, not both: overlapping skills compete for the same context. More options are on the best skills for frontend development.
Scaffold a typed component with script setup
Section titled “Scaffold a typed component with script setup”Scenario: you need a feature component that follows the team’s conventions: typed props, typed emits, and tests that exercise both.
How you know it worked: verify is green, and the test names read like the prop and emit list. Read the test names, not the template. If a test finds an element by class, send it back.
Build a Pinia 4 store that stays reactive
Section titled “Build a Pinia 4 store that stays reactive”Scenario: a cart store must survive a page refresh, support undo of the last change, and keep every component in sync.
How you know it worked: the reload test and the two undo tests pass, and grep -rn "useCartStore()" src/components shows every destructuring call wrapped in storeToRefs. stubActions: false matters: createTestingPinia stubs every action by default, so an action test without it passes without running the action.
Extract composables without rewriting VueUse
Section titled “Extract composables without rewriting VueUse”Scenario: twelve components repeat the same fetch-with-loading pattern, a debounced search, and a permission check.
The trap here is the agent writing its own useFetch or useWebSocket when @vueuse/core 15 already exports both, with abort, auto-reconnect and heartbeat options. Tell it to look first.
How you know it worked: the report lists each pattern once, the component diff removes more lines than the composables add, and vue/no-ref-object-reactivity-loss stays silent. A composable that returns reactive() fails that lint rule the moment a caller destructures it.
Validate a multi-step form with Zod 4
Section titled “Validate a multi-step form with Zod 4”Scenario: a four-step checkout form must block “Next” on invalid fields, keep data when the user goes back, and survive an accidental refresh.
How you know it worked: the four tests pass, grep -rn "vee-validate\|string().email" src package.json returns nothing, and the axe scan is clean. The card-field exclusion is the one line worth a human glance, because a failure there stores card data in the browser.
Render 50,000 rows without freezing the page
Section titled “Render 50,000 rows without freezing the page”Scenario: an admin log view must show 50,000 rows with sorting and filtering.
Virtualization fixes rendering; it does not fix a sort that runs on every keystroke. Ask for a measured result, not a component list. Chrome DevTools MCP gives the agent a performance trace of the running page; the setup for all three tools is in browser automation with MCP.
How you know it worked: the trace numbers are in the report, and the DOM row count while scrolling stays near the viewport size rather than 50,000. Keep the trace as the baseline; performance testing shows how to turn it into a CI budget.
Lazy-load routes and recover from a failed chunk
Section titled “Lazy-load routes and recover from a failed chunk”Scenario: every view ships in the first bundle, and after each deploy some users hit a blank page because their tab asks for a chunk that no longer exists.
How you know it worked: the build output lists one chunk per lazy route, and the reload test proves the guard. <Suspense> is still marked experimental in the Vue docs, which is why the prompt bans it for routing.
Add i18n with lazy-loaded locales
Section titled “Add i18n with lazy-loaded locales”Scenario: the app ships English only, and Spanish and Japanese must load on demand without bloating the first bundle.
How you know it worked: the key-parity test passes, grep -rn "legacy: true\|this\.\$t" src returns nothing, and the build shows es and ja as separate chunks. The parity test is what keeps the next agent from adding an English key and forgetting the other two.
Migrate an Options API component without changing behaviour
Section titled “Migrate an Options API component without changing behaviour”Scenario: a 500-line Options API component with watchers and a Vuex module, no tests, and a plan to move to <script setup> and Pinia.
Pin the behaviour before the refactor. The method is in characterization tests.
How you know it worked: the test bodies are identical between the two commits (git diff HEAD~1 -- '*.test.ts' touches only the mounting helper) and pass on both. That diff is the one thing to check by hand.
Set up Storybook 10 with accessibility checks
Section titled “Set up Storybook 10 with accessibility checks”Scenario: new team members cannot find existing components, and accessibility issues reach review.
The installer asks questions, so how you start it differs by tool:
Run npm create storybook@latest in your own terminal first, answer the prompts (Vue 3 with Vite), then give claude the prompt below to write the stories.
Run npm create storybook@latest yourself, or let Codex run it and approve the install when asked, then give Codex the prompt below.
In Agent mode, ask Cursor to run npm create storybook@latest in its terminal and answer the prompts, then paste the prompt below.
How you know it worked: build-storybook succeeds, the a11y panel shows no violations on the five components, and the Modal play function passes. If the agent reports the Vitest range mismatch, that is the recipe working: a forced install with --legacy-peer-deps is the wrong fix.
How to verify agent-written Vue code without reading every line
Section titled “How to verify agent-written Vue code without reading every line”| Gate | Catches | Runs where | Signs off |
|---|---|---|---|
vue-tsc --noEmit with strict | Wrong props and emits, lost generics, APIs the installed version does not have | agent loop, CI | automatic |
eslint-plugin-vue with the reactivity-loss rules at error | Destructured props, refs and stores that stop updating; implicit emits; v-html | agent loop, CI | automatic |
| Vitest + Vue Test Utils + MSW | Behaviour after each user action, including error paths and reloads | agent loop, CI | automatic; the author reviews test names, not bodies |
| Characterization tests, unchanged across a migration | Behaviour drift in refactors | agent loop, CI | author checks the test diff |
| Locale key-parity test | Missing translations | agent loop, CI | automatic |
Playwright + @axe-core/playwright, Storybook a11y addon | Missing labels, focus traps, contrast, roles | CI | automatic; an accessibility owner reviews new exceptions |
| Performance trace before and after | Regressions in the interaction you changed | agent loop with Chrome DevTools MCP | author; a CI budget when it matters |
A human reviews what the gates cannot: the acceptance criteria, the test names, and any diff to a test file. Write the acceptance criteria before the prompt; see acceptance criteria. Keep the agent from editing its own tests to make them pass; see protect the oracle. For the pull request itself, reviewing agent pull requests covers who signs off and on what evidence.
When agent-written Vue code breaks
Section titled “When agent-written Vue code breaks”- Reactivity silently lost. The agent destructures a store, a
reactive()object or a composable’s return value, and the UI stops updating after the first render. Recovery:storeToRefsfor stores,toRefsor ref returns for composables, and the two reactivity-loss lint rules aterror. - Vue 2 or Options API code in a Vue 3.5 project.
this.$store,data(), Vuex and filters appear in new files. Recovery: the version-pin block, the Vue skill, and a lint rule or code review that rejects Options API in new files. - Peer-range mismatches forced through. The agent hits an
ERESOLVEerror from@vee-validate/zodor@storybook/addon-vitestand retries with--legacy-peer-deps. Recovery: ban that flag in the instruction file, and have the agent report the mismatch instead, as the Storybook prompt does. - Store tests that never run the store.
createTestingPiniastubs actions by default, so the test asserts a spy was called and the action body never executes. Recovery:stubActions: falsein store tests; keep stubbing only in component tests. - A composable that duplicates VueUse. The agent writes 200 lines of reconnect logic that
useWebSocketalready has. Recovery: the “check@vueuse/corefirst” line in the instructions, and the replaced-versus-written report the composable prompt asks for. - Hydration or
windowerrors when the app moves to SSR. Code that readswindoworlocalStorageat module scope breaks on the server. Recovery: browser-only code goes inonMounted; for a server-rendered Vue app, the Nuxt recipes cover the SSR gates. - Tests that test the mock. The agent mocks the composable it wrote and asserts the mock’s return value. Recovery: mock at the HTTP boundary with MSW, and reject tests that
vi.mockthe module under test.
Where to go next with Vue and agents
Section titled “Where to go next with Vue and agents”- Frontend recipes overview for the other frameworks in this section
- Nuxt recipes for server rendering, server routes and deployment, which this page leaves to its neighbour
- Unit testing with agents and accessibility testing for the gates behind
verify - Playwright MCP for letting the agent check the running app, and end-to-end testing for the flows that span pages
- API development patterns for the backend these components call
Did this guide help you?
One tap, no sign-in.
What will you do with it?
How did it read?
What got in the way? Pick any that apply
Your role?
Thanks. I read every answer. Michał Jaskólski
Did this guide help?
One tap each. Close whenever you like.
no sign-in