What Causes This Error?
This warning appears when a page component has multiple root elements. While Vue 3 supports fragments (multiple roots), Nuxt page transitions and navigation require a single root element.
The Problem
<!-- ❌ Wrong - pages/about.vue -->
<template>
<h1>About Us</h1>
<p>Our story...</p>
<ContactForm />
</template>
This will cause errors when navigating to/from this page.
The Fix
Wrap everything in a single element:
<!-- ✅ Correct - pages/about.vue -->
<template>
<div>
<h1>About Us</h1>
<p>Our story...</p>
<ContactForm />
</div>
</template>
Why Single Root Is Required
- Page Transitions - CSS transitions need a single element to animate
- Keep-Alive - Caching pages requires single root components
- Suspense - Async loading boundaries expect single root
- Hydration - SSR matching requires consistent DOM structure
Common Patterns That Cause This
Multiple Elements
<!-- ❌ Wrong -->
<template>
<Header />
<main>Content</main>
<Footer />
</template>
<!-- ✅ Correct -->
<template>
<div class="page">
<Header />
<main>Content</main>
<Footer />
</div>
</template>
Conditional Rendering
<!-- ❌ Wrong - v-if/v-else at root -->
<template>
<Loading v-if="loading" />
<Content v-else />
</template>
<!-- ✅ Correct -->
<template>
<div>
<Loading v-if="loading" />
<Content v-else />
</div>
</template>
Comments at Root
<!-- ❌ Wrong - comment is a node -->
<template>
<!-- Page content -->
<div>Content</div>
</template>
<!-- ✅ Correct -->
<template>
<div>
<!-- Page content -->
Content
</div>
</template>
Teleport at Root
<!-- ❌ Wrong -->
<template>
<div>Main content</div>
<Teleport to="body">
<Modal />
</Teleport>
</template>
<!-- ✅ Correct -->
<template>
<div>
<div>Main content</div>
<Teleport to="body">
<Modal />
</Teleport>
</div>
</template>
Using Semantic HTML
You don’t have to use <div>. Use appropriate semantic elements:
<!-- ✅ Using main -->
<template>
<main class="about-page">
<h1>About</h1>
<p>Content...</p>
</main>
</template>
<!-- ✅ Using article -->
<template>
<article>
<header>
<h1>Blog Post Title</h1>
</header>
<p>Content...</p>
</article>
</template>
<!-- ✅ Using section -->
<template>
<section class="dashboard">
<Sidebar />
<MainContent />
</section>
</template>
Page Transitions
With a single root, transitions work properly:
<!-- nuxt.config.ts -->
export default defineNuxtConfig({
app: {
pageTransition: { name: 'page', mode: 'out-in' }
}
})
/* app.vue or global CSS */
.page-enter-active,
.page-leave-active {
transition: all 0.3s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
transform: translateY(20px);
}
Checking Your Pages
Quick script to find problematic pages:
# Look for templates without wrapper
grep -r "^<template>" pages/ | head -20
Or in Vue DevTools:
- Open Vue DevTools
- Navigate to Components tab
- Check page component structure
Fix Multiple Pages at Once
If you have many pages to fix, consider a layout approach:
<!-- layouts/default.vue -->
<template>
<div class="layout">
<slot />
</div>
</template>
Each page content becomes a single element wrapped by layout.
Quick Transformation
If you see:
<template>
<A />
<B />
<C />
</template>
Change to:
<template>
<div>
<A />
<B />
<C />
</div>
</template>
Quick Checklist
- Every page has exactly one root element in template
- No comments outside the root wrapper
- v-if/v-else blocks are inside a wrapper
- Teleports are inside the wrapper
- No text nodes directly at root level