Nuxt.js Design Patterns

Essential patterns for building scalable, maintainable Nuxt applications

Nuxt 3 Vue.js Best Practices Architecture

Introduction to Nuxt Design Patterns

Design patterns in Nuxt.js provide proven solutions to common problems in web application development. They help create applications that are:

  • Maintainable: Easy to understand and modify
  • Scalable: Can grow with your application
  • Performant: Optimized for speed and efficiency
  • Consistent: Follow standardized approaches

This guide covers the most essential Nuxt.js design patterns with practical examples you can use in your projects today.

1. Layout Pattern

Purpose

Create consistent page layouts with reusable templates

Implementation

<!-- layouts/default.vue -->
<template>
  <div>
    <AppHeader />
    <NuxtPage />
    <AppFooter />
  </div>
</template>

<!-- pages/index.vue -->
<script setup>
definePageMeta({
  layout: 'default'
})
</script>

Variations

  • Authentication layouts (different for logged-in/logged-out users)
  • Admin vs public layouts
  • Error page layouts

2. Component Composition Pattern

Purpose

Build complex UIs from small, reusable components

Implementation

<!-- components/FeatureCard.vue -->
<template>
  <div class="feature-card">
    <Icon :name="icon" />
    <h3>{{ title }}</h3>
    <p>{{ description }}</p>
  </div>
</template>

<!-- pages/index.vue -->
<template>
  <div>
    <FeatureCard 
      v-for="feature in features"
      :key="feature.id"
      v-bind="feature"
    />
  </div>
</template>

Benefits

  • Reusable UI elements
  • Better code organization
  • Easier maintenance

3. Composables Pattern (Hooks)

Purpose

Reuse stateful logic across components

Implementation

// composables/useCounter.ts
export const useCounter = () => {
  const count = ref(0)
  
  const increment = () => count.value++
  const decrement = () => count.value--
  
  return { count, increment, decrement }
}

// components/Counter.vue
<script setup>
const { count, increment } = useCounter()
</script>

Common Use Cases

  • API fetching
  • Form handling
  • Authentication
  • Animations

4. State Management Patterns

Pinia (Recommended)

// stores/counter.ts
export const useCounterStore = defineStore('counter', {
  state: () => ({ count: 0 }),
  actions: {
    increment() {
      this.count++
    }
  }
})

// components/Counter.vue
<script setup>
const counter = useCounterStore()
</script>

Provide/Inject (For Component Trees)

// parent component
const user = ref({ name: 'John' })
provide('user', user)

// child component
const user = inject('user')

5. Route Middleware Pattern

Purpose

Handle navigation guards and route protection

Implementation

// middleware/auth.ts
export default defineNuxtRouteMiddleware((to) => {
  const { isAuthenticated } = useAuth()
  
  if (!isAuthenticated && to.path !== '/login') {
    return navigateTo('/login')
  }
})

// pages/dashboard.vue
definePageMeta({
  middleware: 'auth'
})

Types

  • Global middleware (runs on every route)
  • Named middleware (specific to pages)
  • Inline middleware (defined in page components)

6. Plugin Pattern

Purpose

Extend Nuxt functionality or add global features

Implementation

// plugins/hello.ts
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.provide('hello', (name: string) => `Hello ${name}!`)
})

// components/MyComponent.vue
<script setup>
const { $hello } = useNuxtApp()
console.log($hello('World')) // "Hello World!"
</script>

Common Plugin Uses

  • Adding global components
  • Integrating third-party libraries
  • Setting up analytics
  • Custom directives

7. Dynamic Component Loading

Purpose

Improve performance by lazy loading components

Implementation

<script setup>
const MyComponent = defineAsyncComponent(() =>
  import('./MyComponent.vue')
)
</script>

<template>
  <Suspense>
    <MyComponent />
    <template #fallback>
      <LoadingSpinner />
    </template>
  </Suspense>
</template>

8. Server API Pattern

Purpose

Create server-side API endpoints

Implementation

// server/api/hello.ts
export default defineEventHandler((event) => {
  return { message: 'Hello World' }
})

// Client-side usage
const { data } = await useFetch('/api/hello')

Advanced Features

  • Route parameters
  • Middleware
  • Request validation
  • Caching

9. Error Handling Pattern

Purpose

Centralize error handling

Implementation

// plugins/error-handler.ts
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('vue:error', (error) => {
    // Send to error tracking service
    console.error('Vue error:', error)
  })
  
  nuxtApp.hook('app:error', (error) => {
    // Handle app errors
    console.error('App error:', error)
  })
})

10. SEO & Meta Pattern

Purpose

Manage head tags and SEO metadata

Implementation

// composables/useSeo.ts
export const useSeo = (title, description) => {
  useHead({
    title,
    meta: [
      { name: 'description', content: description },
      { property: 'og:title', content: title }
    ]
  })
}

// pages/index.vue
<script setup>
useSeo('Home Page', 'Welcome to our website')
</script>

11. Dependency Injection Pattern

Purpose

Share services across components

Implementation

// plugins/api.ts
export default defineNuxtPlugin(() => {
  const api = {
    fetchPosts: () => $fetch('/api/posts')
  }
  
  return {
    provide: { api }
  }
})

// components/Posts.vue
<script setup>
const { $api } = useNuxtApp()
const posts = await $api.fetchPosts()
</script>

12. Module Pattern

Purpose

Extend Nuxt core functionality

Implementation

// modules/my-module.ts
export default defineNuxtModule({
  meta: {
    name: 'my-module'
  },
  setup(options, nuxt) {
    // Add custom logic here
  }
})

Common Module Uses

  • Adding UI libraries
  • Integrating databases
  • Setting up authentication
  • Custom build optimizations

Conclusion

Nuxt.js design patterns provide structured approaches to solving common problems in web application development. By applying these patterns, you can create applications that are:

  • More maintainable: Clear structure makes code easier to understand
  • Easier to scale: Patterns accommodate growth
  • More performant: Optimized approaches improve speed
  • More consistent: Standardized solutions across your codebase

Pro Tip: Start with the patterns that address your immediate needs, then gradually incorporate others as your application grows in complexity.

Pattern Cheat Sheet

Pattern Best For Complexity
Layout Page structure Low
Composables Reusable logic Medium
Pinia Global state Medium
Middleware Route protection Low
Plugins Global features Medium
Dynamic Import Performance Medium
Server API Backend logic High
Error Handling Debugging Medium
SEO Search optimization Low
DI Service sharing High
Modules Nuxt extensions High