What Causes This Warning?
This warning appears when you have a pages/ directory but no app.vue file to render the pages. Nuxt needs an entry component that includes <NuxtPage>.
The Problem
my-nuxt-app/
├── pages/
│ ├── index.vue
│ └── about.vue
├── nuxt.config.ts
└── package.json
# Missing: app.vue ❌
Without app.vue, Nuxt doesn’t know where to render your pages.
The Fix
Create app.vue
<!-- app.vue -->
<template>
<NuxtPage />
</template>
That’s it! This minimal app.vue enables page routing.
Full Setup
Basic app.vue
<!-- app.vue -->
<template>
<NuxtPage />
</template>
With Layout Support
<!-- app.vue -->
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
With Global Components
<!-- app.vue -->
<template>
<div>
<Header />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
<Footer />
</div>
</template>
With Error Handling
<!-- app.vue -->
<template>
<NuxtLayout>
<NuxtErrorBoundary>
<NuxtPage />
<template #error="{ error, clearError }">
<div class="error-page">
<h1>Something went wrong</h1>
<p>{{ error.message }}</p>
<button @click="clearError">Try again</button>
</div>
</template>
</NuxtErrorBoundary>
</NuxtLayout>
</template>
Understanding NuxtPage
<NuxtPage> is the component that renders the current page based on the route:
URL: /about
↓
<NuxtPage /> renders pages/about.vue
URL: /products/123
↓
<NuxtPage /> renders pages/products/[id].vue
Project Structure
Minimal Setup
my-nuxt-app/
├── app.vue ← Required for pages
├── pages/
│ └── index.vue
└── nuxt.config.ts
Full Setup
my-nuxt-app/
├── app.vue ← Entry point
├── layouts/
│ └── default.vue ← Optional layouts
├── pages/
│ ├── index.vue
│ └── about.vue
├── components/
│ ├── Header.vue
│ └── Footer.vue
└── nuxt.config.ts
Page Transitions
<!-- app.vue -->
<template>
<NuxtLayout>
<NuxtPage :transition="{
name: 'page',
mode: 'out-in'
}" />
</NuxtLayout>
</template>
<style>
.page-enter-active,
.page-leave-active {
transition: all 0.3s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
transform: translateY(10px);
}
</style>
Global State/Providers
<!-- app.vue -->
<script setup>
// Global state initialization
const colorMode = useColorMode()
</script>
<template>
<div :class="colorMode.value">
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</div>
</template>
With Suspense
<!-- app.vue -->
<template>
<NuxtLayout>
<NuxtLoadingIndicator />
<NuxtPage />
</NuxtLayout>
</template>
Alternative: No Pages
If you don’t need file-based routing, you can skip app.vue:
// nuxt.config.ts
export default defineNuxtConfig({
// Disable pages
pages: false
})
Then create your app structure differently:
<!-- app.vue (without NuxtPage) -->
<template>
<div>
<MyCustomRouter />
</div>
</template>
Common Patterns
Loading States
<!-- app.vue -->
<script setup>
const nuxtApp = useNuxtApp()
const loading = ref(false)
nuxtApp.hook('page:start', () => {
loading.value = true
})
nuxtApp.hook('page:finish', () => {
loading.value = false
})
</script>
<template>
<div>
<LoadingBar v-if="loading" />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</div>
</template>
Authentication Wrapper
<!-- app.vue -->
<script setup>
const { user, loading } = useAuth()
</script>
<template>
<div v-if="loading">
Loading...
</div>
<NuxtLayout v-else>
<NuxtPage />
</NuxtLayout>
</template>
Quick Checklist
- Create
app.vuein project root - Include
<NuxtPage />in template - Add
<NuxtLayout>if using layouts - Add global components/providers as needed
- Configure transitions if desired
- Add error boundaries for robustness