10 Prompts for Vue.js and Nuxt: Components, Composables, Routing
Vue.js has become one of the most popular frontend frameworks, and Nuxt brings server-side rendering, file-based routing, and a powerful module system to the table. Yet even experienced developers spend hours writing boilerplate for composables, stores, and page components. AI prompt engineering can cut that time drastically—if you know how to write the right prompts.
This guide is a practical cheat sheet for Vue.js and Nuxt developers who want to generate high-quality code with AI assistants like ChatGPT, Claude, or Copilot. Each prompt is ready to copy-paste, with a real-world example and a note on when to use it. Whether you're building a small component or a full Nuxt application, these prompts will help you move faster without sacrificing code quality.
I've selected 10 prompts based on the official Vue.js and Nuxt documentation, as well as common patterns from production codebases. Remember to always review and test AI-generated code—it's a starting point, not a replacement for your judgment.
Why Prompt Engineering Matters for Vue.js
AI models are trained on a massive amount of public code, including Vue components, composables, and Pinia stores. A well-crafted prompt gives the model context: the version of Vue or Nuxt you're using, the problem you're solving, and the constraints you care about (TypeScript, testing, accessibility). The Vue.js docs emphasize the Composition API, and Nuxt's documentation shows modern data fetching patterns like useAsyncData. When your prompt references these patterns explicitly, you get more accurate results.
10 Prompts for Vue.js and Nuxt
Here are the prompts. Each section includes a short explanation, the prompt itself, and an example of what the generated code might look like.
1. Vue 3 Component with Composition API and TypeScript
When to use: You need a reusable component with typed props and events, following modern Vue coding standards.
Prompt:
Act as a senior Vue.js developer. Write a Vue 3 component using the Composition API with `<script setup lang="ts">`. The component should be a search input that accepts a `label` prop (string), emits an `update:modelValue` event, and supports a `clear` button. Use `defineProps`, `defineEmits`, and a computed ref. Style it with scoped CSS. Include clear, concise comments.
Example generated code (abridged):
<template>
<div class="search-input">
<label :for="id">{{ label }}</label>
<input
:id="id"
:value="modelValue"
@input="onInput"
type="text"
/>
<button v-if="modelValue" @click="clear">Clear</button>
</div>
</template>
<script setup lang="ts">
import { computed, useId } from 'vue'
const props = defineProps<{
label: string
modelValue: string
}>()
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
}>()
const id = `search-${useId()}`
const onInput = (event: Event) => {
const target = event.target as HTMLInputElement
emit('update:modelValue', target.value)
}
const clear = () => emit('update:modelValue', '')
</script>
<style scoped>
.search-input { display: inline-flex; align-items: center; gap: 0.5rem; }
</style>
This prompt works well because it specifies the version, the API (<script setup>), and the exact feature set. You can extend it with error handling or localization.
2. Reusable Data Fetching Composable with Loading and Error States
When to use: You're building a composable that can be reused across components to fetch data from an API, handling loading and error states consistently.
Prompt:
Create a Vue 3 composable called `useFetchData` that takes a URL string and an options object. It should return `{ data, loading, error, refetch }`. Use the Composition API with `ref` and `watch`. The composable should run the fetch immediately and support a `immediate` option (default true). Use AbortController to cancel the previous request when refetch is called. Include TypeScript types and error handling for non-200 responses.
Example use in a component:
const { data, loading, error, refetch } = useFetchData('/api/users')
watch(loading, (isLoading) => {
if (isLoading) showSpinner()
})
The generated composable abstracts a common concern. According to the Vue Composition API docs, composables are the recommended way to share stateful logic.
3. Nuxt Page with useAsyncData
When to use: You're working in a Nuxt app and need to fetch data on the server before rendering, or fetch it client-side after navigation.
Prompt:
Write a Nuxt 3 page component that fetches a list of articles from a REST API. Use `useAsyncData` with a proper key and `$fetch`. The page should have a loading state, an error state, and show a list of articles. Use `<script setup>` and TypeScript. Follow the Nuxt 3 data fetching conventions from the official docs.
Example generated code snippet:
<script setup lang="ts">
interface Article {
id: number
title: string
summary: string
}
const { data: articles, pending, error } = await useAsyncData<Article[]>(
'articles',
() => $fetch('/api/articles')
)
</script>
<template>
<div>
<p v-if="pending">Loading articles...</p>
<p v-else-if="error">{{ error.message }}</p>
<ul v-else>
<li v-for="article in articles" :key="article.id">
<h2>{{ article.title }}</h2>
<p>{{ article.summary }}</p>
</li>
</ul>
</div>
</template>
The Nuxt useAsyncData documentation explains why keys are important for caching and invalidation.
4. Pinia Store for Authentication
When to use: You need a global store for user authentication with state, getters, and async actions.
Prompt:
Create a Pinia store for authentication in a Vue 3 application. The store should have state: user (object
| null), token (string | null). Getters: isAuthenticated. Actions: login(credentials), logout(), and fetchCurrentUser(). Use async/await and simulate the API calls. Include comments explaining each part. Use the Composition API style for the store (rather than the options API).
Example generated store (abridged):
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
export const useAuthStore = defineStore('auth', () => {
const user = ref(null)
const token = ref<string | null>(null)
const isAuthenticated = computed(() => !!token.value)
async function login(credentials: { email: string; password: string }) {
const response = await api.login(credentials)
token.value = response.token
user.value = response.user
}
function logout() {
token.value = null
user.value = null
}
return { user, token, isAuthenticated, login, logout }
})
Pinia is the official state management library for Vue, as described in the Pinia docs. Using the Composition API style matches Vue's recommended patterns.
5. Dynamic Form Component with Validation
When to use: You need a form that can generate fields from a schema and perform validation without pulling in a heavy form library.
Prompt:
Build a Vue 3 form component that accepts a schema array with fields. Each field has: `name`, `label`, `type` (text, email, select, textarea), `required` (boolean), and `options` (for select). The component should manage its own state, validate on submit, and emit a `submit` event with the form data. Use Vuelidate or a simple custom validation function. Prefer custom validation to keep the component self-contained. Style with scoped CSS. Add error messages below each field.
Example schema:
const fields = [
{ name: 'name', label: 'Name', type: 'text', required: true },
{ name: 'email', label: 'Email', type: 'email', required: true },
{ name: 'role', label: 'Role', type: 'select', required: true, options: ['Developer', 'Designer'] }
]
The component can be reused in any form. For a production approach, check out the Vue form validation guide.
6. Unit Tests for a Vue Component with Vitest
When to use: You want to generate tests that cover component rendering, props, and emitted events.
Prompt:
Write Vitest unit tests for a Vue 3 component called `Counter.vue`. The component has a `start` prop (default 0), a `count` ref, `increment` and `decrement` methods, and emits `update:count`. Test that:
1. It renders the initial count.
2. Clicking the increment button increases the count.
3. Clicking the decrement button decreases the count.
4. The `update:count` event is emitted with the new value.
Use `@vue/test-utils` and `vitest`. Provide complete test code with imports.
Example test snippet:
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
import Counter from '../Counter.vue'
describe('Counter', () => {
it('renders the initial count', () => {
const wrapper = mount(Counter, { props: { start: 5 } })
expect(wrapper.find('.count').text()).toBe('5')
})
})
The Vitest docs recommend this setup for Vue. Testing is an area where AI prompts shine because the boilerplate is repetitive.
7. Nuxt Module with Runtime Config and Plugin
When to use: You're building a Nuxt module to integrate an external service and need to add runtime configuration and a plugin.
Prompt:
Create a Nuxt 3 module named `@my/module`. The module should:
- Accept options: `apiKey` (string) and `endpoint` (string).
- Set runtime config with the options.
- Add a plugin that creates a client from `useRuntimeConfig()` and injects it as `$myClient`.
- Include a proper Nuxt module file with `defineNuxtModule`.
Use TypeScript and add JSDoc comments. Follow the pattern from the Nuxt module author guide.
Example generated module structure:
// modules/my-module/module.ts
import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit'
export default defineNuxtModule({
meta: { name: 'my-module' },
setup(options, nuxt) {
nuxt.options.runtimeConfig.public.myModule = options
addPlugin(resolver.resolve('./runtime/plugin'))
}
})
The Nuxt modules guide provides official examples. Modules are a powerful way to share functionality across projects.
8. Custom Directive to Detect Outside Click
When to use: You need a reusable directive for dropdowns, popovers, or modal windows that close when the user clicks outside.
Prompt:
Write a Vue 3 custom directive called `v-click-outside` that invokes a provided function when the user clicks or taps outside the element. Register the directive globally in a Vue application. The directive should support a value that is a function, and it should work with both mouse and touch events. Include cleanup on unmount.
Example usage in a component:
<template>
<div v-click-outside="closeDropdown">
<button @click="toggle">Toggle</button>
<div v-if="isOpen">...</div>
</div>
</template>
The Vue custom directive guide shows how to create and register directives. This prompt is great when you want a lightweight alternative to a third-party library.
9. Vue Router Configuration with Lazy Loading and Meta
When to use: You're setting up routing for a large application and want lazy-loaded routes with metadata for authentication and titles.
Prompt:
Generate a Vue Router configuration for a Vue 3 app. It should include routes: `/` (Home), `/about` (About), and `/admin` (Admin). Use dynamic imports for each component to enable lazy loading. Add `meta` fields: `requiresAuth` (boolean) and `title` (string). Create a helper function to set the document title based on `meta.title` and use `router.beforeEach` to check `requiresAuth`. Write the complete code.
Example generated router snippet:
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: () => import('@/views/Home.vue'), meta: { title: 'Home', requiresAuth: false } },
{ path: '/about', component: () => import('@/views/About.vue'), meta: { title: 'About', requiresAuth: false } },
{ path: '/admin', component: () => import('@/views/Admin.vue'), meta: { title: 'Admin', requiresAuth: true } }
]
})
router.beforeEach((to) => {
document.title = to.meta.title || 'Default'
if (to.meta.requiresAuth && !useAuthStore().isAuthenticated) return '/login'
})
The official Vue Router docs recommend dynamic imports for code splitting.
10. Composable for Persistent Local Storage Sync
When to use: You want to store a piece of reactive state in localStorage and keep it in sync with a ref.
Prompt:
Write a Vue 3 composable `useLocalStorage` that takes a key and a default value. It should return a `ref` that reads the value from `localStorage` on initialization and writes to `localStorage` on every change. Support JSON serialization and handle parsing errors gracefully. Use `watch` with `deep: true` for objects. Include TypeScript generics.
Example usage:
const settings = useLocalStorage('settings', { theme: 'dark' })
// Changing the ref automatically updates localStorage
This pattern is common in many Vue applications. You can find similar implementations on GitHub, but a well-crafted prompt gives you a version tailored to your types.
Conclusion
Prompt engineering is a skill that pays off immediately when working with Vue.js and Nuxt. The 10 prompts above cover essential tasks: building components, fetching data, managing state with Pinia, testing, routing, and creating custom directives. Each prompt gives the AI the context it needs to produce idiomatic code.
Before you use any generated code, always run it through your linter, add tests, and check the official documentation for your specific Vue or Nuxt version. AI-generated code is not a substitute for understanding the framework—it's a starting point that can save you hours of boilerplate writing.
I use these prompts daily in my own development workflow. Start with the ones that match your current task, and adapt them to your project's conventions. You'll quickly find that you spend more time reviewing code than writing it—and that's a good thing.
Now pick a prompt and try it out. Your next component is a click away.
Comments