Router based layouts for Vue 3 applications using Vite.
- π File Based Layouts are stored in
/src/layouts
as standard Vue components - π Sensible Defaults Pages without a layout use
default.vue
automatically - π Multiple Layouts Support for multiple layout directories
- π¨ Meta Configuration Specify layouts via route blocks in your pages
- π Router Integration Pairs with
unplugin-vue-router
- π± HMR Optimized Client-side layout mode for faster HMR
- π οΈ Flexible Configuration Customize layout directories, exclusions, and more
npm install -D vite-plugin-layouts
# or
yarn add -D vite-plugin-layouts
# or
pnpm add -D vite-plugin-layouts
# or
bun add -D vite-plugin-layouts
// vite.config.ts
import Vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'
import Layouts from 'vite-plugin-layouts'
import Pages from 'vite-plugin-pages'
export default defineConfig({
plugins: [
Vue(),
Pages(),
Layouts()
]
})
Then in your main.ts file:
// main.ts
import { setupLayouts } from 'virtual:generated-layouts'
import { createRouter } from 'vue-router'
import generatedRoutes from '~pages'
const routes = setupLayouts(generatedRoutes)
const router = createRouter({
// ...
routes,
})
If you're using unplugin-vue-router
:
// main.ts
import { setupLayouts } from 'virtual:generated-layouts'
import { createRouter } from 'vue-router/auto'
const router = createRouter({
// ...
extendRoutes: routes => setupLayouts(routes),
})
If you want type definition for the virtual modules, add the following to your tsconfig.json
:
{
"compilerOptions": {
"types": ["vite-plugin-layouts/client"]
}
}
Layouts are stored in the /src/layouts
folder by default and are standard Vue components with a <router-view></router-view>
in the template.
You can specify which layout to use for a page by using a route block:
<route lang="yaml">
meta:
layout: users
</route>
This will look for /src/layouts/users.vue
for the page's layout. If no layout is specified, it will use default.vue
.
// vite.config.ts
import Vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'
import Layouts from 'vite-plugin-layouts'
import Pages from 'vite-plugin-pages'
export default defineConfig({
plugins: [
Vue(),
Pages(),
Layouts({
layoutsDirs: 'src/layouts', // default: 'src/layouts'
pagesDirs: 'src/pages', // default: 'src/pages'
defaultLayout: 'default', // default: 'default'
exclude: [], // Patterns to exclude from layout loading
})
]
})
Option | Type | Default | Description |
---|---|---|---|
layoutsDirs |
string | string[] |
'src/layouts' |
Path(s) to the layouts directory. Supports globs. |
pagesDirs |
string | string[] | null |
'src/pages' |
Path(s) to the pages directory. Set to null to watch all files. |
defaultLayout |
string |
'default' |
Name of the default layout to use when none is specified. |
exclude |
string[] |
[] |
Patterns to exclude from layout loading. Files named __*__.vue are automatically excluded. |
For faster HMR and more efficient loading, you can use the ClientSideLayout mode:
// vite.config.ts
import Vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'
import { ClientSideLayout } from 'vite-plugin-layouts'
import Pages from 'vite-plugin-pages'
export default defineConfig({
plugins: [
Vue(),
Pages(),
ClientSideLayout({
layoutsDir: 'src/layouts', // default: 'src/layouts'
defaultLayout: 'default', // default: 'default'
importMode: 'sync' // Auto-detect: 'sync' for SSG, 'async' for others
})
]
})
The setupLayouts
function transforms your routes by:
- Replacing pages with their specified layouts
- Making the original pages children of their layouts
This creates nested routes with the same paths, giving you full access to the vue-router API.
To add transitions between routes, including when using the same layout:
<!-- App.vue -->
<template>
<router-view v-slot="{ Component, route }">
<transition name="slide">
<component :is="Component" :key="route" />
</transition>
</router-view>
</template>
Use props to pass data down from layout to page:
<router-view foo="bar" />
Use the route's meta property in your page:
<!-- page.vue -->
<template>
<div>Content</div>
</template>
<route lang="yaml">
meta:
layout: default
bgColor: yellow
</route>
Then in your layout:
<!-- layout.vue -->
<script setup>
import { useRouter } from 'vue-router'
</script>
<template>
<div :style="`background: ${useRouter().currentRoute.value.meta.bgColor};`">
<router-view />
</div>
</template>
Use custom events to pass data from page to layout:
<!-- page.vue -->
<script setup>
import { defineEmits } from 'vue'
const emit = defineEmits(['setColor'])
if (2 + 2 === 4)
emit('setColor', 'green')
else
emit('setColor', 'red')
</script>
Listen for the events in your layout:
<!-- layout.vue -->
<script setup>
import { ref } from 'vue'
const bgColor = ref('yellow')
function setBg(color) {
bgColor.value = color
}
</script>
<template>
<main :style="`background: ${bgColor};`">
<router-view @set-color="setBg" />
</main>
</template>
bun test
Please see our releases page for more information on what has changed recently.
Please review the Contributing Guide for details.
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
For casual chit-chat with others using this package:
Join the Stacks Discord Server
"Software that is free, but hopes for a postcard." We love receiving postcards from around the world showing where vite-plugin-layouts
is being used! We showcase them on our website too.
Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States π
We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.
- JohnCampionJr - Creator of original vite-plugin-vue-layouts
- Chris Breuer
- All Contributors
The MIT License (MIT). Please see LICENSE for more information.
Made with π