Getting Started
Overview
vite-plugin-vue-layouts-next gives Vue Router 5 file-based routes a layout system. Layouts live in src/layouts and are ordinary Vue components with a <router-view> in the template; every page is wrapped in one.
It consists of two parts:
- A Vite plugin that scans your layouts directory and generates the
virtual:generated-layoutsmodule. - A
setupLayoutshelper that rewrites your route records so each page becomes a child of its layout.
Pages that do not choose a layout use default.vue. Pages that do choose one name it through meta.layout, using a normalized layout name.
You can learn more about the rationale behind the fork in the Why section, and about the route transformation in How it works.
Installation
npm install -D vite-plugin-vue-layouts-nextyarn add -D vite-plugin-vue-layouts-nextpnpm add -D vite-plugin-vue-layouts-nextCompatibility Note
This package targets Vite 6 to 8, Vue 3.2+, and Vue Router 4.0.11 or 5. Page discovery is owned by Vue Router 5's own Vite plugin - this plugin only resolves layouts, so no page-routing plugin is needed alongside it.
Support Policy
Which versions of the peer dependencies a release of this plugin supports follows one rule set, so it does not have to be decided again for each release:
- Vite - the current major and the two previous majors, mirroring Vite's own release policy. Today: Vite 6, 7 and 8.
- Vue Router - the current major and the previous major; for each, only its latest minor is a support target. Today: Vue Router 5.x and 4.6.x. Older 4.x minors still install (the peer range starts at 4.0.11) but are not tested against.
- Vue - no separate floor. Vue has no LTS or backport policy, so the supported Vue range is whatever the oldest supported Vue Router minor requires (Vue Router 4.6 requires Vue 3.5). The peer range currently still allows Vue 3.2+; features that need a newer Vue degrade gracefully there and say so in their docs (see Dynamic Layouts).
- Narrowing any peer range is a breaking change and only happens in a major release, listed under "Removed" in the changelog. The next major is expected to move the floors to Vue Router 4.6 and Vue 3.5.
Adding the Plugin
Add it to your vite.config.ts, after VueRouter():
import Vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'
import Layouts from 'vite-plugin-vue-layouts-next'
import VueRouter from 'vue-router/vite'
export default defineConfig({
plugins: [VueRouter(), Vue(), Layouts()],
})In main.ts, import Vue Router 5's generated file-based routes and wrap them with setupLayouts:
import { setupLayouts } from 'virtual:generated-layouts'
import { createRouter } from 'vue-router'
import { routes } from 'vue-router/auto-routes'
const router = createRouter({
// ...
routes: setupLayouts(routes),
})See the Config Section for the options Layouts() accepts.
Writing a Layout
A layout is a standard Vue component whose template renders a <router-view>:
<template>
<div class="app">
<header>My site</header>
<router-view />
</div>
</template>Every page without an explicit layout renders inside this one.
Choosing a Layout Per Page
A page selects its layout through meta.layout, either in a <route> block:
<route lang="yaml">
meta:
layout: users
</route>or with definePage:
<script setup lang="ts">
definePage({
meta: {
layout: 'users',
},
})
</script>Both look for src/layouts/users.vue. Note that the value is a layout name, not a path - see Layout Names.
Passing Props to a Layout
meta.layout also accepts an object with the layout name and the props to pass to the layout component:
<script setup lang="ts">
definePage({
meta: {
layout: {
name: 'panel',
props: {
sidebar: true,
title: 'Dashboard',
},
},
},
})
</script>The layout receives them through defineProps:
<script setup lang="ts">
defineProps<{
sidebar?: boolean
title?: string
}>()
</script>
<template>
<div class="panel">
<h1>{{ title }}</h1>
<aside v-if="sidebar">
...
</aside>
<router-view />
</div>
</template>- Omit
nameto pass props todefaultLayout;name: falserenders the page without a layout, likelayout: false. - Props a layout does not declare fall through as attributes on its root element.
- A flat
meta.layoutPropsnext to a stringlayoutworks too (layout.propswins when both are set). definePageand<route>blocks are extracted statically, so props must be plain serializable values. To compute them at runtime, use a router guard orsetPageLayout- see Dynamic Layouts.
Client Types
To get type definitions for virtual:generated-layouts, add the client types to your tsconfig:
{
"compilerOptions": {
"types": ["vite-plugin-vue-layouts-next/client"]
}
}Trying It Out
The repository ships four runnable setups covering SPA, SSG, client-side layouts and nested routes. See Examples.