# Source: https://nuxt.com/raw/docs/3.x/guide/modules/recipes-advanced.md

# Source: https://nuxt.com/raw/docs/4.x/guide/modules/recipes-advanced.md

# Use Hooks & Extend Types

> Master lifecycle hooks, virtual files and TypeScript declarations in your modules.

Here are some advanced patterns for authoring modules, including hooks, templates, and type augmentation.

## Use Lifecycle Hooks

[Lifecycle hooks](/docs/4.x/guide/going-further/hooks) allow you to expand almost every aspect of Nuxt. Modules can hook to them programmatically or through the `hooks` map in their definition.

```js
import { addPlugin, createResolver, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  // Hook to the `app:error` hook through the `hooks` map
  hooks: {
    'app:error': (err) => {
      console.info(`This error happened: ${err}`)
    },
  },
  setup (options, nuxt) {
    // Programmatically hook to the `pages:extend` hook
    nuxt.hook('pages:extend', (pages) => {
      console.info(`Discovered ${pages.length} pages`)
    })
  },
})
```

<read-more to="/docs/4.x/api/advanced/hooks">



</read-more>

<tip icon="i-lucide-video" target="_blank" to="https://vueschool.io/lessons/nuxt-lifecycle-hooks?friend=nuxt">

Watch Vue School video about using Nuxt lifecycle hooks in modules.

</tip>

<note>

**Module cleanup**

<br />

<br />

If your module opens, handles, or starts a watcher, you should close it when the Nuxt lifecycle is done. The `close` hook is available for this.

```ts
import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    nuxt.hook('close', async (nuxt) => {
      // Your custom code here
    })
  },
})
```

</note>

### Create Custom Hooks

Modules can also define and call their own hooks, which is a powerful pattern for making your module extensible.

If you expect other modules to be able to subscribe to your module's hooks, you should call them in the `modules:done` hook. This ensures that all other modules have had a chance to be set up and register their listeners to your hook during their own `setup` function.

```ts
// my-module/module.ts
import { defineNuxtModule } from '@nuxt/kit'

export interface ModuleHooks {
  'my-module:custom-hook': (payload: { foo: string }) => void
}

export default defineNuxtModule({
  setup (options, nuxt) {
    // Call your hook in `modules:done`
    nuxt.hook('modules:done', async () => {
      const payload = { foo: 'bar' }
      await nuxt.callHook('my-module:custom-hook', payload)
    })
  },
})
```

## Add Virtual Files

If you need to add a virtual file that can be imported into the user's app, you can use the `addTemplate` utility.

```ts
import { addTemplate, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    // The file is added to Nuxt's internal virtual file system and can be imported from '#build/my-module-feature.mjs'
    addTemplate({
      filename: 'my-module-feature.mjs',
      getContents: () => 'export const myModuleFeature = () => "hello world !"',
    })
  },
})
```

For the server, you should use the `addServerTemplate` utility instead.

```ts
import { addServerTemplate, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    // The file is added to Nitro's virtual file system and can be imported in the server code from 'my-server-module.mjs'
    addServerTemplate({
      filename: 'my-server-module.mjs',
      getContents: () => 'export const myServerModule = () => "hello world !"',
    })
  },
})
```

## Update Virtual Files

If you need to update your templates/virtual files, you can leverage the `updateTemplates` utility like this:

```ts
nuxt.hook('builder:watch', (event, path) => {
  if (path.includes('my-module-feature.config')) {
    // This will reload the template that you registered
    updateTemplates({ filter: t => t.filename === 'my-module-feature.mjs' })
  }
})
```

## Add Type Declarations

You might also want to add a type declaration to the user's project (for example, to augment a Nuxt interface
or provide a global type of your own). For this, Nuxt provides the `addTypeTemplate` utility that both
writes a template to the disk and adds a reference to it in the generated `nuxt.d.ts` file.

If your module should augment types handled by Nuxt, you can use `addTypeTemplate` to perform this operation:

```js
import { addTemplate, addTypeTemplate, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup (options, nuxt) {
    addTypeTemplate({
      filename: 'types/my-module.d.ts',
      getContents: () => `// Generated by my-module
        interface MyModuleNitroRules {
          myModule?: { foo: 'bar' }
        }
        declare module 'nitropack/types' {
          interface NitroRouteRules extends MyModuleNitroRules {}
          interface NitroRouteConfig extends MyModuleNitroRules {}
        }
        export {}`,
    })
  },
})
```

If you need more granular control, you can use the `prepare:types` hook to register a callback that will inject your types.

```ts
const template = addTemplate({ /* template options */ })
nuxt.hook('prepare:types', ({ references }) => {
  references.push({ path: template.dst })
})
```

## Extend TypeScript Config

There are multiple ways to extend the TypeScript configuration of the user's project from your module.

The simplest way is to modify the Nuxt configuration directly like this:

```ts
// extend tsconfig.app.json
nuxt.options.typescript.tsConfig.include ??= []
nuxt.options.typescript.tsConfig.include.push(resolve('./augments.d.ts'))

// extend tsconfig.shared.json
nuxt.options.typescript.sharedTsConfig.include ??= []
nuxt.options.typescript.sharedTsConfig.include.push(resolve('./augments.d.ts'))

// extend tsconfig.node.json
nuxt.options.typescript.nodeTsConfig.include ??= []
nuxt.options.typescript.nodeTsConfig.include.push(resolve('./augments.d.ts'))

// extend tsconfig.server.json
nuxt.options.nitro.typescript ??= {}
nuxt.options.nitro.typescript.tsConfig ??= {}
nuxt.options.nitro.typescript.tsConfig.include ??= []
nuxt.options.nitro.typescript.tsConfig.include.push(resolve('./augments.d.ts'))
```

Alternatively, you can use the `prepare:types` and `nitro:prepare:types` hooks to extend the TypeScript references for specific type contexts, or modify the TypeScript configuration similar to the example above.

```ts
nuxt.hook('prepare:types', ({ references, sharedReferences, nodeReferences }) => {
  // extend app context
  references.push({ path: resolve('./augments.d.ts') })
  // extend shared context
  sharedReferences.push({ path: resolve('./augments.d.ts') })
  // extend node context
  nodeReferences.push({ path: resolve('./augments.d.ts') })
})

nuxt.hook('nitro:prepare:types', ({ references }) => {
  // extend server context
  references.push({ path: resolve('./augments.d.ts') })
})
```

<note>

TypeScript references add files to the type context [without being affected by the `exclude` option in `tsconfig.json`](https://www.typescriptlang.org/tsconfig/#exclude).

</note>

## Augment Types

Nuxt automatically includes your module's directories in the appropriate type contexts. To augment types from your module, all you need to do is place the type declaration file in the appropriate directory based on the augmented type context. Alternatively, you can [extend the TypeScript configuration](#extend-typescript-config) to augment from an arbitrary location.

- `my-module/runtime/` - app type context (except for the `runtime/server` directory)
- `my-module/runtime/server/` - server type context
- `my-module/` - node type context (except for the `runtime/` and `runtime/server` directories)

```bash [Directory Structure]
-| my-module/   # node type context
---| runtime/   # app type context
------| augments.app.d.ts
------| server/ # server type context
---------| augments.server.d.ts
---| module.ts
---| augments.node.d.ts
```

### Known Limitations

#### Type-Checking Server Routes in App Context

Server routes are also type-checked using `tsconfig.app.json` in addition to `tsconfig.server.json`.

This is required because Nuxt infers the return types of your server endpoints to provide response types in [`$fetch`](/docs/4.x/api/utils/dollarfetch) and [`useFetch`](/docs/4.x/api/composables/use-fetch).

<warning>

This can cause issues when using **server-only types** in your route files. For example, if a module creates a server-only virtual file using [`addServerTemplate`](/docs/4.x/api/kit/templates#addservertemplate) and you declare types for it in `tsconfig.server.json`, those type declarations will only be available in the server context. When the app context type-checks your server routes, it won't recognize these server-only types and will report errors.  To resolve this, you unfortunately need to declare such types in the app context as well.

</warning>