https://github.com/loicduong/vite-plugin-vue-layouts-next
Vue layout plugin for Vite, supports the Vite 7 and Vue 3.
https://github.com/loicduong/vite-plugin-vue-layouts-next
typescript vite-plugin vite7 vue-router vue3
Last synced: 7 months ago
JSON representation
Vue layout plugin for Vite, supports the Vite 7 and Vue 3.
- Host: GitHub
- URL: https://github.com/loicduong/vite-plugin-vue-layouts-next
- Owner: loicduong
- License: mit
- Created: 2025-02-25T02:48:51.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-11-17T15:35:22.000Z (8 months ago)
- Last Synced: 2025-11-17T16:20:43.903Z (8 months ago)
- Topics: typescript, vite-plugin, vite7, vue-router, vue3
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/vite-plugin-vue-layouts-next
- Size: 1.9 MB
- Stars: 50
- Watchers: 1
- Forks: 2
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# vite-plugin-vue-layouts-next
[![npm version][npm-badge]][npm]
[![monthly downloads][monthly-downloads-badge]][monthly-downloads]
[![Keep a Changelog v1.1.0 badge][changelog-badge]][changelog]
[![standard-readme compliant][standard-readme-badge]][standard-readme]
Router based layout plugin for Vite 7 and Vue 3.
A fork of [vite-plugin-vue-layouts](https://github.com/JohnCampionJr/vite-plugin-vue-layouts) with some improvements and fixes, supports Vite 7 and Vue 3.
This works best along with the [vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages).
Layouts are stored in the `/src/layouts` folder by default and are standard Vue components with a `` in the template.
Pages without a layout specified use `default.vue` for their layout.
You can use route blocks to allow each page to determine its layout. The block below in a page will look for `/src/layouts/users.vue` for its layout.
```html
meta:
layout: users
```
## Table of Contents
- [Install](#install)
- [Usage](#usage)
- [API](#api)
- [How it works](#how-it-works)
- [Common patterns](#common-patterns)
- [ClientSideLayout](#clientsidelayout)
- [Contributing](#contributing)
- [License](#license)
## Install
```bash
# npm
npm install -D vite-plugin-vue-layouts-next
# yarn
yarn add -D vite-plugin-vue-layouts-next
# pnpm
pnpm add -D vite-plugin-vue-layouts-next
```
## Usage
Add to your `vite.config.ts`:
```js
import Vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'
import Pages from 'vite-plugin-pages'
import Layouts from 'vite-plugin-vue-layouts-next'
export default defineConfig({
plugins: [Vue(), Pages(), Layouts()],
})
```
In main.ts, you need to add a few lines to import the generated code and setup the layouts.
### vue-router
```js
import { setupLayouts } from 'virtual:generated-layouts'
import { createRouter } from 'vue-router'
import generatedRoutes from '~pages'
const routes = setupLayouts(generatedRoutes)
const router = createRouter({
// ...
routes,
})
```
### unplugin-vue-router
```js
import { setupLayouts } from 'virtual:generated-layouts'
import { createRouter } from 'vue-router'
import { routes } from 'vue-router/auto-routes'
const router = createRouter({
// ...
routes: setupLayouts(routes),
})
```
### Client Types
If you want type definition of `virtual:generated-layouts`, add `vite-plugin-vue-layouts-next/client` to `compilerOptions.types` of your `tsconfig`:
```json
{
"compilerOptions": {
"types": ["vite-plugin-vue-layouts-next/client"]
}
}
```
## API
```ts
interface UserOptions {
layoutsDirs?: string | string[]
pagesDirs?: string | string[] | null
extensions?: string[]
exclude?: string[]
defaultLayout?: string
importMode?: (name: string) => 'sync' | 'async'
inheritDefaultLayout?: boolean
}
```
### Using configuration
To use custom configuration, pass your options to Layouts when instantiating the plugin:
```js
// vite.config.ts
import { defineConfig } from 'vite'
import Layouts from 'vite-plugin-vue-layouts-next'
export default defineConfig({
plugins: [
Layouts({
layoutsDirs: 'src/mylayouts',
pagesDirs: 'src/pages',
defaultLayout: 'myDefault'
}),
],
})
```
### layoutsDirs
Relative path to the layouts directory. Supports globs.
All .vue files in this folder are imported async into the generated code.
Can also be an array of layout dirs
Can use `**` to support scenarios like `module1/layouts` and `modules2/layouts` with a setting of `src/**/layouts`
Any files named `__*__.vue` will be excluded, and you can specify any additional exclusions with the `exclude` option
**Default:** `'src/layouts'`
### pagesDirs
Defines the pages dir to avoid HMR reloading for all added or deleted files anywhere in the project.
Relative path to the pages directory. If you want it to watch for all files, like in v0.8.0 or earlier, set to null.
Can also be an array of layout dirs or use `**` glob patterns
**Default:** `'src/pages'`
### extensions
Valid file extensions for page components.
**Default:** `['vue']`
### exclude
List of path globs to exclude when resolving pages.
### defaultLayout
Filename of default layout (".vue" is not needed).
**Default:** `'default'`
### importMode
Mode for importing layouts.
**Default:** ssg is `'sync'`, other is `'async'`
### inheritDefaultLayout
Whether nested routes should inherit the default layout from parent routes. When `false`, if a child route has its own layout, the parent route won't use the default layout. This prevents double-wrapping layouts when child routes specify their own layout. This option only works with [unplugin-vue-router](https://github.com/posva/unplugin-vue-router). It has no effect when using [vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages) because `vite-plugin-pages` generates flat route structures without nested parent-child relationships, while `unplugin-vue-router` generates nested route structures with `children` arrays. This option can only be set globally in the plugin configuration.
**Default:** `true`
## How it works
`setupLayouts` transforms the original `router` by
1. Replacing every page with its specified layout
2. Appending the original page in the `children` property.
Simply put, layouts are [nested routes](https://next.router.vuejs.org/guide/essentials/nested-routes.html#nested-routes) with the same path.
Before:
```text
router: [ page1, page2, page3 ]
```
After `setupLayouts()`:
```text
router: [
layoutA: page1,
layoutB: page2,
layoutA: page3,
]
```
That means you have the full flexibility of the [vue-router API](https://next.router.vuejs.org/api/) at your disposal.
## Common patterns
### Transitions
Layouts and Transitions work as expected and explained in the [vue-router docs](https://next.router.vuejs.org/guide/advanced/transitions.html) only as long as `Component` changes on each route. So if you want a transition between pages with the same layout *and* a different layout, you have to mutate `:key` on `` (for a detailed example, see the vue docs about [transitions between elements](https://v3.vuejs.org/guide/transitions-enterleave.html#transitioning-between-elements)).
`App.vue`
```html
```
Now Vue will always trigger a transition if you change the route.
### Data from layout to page
If you want to send data *down* from the layout to the page, use props
```html
```
### Set static data at the page
If you want to set state in your page and do something with it in your layout, add additional properties to a route's `meta` property. Doing so only works if you know the state at build-time.
You can use the `` block if you work with [vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages).
In `page.vue`:
```html
Content
meta:
layout: default
bgColor: yellow
```
Now you can read `bgColor` in `layout.vue`:
```html
import { useRouter } from 'vue-router'
```
### Data dynamically from page to layout
If you need to set `bgColor` dynamically at run-time, you can use [custom events](https://v3.vuejs.org/guide/component-custom-events.html#custom-events).
Emit the event in `page.vue`:
```html
import { defineEmit } from 'vue'
const emit = defineEmit(['setColor'])
if (2 + 2 === 4)
emit('setColor', 'green')
else
emit('setColor', 'red')
```
Listen for `setColor` custom-event in `layout.vue`:
```html
import { ref } from 'vue'
const bgColor = ref('yellow')
const setBg = (color) => {
bgColor.value = color
}
```
## ClientSideLayout
The clientSideLayout uses a simpler [virtual file](https://vitejs.dev/guide/api-plugin.html#importing-a-virtual-file) + [glob import](https://vitejs.dev/guide/features.html#glob-import) scheme, This means that its hmr is faster and more accurate, but also more limited
### Usage
```js
// vite.config.ts
import { defineConfig } from 'vite'
import { ClientSideLayout } from 'vite-plugin-vue-layouts-next'
export default defineConfig({
plugins: [
ClientSideLayout({
layoutsDir: 'src/mylayouts', // default to 'src/layouts'
defaultLayout: 'myDefault', // default to 'default', no need '.vue'
importMode: 'sync' // The default will automatically detect -> ssg is sync,other is async
}),
],
})
```
## Contributing
PRs accepted.
Open an issue or submit PRs for any improvements.
## License
MIT © loicduong
[npm]: https://www.npmjs.com/package/vite-plugin-vue-layouts-next
[npm-badge]: https://img.shields.io/npm/v/vite-plugin-vue-layouts-next
[monthly-downloads]: https://npmjs.com/package/vite-plugin-vue-layouts-next?activeTab=versions
[monthly-downloads-badge]: https://img.shields.io/npm/dm/vite-plugin-vue-layouts-next
[changelog]: ./CHANGELOG.md
[changelog-badge]: https://img.shields.io/badge/changelog-Keep%20a%20Changelog%20v1.1.0-%23E05735
[standard-readme]: https://github.com/RichardLitt/standard-readme
[standard-readme-badge]: https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square