10 Battle-Tested Prompts for Vue.js and Nuxt: From Components to Pinia Stores

10 Battle-Tested Prompts for Vue.js and Nuxt: From Components to Pinia Stores

If you spend your days writing Vue 3 or Nuxt 3 code, you already know that AI assistants can save you hours — but only if you know how to talk to them. A vague "write a component for me" usually returns mediocre, boilerplate-heavy code. A well-structured prompt, on the other hand, gives you production-ready composables, typed stores, and clean component logic.

This collection contains prompts I actually use in my daily workflow. Each one has been tested, refined, and battle-hardened across real projects — from SaaS dashboards to e-commerce frontends. No fluff, no theory. Just working prompts with concrete examples.

Why Precision Matters in Vue/Nuxt Prompts

Vue 3’s Composition API and Nuxt 3’s auto-imports, server routes, and modules create a unique environment. A prompt that works for generic JavaScript will fail here because it doesn't account for:
- defineComponent and defineProps with TypeScript
- Nuxt’s useFetch, useAsyncData, and auto-imported composables
- Pinia store patterns with setup stores
- File-based routing conventions

Every prompt below accounts for these specifics. They produce code that fits naturally into your existing project structure.

The Prompts

1. Typed Component with Composition API

When you need: A reusable, fully typed Vue 3 component with props, emits, and slots.

Prompt:

Generate a Vue 3 component using Composition API with script setup and TypeScript.
- Props: `title: string`, `items: Array<{id: number, label: string}>`, `loading?: boolean`
- Emits: `select(item: {id: number, label: string})`, `close`
- Slots: default, `empty` (shown when items array is empty)
- Use `defineComponent` and explicit generics where appropriate
- Include a loading skeleton state
- Style with scoped CSS using CSS custom properties for theming

Why it works: It explicitly defines the shape of props, emits, and slots. The AI knows exactly what types to generate and can produce a realistic loading state. Without the slot and state instructions, you’d get a generic list component.

2. Nuxt 3 Server Route with Validation

When you need: A type-safe API endpoint inside server/api/.

Prompt:

Create a Nuxt 3 server route at `server/api/products/[id].get.ts`.
- Accept a numeric `id` from route params
- Validate `id` using zod (return 400 if invalid)
- Simulate fetching from a database (use a hardcoded array for now)
- Return a typed response with `{ id: number, name: string, price: number, inStock: boolean }`
- Handle the case where product is not found (404)
- Use `defineEventHandler` and `getRouterParam`

Why it works: It specifies the exact file path, which Nuxt uses for auto-routing. It also mandates validation, error handling, and types — things developers often forget.

3. Pinia Store with Setup Syntax

When you need: A Pinia store using the setup store syntax (similar to Composition API).

Prompt:

Generate a Pinia store using the setup store syntax for a shopping cart.
- Use `defineStore` with a unique id 'cart'
- State: `items: CartItem[]`, `coupon: string | null`
- Getters: `totalPrice` (computed from items), `itemCount`
- Actions: `addItem(product, quantity)`, `removeItem(productId)`, `applyCoupon(code)`, `clearCart`
- Define a TypeScript interface `CartItem` with `productId: number`, `name: string`, `price: number`, `quantity: number`
- Use `ref` and `computed` inside the store function
- Include a `$reset` method that clears all state

Why it works: Many developers still use the options API for Pinia. This prompt forces the setup syntax, which integrates better with TypeScript and Composition API components.

4. Composable for Data Fetching with Caching

When you need: A custom composable that wraps useAsyncData or useFetch with caching logic.

Prompt:

Write a Nuxt composable `useProducts` in `composables/useProducts.ts`.
- Use `useAsyncData` internally
- Accept optional `categoryId?: number` parameter
- Cache results for 5 minutes using `getCachedData` option
- Return `{ data, pending, error, refresh }`
- Transform the raw API response: map `price` to cents (multiply by 100) and add a `formattedPrice` computed property
- Use TypeScript generics to make it reusable for other entities
- Add JSDoc comments explaining parameters and return type

Why it works: It specifies caching, transformation, and reusability. Without those details, you’d get a generic fetch function that doesn't add value.

5. Complex Form with Validation (VeeValidate or Vuelidate)

When you need: A multi-step form with validation and dynamic fields.

Prompt:

Build a multi-step checkout form component in Vue 3 using VeeValidate 4 with Zod validation.
- Steps: Shipping  Payment  Review
- Shipping step: name, email, address (all required with email format validation)
- Payment step: cardNumber (16 digits, required), expiry (MM/YY, required, validate future date), cvv (3 digits, required)
- Review step: show all entered data for confirmation, emit 'submit' with form values
- Use `useForm` and `useField` from VeeValidate
- Navigation buttons: Previous/Next, disable Next if current step is invalid
- Show validation errors inline below each field
- Use scoped CSS for error styling

Why it works: It defines exact fields, validation rules, and UI behavior. The AI can generate a realistic multi-step flow without guessing.

6. Nuxt Module with Options

When you need: A reusable Nuxt module that adds composables and modifies config.

Prompt:

Create a Nuxt module `my-analytics` in a file `modules/my-analytics/index.ts`.
- Use `defineNuxtModule` from `nuxt/module`
- Accept module options: `{ apiKey: string, trackPageViews: boolean, debug: boolean }`
- Add a composable `useAnalytics` that returns `{ trackEvent(name, data?), pageView() }`
- If `trackPageViews` is true, automatically call `pageView()` on route change using `useRouter`'s `afterEach` hook
- Register a Nitro plugin that adds a server route `/_analytics/collect` (POST) which logs events in dev mode
- Add TypeScript declarations for the composable to `app.d.ts`

Why it works: It covers the three main aspects of a Nuxt module: options, client composables, and server routes. Without the Nitro plugin instruction, you’d only get a client-side module.

7. Responsive Grid with Dynamic Columns

When you need: A grid component that changes columns based on viewport width.

Prompt:

Create a responsive grid component `DynamicGrid.vue` in Vue 3 with Composition API.
- Props: `items: any[]`, `minColumnWidth: number` (default 250px)
- Use CSS Grid with `auto-fill` and `minmax()` for responsive columns
- Add a slot `item` for each grid item, receiving the item as slot prop
- Implement a resize observer that updates a `columns` variable (number of visible columns)
- Expose `columns` via `defineExpose`
- Add a transition for items when columns change (stagger animation)
- Use scoped CSS with custom properties

Why it works: It combines CSS Grid with JavaScript logic (resize observer) and exposes state. The transition instruction forces the AI to think about user experience, not just layout.

8. Auth Guard Middleware in Nuxt

When you need: Route protection with redirect logic.

Prompt:

Write a Nuxt middleware `auth.ts` in `middleware/` directory.
- Check if user is authenticated by reading a Pinia store `useAuthStore`
- Store has `isAuthenticated` getter and `user` state
- If not authenticated and route is not `/login` or `/register`, redirect to `/login` with a `redirect` query param
- If authenticated and route is `/login` or `/register`, redirect to `/dashboard`
- Use `navigateTo` from Nuxt
- Add global middleware configuration via `addRouteMiddleware` or file naming convention
- Handle both client-side and server-side (check `process.client` vs `process.server`)

Why it works: It covers edge cases: already-authenticated users visiting login, redirect preservation, and SSR compatibility.

9. Reactive Chart Component with Chart.js

When you need: A chart component that updates when data changes.

Prompt:

Create a Vue 3 component `LineChart.vue` using Chart.js via the `vue-chartjs` wrapper.
- Props: `data: { labels: string[], values: number[] }`, `options?: object`, `height?: number`
- Use `watch` to update the chart when `data` changes (destroy and re-create or use `update()`)
- Register required Chart.js components: CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend
- Use `ref` to hold the chart instance
- Implement `onUnmounted` to destroy the chart and prevent memory leaks
- Add a loading state (show a skeleton while chart renders)
- Style the chart container with responsive width

Why it works: It handles lifecycle (destroy on unmount), reactivity (watch), and registration — common pitfalls when using Chart.js in Vue.

10. Dynamic Table with Sorting, Filtering, and Pagination

When you need: A data table that handles all CRUD display logic.

Prompt:

Build a reusable Vue 3 table component `DataTable.vue`.
- Props: `columns: { key: string, label: string, sortable?: boolean, filterable?: boolean }[]`, `rows: any[]`, `pageSize?: number`
- Features:
  1. Client-side sorting: click column header to sort ascending/descending, show sort indicator
  2. Client-side filtering: text input above each filterable column
  3. Pagination: page numbers, previous/next buttons, show 'X of Y' info
- Emit `update:sort` and `update:filter` events with current state
- Use computed properties for sorted, filtered, and paginated data
- Add a slot `cell` for custom cell rendering, receiving `{ column, row, value }`
- Support multi-column sorting (shift+click)
- Style with scoped CSS, use CSS variables for theming

Why it works: It defines exact column configuration, events, and advanced features (multi-column sorting). The slot instruction makes it truly reusable.

Pro Tips for Better Prompts

Mistake Fix
"Write a component" Specify props, emits, slots, and styling
"Use Pinia" Specify setup vs options syntax, exact state shape
"Make it responsive" Define breakpoints or min/max widths
"Handle errors" Specify error states (404, 500, network) and UI feedback
"Add loading" Define what loading looks like (skeleton, spinner, shimmer)

Always include:
- Exact file path (Nuxt auto-imports depend on it)
- TypeScript interfaces (even if not using TS, it forces structure)
- Lifecycle hooks (onMounted, onUnmounted, watch)
- Edge cases (empty state, error state, loading state)

Conclusion

A well-crafted prompt is the difference between "this AI writes garbage" and "this AI just saved me two hours." The prompts above are the ones I open every day — they produce code that requires minimal edits before hitting production.

The key is specificity. Don't ask for a "composable" — ask for a composable that caches for 5 minutes, transforms API responses, and returns a typed object. Don't ask for a "form" — ask for a multi-step form with VeeValidate, Zod, and inline error messages.

Copy these prompts, adapt them to your project, and watch your productivity jump. Your future self — the one not debugging a broken chart component at 11 PM — will thank you.

← All posts

Comments