---
layout: doc
title: Resolvers
description: createResolver builds a Resolver that controls file naming and path
  resolution for a plugin, with auto-injected defaults under resolver.default
  and Resolver.merge for layering overrides.
outline:
  - 2
  - 3
order: 3
navigation:
  title: Resolvers
  icon: i-iconoir-label
---

# Resolvers

## `createResolver`

`createResolver` builds a plugin resolver for names, paths, and imports.

Provide `pluginName`. Methods use `this` to reach the merged resolver, so namespaced methods call `this.name(...)` for active casing.

```typescript twoslash [resolver.ts]
import { createResolver } from 'kubb/kit'
import type { PluginFactoryOptions, Resolver } from 'kubb/kit'

// Extend the base Resolver with plugin-specific naming namespaces.
type MyResolver = Resolver & {
  schema: {
    name(node: { name: string }): string
  }
}

type MyPlugin = PluginFactoryOptions<'plugin-example', object, object, MyResolver>

export const resolver = createResolver<MyPlugin>({
  pluginName: 'plugin-example',
  name(name) {
    return `${name.charAt(0).toUpperCase()}${name.slice(1)}`
  },
  schema: {
    name(node) {
      return this.name(node.name)
    },
  },
})
```

### Auto-injected resolver defaults

| Method           | Default behavior                                                        |
| ---------------- | ---------------------------------------------------------------------- |
| `name`           | Active top-level identifier casing. Delegates to `default.name` when omitted |
| `file`           | Top-level `FileNode` builder, delegates to `default.file`              |
| `default.name`   | The core `camelCase` generated-identifier casing                       |
| `default.options`| Applies `exclude`, `include`, and `override` filters                   |
| `default.path`   | Resolves to `output.path`, with optional tag/path-based subdirectories |
| `default.file`   | Constructs a full `FileNode` using the resolver's `file.baseName` casing (default `toFilePath`) |
| `default.banner` | Returns `output.banner` or the standard "Generated by Kubb" header     |
| `default.footer` | Returns `output.footer` when set                                       |

### `resolver.imports` {#imports}

`resolver.imports` builds one import entry per `$ref` in a schema tree, so a generator emits the imports for every schema the current node references. Each ref resolves through `ast.resolveRefName`, which prefers the node's `targetName` and falls back to the pointer's last segment. 

The resulting names and paths go through the resolver's own `name` and `file` conventions. `extname` defaults to `.ts`.

```typescript twoslash [imports.ts]
import { ast } from 'kubb/kit'
import type { GeneratorContext, PluginFactoryOptions } from 'kubb/kit'

declare const ctx: GeneratorContext<PluginFactoryOptions>
const { resolver, root, output } = { ...ctx, output: { path: 'types' } }
const node = ast.factory.createSchema({ type: 'ref', ref: '#/components/schemas/Pet' })
// ---cut---
const imports = resolver.imports({ node, root, output })
// → [{ kind: 'Import', name: ['pet'], path: '/src/types/pet.ts' }]
```

Pass `name` to override how a referenced schema name becomes the imported identifier, for example to point enum refs at a suffixed type name:

```typescript twoslash [importsName.ts]
import { ast } from 'kubb/kit'
import type { GeneratorContext, PluginFactoryOptions } from 'kubb/kit'

declare const ctx: GeneratorContext<PluginFactoryOptions>
const { resolver, root, output } = { ...ctx, output: { path: 'types' } }
const node = ast.factory.createSchema({ type: 'ref', ref: '#/components/schemas/Pet' })
// ---cut---
const imports = resolver.imports({
  node,
  root,
  output,
  name: (schemaName) => `${resolver.name(schemaName)}Type`,
})
```

### `Resolver.merge`

`Resolver.merge(base, patch)` returns a new resolver with `patch`'s fields layered over `base`'s and every helper re-bound. A top-level `name` replaces, while `file` and each namespace merge per member, so overriding `query.name` keeps the base `query.keyName`. 

Use it to compose resolvers or apply partial overrides.

> [!TIP]
> Type a patch with `ResolverPatch<T>` to keep `this` and namespace shapes checked against the target resolver.

```typescript twoslash [merge.ts]
import { createResolver, Resolver } from 'kubb/kit'
import type { PluginFactoryOptions } from 'kubb/kit'

const resolver = createResolver<PluginFactoryOptions>({ pluginName: 'plugin-example' })
// ---cut---
const patched = Resolver.merge(resolver, {
  name(name) {
    return `Custom${this.default.name(name)}`
  },
})
```


## Resolver overrides {#resolver-overrides}

`name` changes identifier casing. `file.baseName` and `file.path` control files. Plugin namespaces control specific symbols.

```typescript [Type definition]
type ResolverPatch = {
  name?: (name: string) => string
  file?: {
    baseName?: (params: { name: string; extname: string }) => string
    path?: (params: { baseName: string; output: Output }) => string
  }
  // plugin-specific namespaces, such as query.keyName or schema.typeName
}
```

Methods run with a `this` context bound to the full, merged resolver, so write them as regular functions rather than arrow functions. `this.default.name(name)` always applies Kubb's core `camelCase` default. The plugin preset's `name` method remains separate.

From a namespaced method, `this.name(name)` calls the active top-level `name` method and follows any user override. Calling `this.name` from the top-level `name` method itself recurses, so call an exported preset resolver when you want to wrap its casing.


## See also

- [Resolver concepts](/docs/5.x/explanation/extensions#resolvers)
- [Override a resolver](/docs/5.x/how-to/resolvers)
- [Creating plugins](/docs/5.x/tutorials/creating-plugins)
