The Problem
In Nuxt, layouts are typically set in page components or via middleware. But what if you want to set layouts based on route rules for more dynamic control?
The Solution
Here's a custom Nuxt module that allows setting layouts directly from route rules:
import { defu } from 'defu'
import { defineNuxtModule } from 'nuxt/kit'
import { createRouter as createRadixRouter, toRouteMatcher } from 'radix3'
const declaration = `
import type { LayoutKey } from '#build/types/layouts'
declare module "nitropack" {
interface NitroRouteConfig {
layout?: LayoutKey | false
}
}`
export default defineNuxtModule({
meta: { name: 'route-rules-layout' },
setup(_, nuxt) {
const getRules = (url: string) => {
const _routeRulesMatcher = toRouteMatcher(
createRadixRouter({ routes: nuxt.options.routeRules })
)
return defu({}, ..._routeRulesMatcher.matchAll(url).reverse())
}
nuxt.hook('prepare:types', ({ declarations }) => {
declarations.push(declaration)
})
nuxt.hook('pages:extend', (pages) => {
pages.forEach((page) => {
const rules = getRules(page.path)
if (rules?.layout) {
page.meta ||= {}
page.meta.layout ??= rules.layout
}
})
})
},
})
How to Use
- Create a new file in your Nuxt project, e.g.,
modules/route-rules-layout.ts
. - Paste the above code into this file.
- In your
nuxt.config.ts
, add:
export default defineNuxtConfig({
routeRules: {
'/admin/**': { layout: 'admin' },
'/blog/**': { layout: 'blog' },
// ... other route rules
}
})
Now, routes matching /admin/**
will use the admin
layout, and those matching /blog/**
will use the blog
layout.
Considerations
- Test performance with large numbers of routes
- Be aware of conflicts with layouts set in page components
- Document this approach for your team
This recipe offers a flexible way to manage layouts in Nuxt, especially useful for projects with complex routing needs.