What Causes This Error?
This error occurs when your layout or page component has multiple root elements. Vue 3 allows multiple root nodes (fragments), but Nuxt layouts require a single root node to properly handle transitions and hydration.
The Problem
Multiple Root Elements in Layout
<!-- ❌ Wrong - layouts/default.vue -->
<template>
<Header />
<main>
<slot />
</main>
<Footer />
</template>
Multiple Root Elements in Page
<!-- ❌ Wrong - pages/index.vue -->
<template>
<h1>Title</h1>
<p>Content</p>
<Footer />
</template>
The Fix
Wrap Everything in a Single Element
<!-- ✅ Correct - layouts/default.vue -->
<template>
<div>
<Header />
<main>
<slot />
</main>
<Footer />
</div>
</template>
<!-- ✅ Correct - pages/index.vue -->
<template>
<div>
<h1>Title</h1>
<p>Content</p>
<Footer />
</div>
</template>
Common Scenarios
Layout with Teleport
<!-- ❌ Wrong - teleport counts as root node -->
<template>
<div class="layout">
<slot />
</div>
<Teleport to="body">
<Modal />
</Teleport>
</template>
<!-- ✅ Correct - wrap everything -->
<template>
<div>
<div class="layout">
<slot />
</div>
<Teleport to="body">
<Modal />
</Teleport>
</div>
</template>
Conditional Root Elements
<!-- ❌ Wrong - v-if creates multiple potential roots -->
<template>
<Loading v-if="loading" />
<Content v-else />
</template>
<!-- ✅ Correct - single wrapper -->
<template>
<div>
<Loading v-if="loading" />
<Content v-else />
</div>
</template>
Page with Comments
<!-- ❌ Wrong - HTML comments count as nodes -->
<template>
<!-- Page Header -->
<Header />
<main>Content</main>
</template>
<!-- ✅ Correct - comments inside wrapper -->
<template>
<div>
<!-- Page Header -->
<Header />
<main>Content</main>
</div>
</template>
Why Single Root Is Required
- Transitions - Nuxt page transitions need a single element to animate
- Hydration - SSR hydration expects matching DOM structure
- Layout system - NuxtLayout injects content into a specific slot
- Keep-alive - Vue’s keep-alive requires single root components
Using Semantic HTML
You don’t have to use <div>. Any single wrapper works:
<!-- ✅ Using main as root -->
<template>
<main class="layout">
<Header />
<slot />
<Footer />
</main>
</template>
<!-- ✅ Using section as root -->
<template>
<section class="page">
<h1>Title</h1>
<p>Content</p>
</section>
</template>
<!-- ✅ Using article as root -->
<template>
<article>
<header>Post Title</header>
<p>Post content</p>
</article>
</template>
Related Error: Layout Multiple Root Nodes
Similar warning for layouts:
[nuxt] `{name}` layout does not have a single root node and will cause errors when navigating between routes.
Same fix applies—wrap all layout content in a single element.
Related Error: Page Multiple Root Nodes
[nuxt] `{filename}` does not have a single root node and will cause errors when navigating between routes.
This warns that page navigation transitions will fail.
Quick Fix Pattern
If you have:
<template>
<A />
<B />
<C />
</template>
Change to:
<template>
<div>
<A />
<B />
<C />
</div>
</template>
Debugging Hydration Issues
If you’re still getting hydration mismatches:
// nuxt.config.ts
export default defineNuxtConfig({
vue: {
compilerOptions: {
comments: false // Remove HTML comments that might cause issues
}
}
})
Quick Checklist
- Layout has single root element wrapping
<slot /> - Page has single root element wrapping all content
- Teleports are inside the root wrapper
- v-if/v-else blocks are inside a wrapper
- No stray HTML comments outside wrapper
- No text nodes outside wrapper