Middleware and Plugins in Nuxt 4: Full Control
Learn to use middleware and plugins in Nuxt 4 to control navigation, authentication, and extend your application functionality.
Written by Francisco Zapata
Middleware and plugins are two fundamental mechanisms in Nuxt 4 for intercepting and extending your application's behavior. Middleware controls navigation between routes, while plugins allow you to inject global functionality. Mastering both gives you full control over your application flow.
Route Middleware
Route middleware execute before navigating to a route. They are ideal for authentication, authorization, and redirects.
Types of Middleware
Nuxt 4 supports three types of route middleware:
1. Inline (Anonymous) Middleware
Defined directly in the page:
<script setup>
definePageMeta({
middleware: [
function (to, from) {
const auth = useAuth()
if (!auth.isAuthenticated.value) {
return navigateTo('/login')
}
}
]
})
</script>
2. Named Middleware
Files in app/middleware/ assigned to specific pages:
// app/middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
const { isAuthenticated } = useAuth()
if (!isAuthenticated.value) {
return navigateTo('/login', {
redirectCode: 302
})
}
})
Assignment in the page:
<script setup>
definePageMeta({
middleware: ['auth']
})
</script>
3. Global Middleware
Files with .global.ts suffix that run on ALL routes:
// app/middleware/01.logger.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
console.log(`Navigating from ${from.path} to ${to.path}`)
})
Execution Order
1. Global middleware (sorted alphabetically by filename)
2. Page middleware (in the order defined in the array)
That is why it is useful to prefix with numbers: 01.logger.global.ts, 02.auth.global.ts.
Practical Examples
Role Middleware
// app/middleware/role.ts
export default defineNuxtRouteMiddleware((to) => {
const { user } = useAuth()
const requiredRole = to.meta.requiredRole as string
if (requiredRole && user.value?.role !== requiredRole) {
return navigateTo('/unauthorized')
}
})
<script setup>
definePageMeta({
middleware: ['auth', 'role'],
requiredRole: 'admin'
})
</script>
Language Redirect Middleware
// app/middleware/i18n-redirect.global.ts
export default defineNuxtRouteMiddleware((to) => {
const preferredLocale = useCookie('locale')
if (!preferredLocale.value) {
const browserLang = useRequestHeaders()['accept-language']
preferredLocale.value = browserLang?.startsWith('es') ? 'es' : 'en'
}
})
Maintenance Middleware
// app/middleware/maintenance.global.ts
export default defineNuxtRouteMiddleware((to) => {
const config = useRuntimeConfig()
if (config.public.maintenanceMode && to.path !== '/maintenance') {
return navigateTo('/maintenance')
}
})
Server Middleware
Server middleware intercepts HTTP requests in the Nitro server. They are different from route middleware:
// server/middleware/cors.ts
export default defineEventHandler((event) => {
setResponseHeaders(event, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
})
if (getMethod(event) === 'OPTIONS') {
setResponseStatus(event, 204)
return ''
}
})
// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
const url = getRequestURL(event)
if (!url.pathname.startsWith('/api/protected')) return
const token = getHeader(event, 'authorization')?.replace('Bearer ', '')
if (!token) {
throw createError({
statusCode: 401,
message: 'Authentication token required'
})
}
const user = await verifyToken(token)
event.context.user = user
})
Plugins
Plugins in Nuxt 4 allow you to extend application functionality, register global components, and provide helpers.
Creating Plugins
// app/plugins/analytics.client.ts
export default defineNuxtPlugin((nuxtApp) => {
const analytics = {
track(event: string, data?: Record<string, unknown>) {
console.log('Track:', event, data)
},
page(name: string) {
console.log('Page view:', name)
}
}
nuxtApp.hook('page:finish', () => {
analytics.page(useRoute().fullPath)
})
return {
provide: {
analytics
}
}
})
Usage in components:
<script setup>
const { $analytics } = useNuxtApp()
function handleClick() {
$analytics.track('button_clicked', { section: 'hero' })
}
</script>
Plugin Suffixes
.client.ts: Only runs in the browser.server.ts: Only runs on the server- No suffix: Runs in both environments
API Client Plugin
// app/plugins/api.ts
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
const api = $fetch.create({
baseURL: config.public.apiBase,
onRequest({ options }) {
const token = useCookie('auth_token')
if (token.value) {
options.headers = {
...options.headers,
Authorization: `Bearer ${token.value}`
}
}
},
onResponseError({ response }) {
if (response.status === 401) {
navigateTo('/login')
}
}
})
return { provide: { api } }
})
Plugin Execution Order
Plugins execute in alphabetical order by default. To control the order, use the dependency system:
// app/plugins/02.auth.ts
export default defineNuxtPlugin({
name: 'auth',
dependsOn: ['api'],
async setup(nuxtApp) {
const { $api } = nuxtApp
}
})
Or use numeric prefixes: 01.api.ts, 02.auth.ts.
Lifecycle Hooks
Both middleware and plugins can use Nuxt hooks:
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('app:created', () => {
console.log('App created')
})
nuxtApp.hook('page:start', () => {
console.log('Navigation started')
})
nuxtApp.hook('page:finish', () => {
console.log('Navigation completed')
})
nuxtApp.hook('app:error', (error) => {
console.error('App error:', error)
})
})
Best Practices
1. Keep middleware lightweight: Avoid heavy logic that delays navigation.
2. Use named middleware for reusable logic, inline for page-specific logic.
3. Separate client/server plugins with appropriate suffixes.
4. Do not fetch in middleware: Use useFetch or useAsyncData in the page.
5. Handle errors: Always catch exceptions in plugins and middleware.
Conclusion
Middleware and plugins are complementary tools that give you full control over your Nuxt 4 application. Middleware protects and controls navigation, while plugins extend global functionality. Used correctly, they create a clean and maintainable architecture.
Comments (0)
Leave a comment
Be the first to comment