404 Not Found

404 Not Found


nginx

Module Development

Charlie discovered that MegaShop's tracking logic, error reporting, and performance monitoring were scattered across various locations. Alice and Bob's other projects also needed the same capabilities. By encapsulating these common features into Nuxt modules, they could develop them once and reuse them everywhere, and even publish them to npm for the community to use.

1. What You'll Learn


2. A True Story of an Architect

(1) Pain Point: Duplicate Development of Generic Features

Charlie's MegaShop needs tracking—every page view, every item added to the cart, and every click must be logged. He manually calls the API in five components. Alice's other project also needs tracking, so Bob rewrote the code from scratch. The code is repetitive and inconsistent.

(2) Solution for Nuxt Modules

Once packaged as a module, it's ready to use right out of the box—with automatic injection of Composable and the server API:

TYPESCRIPT
// nuxt.config.ts
modules: ['@megashop/analytics']

(3) Benefits: Develop Once, Reuse Everywhere

All projects gain tracking capabilities simply by installing the module. With Alice, you can enable tracking in any project by calling useAnalytics()—no configuration required.


3. Module Architecture

(1) Nuxt Module Lifecycle

100%
graph TB
    A[defineNuxtModule] --> B[Setup Function]
    B --> C[installModule - Dependencies]
    B --> D[addPlugin - Register Plugins]
    B --> E[addComposable - Inject Composables]
    B --> F[addServerHandler - API Routes]
    B --> G[addLayout - Custom Layouts]
    B --> H[addComponent - Auto Components]
    B --> I[extendConfig - Modify Config]
    
    J[Nuxt Hooks] --> K[modules:before]
    J --> L[modules:done]
    J --> M[build:before]
    J --> N[build:done]

(1) ▶ Example: Minimal Module Skeleton

TYPESCRIPT
// src/module.ts
import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  meta: {
    name: '@megashop/analytics',
    configKey: 'analytics',
    compatibility: {
      nuxt: '^3.0.0'
    }
  },

  defaults: {
    enabled: true,
    endpoint: '/api/analytics',
    debug: false
  },

  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)

    // Register plugin
    addPlugin(resolve('./runtime/plugin'))

    // Expose options to runtime
    nuxt.options.runtimeConfig.public.analytics = {
      enabled: options.enabled,
      endpoint: options.endpoint,
      debug: options.debug
    }
  }
})

Output:

TEXT
// Execution Successful

(2) Quick Reference for Module Capabilities

Capability API Description
Register Plugin addPlugin() Automatically execute initialization logic
Inject a component addComponent() Automatically import Vue components
Inject into Composable addImports() Automatically import functions
Add API Route addServerHandler() Automatically Register Server Routes
Add Layout addLayout() Register Custom Layout
Add middleware addRouteMiddleware() Register route middleware
Modify Configuration extendConfig() Modify Nuxt Configuration
Install a dependency module installModule() Install other modules

4. Hands-On Module: @megashop/analytics

(1) ▶ Example: Module Entry Point

TYPESCRIPT
// src/module.ts
import { defineNuxtModule, addPlugin, addImports, addServerHandler, createResolver } from '@nuxt/kit'

export interface ModuleOptions {
  enabled: boolean
  endpoint: string
  debug: boolean
  trackPageViews: boolean
  trackClicks: boolean
}

export default defineNuxtModule<ModuleOptions>({
  meta: {
    name: '@megashop/analytics',
    configKey: 'analytics',
    compatibility: { nuxt: '^3.0.0' }
  },

  defaults: {
    enabled: true,
    endpoint: '/api/analytics',
    debug: false,
    trackPageViews: true,
    trackClicks: true
  },

  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)

    // 1. Register client plugin for auto-tracking
    if (options.trackPageViews || options.trackClicks) {
      addPlugin(resolve('./runtime/plugin.client'))
    }

    // 2. Auto-import useAnalytics composable
    addImports({
      name: 'useAnalytics',
      from: resolve('./runtime/composables/useAnalytics')
    })

    // 3. Add server API for receiving events
    addServerHandler({
      method: 'post',
      route: options.endpoint,
      handler: resolve('./runtime/server/api/analytics')
    })

    // 4. Expose config to runtime
    nuxt.options.runtimeConfig.public.analytics = {
      enabled: options.enabled,
      endpoint: options.endpoint,
      debug: options.debug
    }
  }
})

Output:

TEXT
// Execution Successful

(2) ▶ Example: Runtime Plugin

TYPESCRIPT
// src/runtime/plugin.client.ts
import { defineNuxtPlugin } from '#app'

export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig().public.analytics
  if (!config.enabled) return

  // Auto-track page views
  if (config.trackPageViews) {
    nuxtApp.hook('page:finish', () => {
      useAnalytics().trackPageView(window.location.pathname)
    })
  }

  // Auto-track clicks on data-track elements
  if (config.trackClicks) {
    document.addEventListener('click', (e) => {
      const target = (e.target as HTMLElement).closest('[data-track]')
      if (target) {
        const event = target.getAttribute('data-track') || 'click'
        useAnalytics().track(event, { element: target.tagName })
      }
    })
  }
})

Output:

TEXT
// Execution Successful

(3) ▶ Example: Runtime Composable

TYPESCRIPT
// src/runtime/composables/useAnalytics.ts
export function useAnalytics() {
  const config = useRuntimeConfig().public.analytics

  async function track(event: string, data?: Record<string, any>) {
    if (!config.enabled) return
    if (config.debug) console.log('[Analytics]', event, data)

    await $fetch(config.endpoint, {
      method: 'POST',
      body: { event, data, timestamp: Date.now(), url: import.meta.client ? window.location.href : '' }
    })
  }

  function trackPageView(path: string) {
    track('page_view', { path })
  }

  function trackAddToCart(productId: number, productName: string, price: number) {
    track('add_to_cart', { productId, productName, price, currency: 'USD' })
  }

  function trackPurchase(orderId: string, total: number) {
    track('purchase', { orderId, total, currency: 'USD' })
  }

  return { track, trackPageView, trackAddToCart, trackPurchase }
}

Output:

TEXT
// Execution Successful

(4) ▶ Example: Runtime Server API

TYPESCRIPT
// src/runtime/server/api/analytics.ts
import { defineEventHandler, readBody, setHeader } from 'h3'

export default defineEventHandler(async (event) => {
  const body = await readBody(event)

  // Validate required fields
  if (!body.event) {
    throw createError({ statusCode: 400, message: 'Event name required' })
  }

  // Store event (in production: send to analytics service)
  const storage = useStorage('analytics')
  const key = `event:${Date.now()}:${Math.random().toString(36).slice(2)}`
  await storage.setItem(key, {
    event: body.event,
    data: body.data || {},
    timestamp: body.timestamp || Date.now(),
    url: body.url,
    userAgent: getHeader(event, 'user-agent')
  })

  setHeader(event, 'cache-control', 'no-store')
  return { success: true }
})

Output:

TEXT
// Execution Successful

5. Module Release

(1) ▶ Example: package.json configuration

JSON
{
  "name": "@megashop/analytics",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/module.mjs",
  "types": "./dist/types.d.ts",
  "exports": {
    ".": {
      "import": "./dist/module.mjs",
      "require": "./dist/module.cjs",
      "types": "./dist/types.d.ts"
    },
    "./runtime/*": "./dist/runtime/*"
  },
  "files": ["dist"],
  "scripts": {
    "build": "nuxt-module-build",
    "dev": "nuxt-module-build --stub",
    "test": "vitest run",
    "prepublishOnly": "npm run build"
  },
  "peerDependencies": {
    "nuxt": "^3.0.0"
  },
  "devDependencies": {
    "@nuxt/module-builder": "^0.6.0",
    "@nuxt/test-utils": "^3.0.0",
    "nuxt": "^3.12.0"
  }
}

Output:

JSON
{
  "name": "@megashop/analytics",
  "version": "1.0.0",
  "type": "module",
  "main": "./dist/module.mjs",
  "types": "./dist/types.d.ts",
  "exports": {
    ".": {
      "import": "./dist/module.mjs",
      "require": "./dist/module.cjs",
      "types": "./dist/types.d.ts"
    },
    "./runtime/*": "./dist/runtime/*"
  },
  "files": [
    "dist"
  ],
  "scripts": {
    "build": "nuxt-module-build",
    "dev": "nuxt-module-build --stub",
    "test": "vitest run",
    "prepublishOnly": "npm run 

(2) ▶ Example: Using it in a project

TYPESCRIPT
// nuxt.config.ts of MegaShop
export default defineNuxtConfig({
  modules: [
    // Local module during development
    '~/modules/analytics',
    // Published module in production
    // '@megashop/analytics'
  ],

  analytics: {
    enabled: true,
    endpoint: '/api/analytics',
    debug: process.env.NODE_ENV === 'development',
    trackPageViews: true,
    trackClicks: true
  }
})

Output:

TEXT
// Execution Successful

6. Module Testing

(1) ▶ Example: Module Test Fixture

TYPESCRIPT
// test/module.test.ts
import { setupTest } from '@nuxt/test-utils'

describe('@megashop/analytics module', () => {
  setupTest({
    fixture: './test/fixtures/basic',
    build: true
  })

  test('registers analytics plugin', () => {
    // Plugin auto-registers, check Nuxt plugins
    const nuxt = useNuxt()
    const hasPlugin = nuxt.options.plugins.some(p => p.src?.includes('analytics'))
    expect(hasPlugin).toBe(true)
  })

  test('exposes useAnalytics composable', async () => {
    // Auto-import available
    const { data } = await useFetch('/api/analytics', {
      method: 'POST',
      body: { event: 'test', data: {} }
    })
    expect(data.value).toBeDefined()
  })

  test('analytics endpoint accepts events', async () => {
    const response = await $fetch('/api/analytics', {
      method: 'POST',
      body: { event: 'page_view', data: { path: '/' } }
    })
    expect(response.success).toBe(true)
  })
})

Output:

TEXT
// Execution Successful

7. Comprehensive Example: Using @megashop/analytics

VUE
<!-- pages/products/[id].vue - Using analytics module -->
<template>
  <div v-if="product">
    <h1>{{ product.name }}</h1>
    <button
      @click="handleAddToCart"
      data-track="add_to_cart"
    >
      Add to Cart
    </button>
  </div>
</template>

<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)

// useAnalytics auto-imported by module
const { trackAddToCart, trackPageView } = useAnalytics()

// Manual tracking
onMounted(() => {
  trackPageView(`/products/${route.params.id}`)
})

async function handleAddToCart() {
  if (!product.value) return
  trackAddToCart(product.value.id, product.value.name, product.value.price)
  // ... add to cart logic
}
</script>

❓ FAQ

Q What is the difference between modules and plugins?
A Modules are executed during the build phase (configuration phase) and can inject plugins, components, Composables, and routes. Plugins are executed at runtime (when the app starts) and handle initialization logic. Modules can contain plugins.
Q What is the runtime directory in the module?
A runtime contains runtime code—code that is executed only when the application is running and is not included in the build configuration. Plugins, Composables, and server handlers are all placed under runtime/ and are not processed by Nuxt Kit.
Q How do I develop and debug local modules?
A Create a module in the project's modules/ directory and reference it in nuxt.config.ts ~/modules/xxx. During development, use nuxt-module-build --stub to generate symbolic links; code changes will take effect in real time.
Q Can a module depend on other modules?
A Yes. Use installModule('@pinia/nuxt') in the setup to install dependent modules. Nuxt will automatically remove duplicates.
Q What do I need to publish to npm?
A Configure exports and types in package.json, build using @nuxt/module-builder, and run npm publish. We recommend using GitHub Actions for automated publishing.
Q How do I export TypeScript types for a module?
A @nuxt/module-builder automatically generates type declarations. Set exports.types in package.json to point to dist/types.d.ts, and users will automatically receive type hints after installation.

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty: ⭐): Create a minimal module skeleton, register a plugin, and output "Module loaded"
  2. Advanced Exercise (Difficulty: ⭐⭐): Develop the @megashop/analytics module to implement the useAnalytics Composable and server API
  3. Challenge (Difficulty: ⭐⭐⭐): Add automatic page view tracking and data-track click tracking, then write unit tests to verify the functionality

---|

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏