---
title: "ESLint Plugin"
description: "Catch unhead misuse, type-narrowing issues, and v2-to-v3 migration problems at the source level with @unhead/eslint-plugin."
canonical_url: "https://unhead.unjs.io/docs/typescript/head/guides/tooling/eslint-plugin"
last_updated: "2026-08-11T00:42:24.640Z"
---

`@unhead/eslint-plugin` checks Unhead calls in source files. It covers a subset of the runtime [`ValidatePlugin`](/docs/typescript/head/guides/tooling/validate-plugin) rules, including deprecated v2 properties, missing required attributes, and meta-tag typos.

## What Source Lint Catches

Use source lint for issues that can be determined without rendering a head:

- Autofixes rename deprecated properties, add required values, and remove redundant values.
- The plugin reports findings in editors and CI without starting the application.
- Lint, runtime validation, and the CLI share rule IDs from `unhead/validate`.

## Install

```bash
pnpm add -D @unhead/eslint-plugin
```

## Usage

Add the recommended config to your flat ESLint config:

```ts [eslint.config.ts]
import { configs } from '@unhead/eslint-plugin'

export default [
  configs.recommended,
]
```

For projects migrating from Unhead v2, swap in `configs.migration` to also wrap tag literals in their `defineLink` / `defineScript` helpers for type narrowing.

```ts [eslint.config.ts]
import { configs } from '@unhead/eslint-plugin'

export default [
  configs.migration,
]
```

## Rules

The plugin ships 15 rules. The full table with severity, autofix status, and what each rule catches lives in the package README:

→ [`@unhead/eslint-plugin` rules table](https://github.com/unjs/unhead/tree/main/packages/eslint-plugin#rules)

Migration rules include:

- `no-deprecated-props` (error, autofix): rewrites v2 property names that v3 no longer converts.
- `no-unknown-meta` (warn, autofix): suggests corrections for typos such as `og:descriptin` → `og:description`.
- `invalid-input-shape` (warn): catches wrong container shapes and head fields such as `meta` or `titleTemplate` nested inside `htmlAttrs` or `bodyAttrs`.
- `numeric-tag-priority` (warn, suggestions): flags numeric `tagPriority` values and suggests `'critical' | 'high' | 'low'`.
- `prefer-define-helpers` (off in `recommended`, on in `migration`, autofix): wraps `link` and `script` literals in `defineLink` and `defineScript` so Unhead's discriminated unions narrow correctly.

## What it can and can't see

Lint rules walk source-level calls into `useHead`, `useHeadSafe`, `useServerHead`, `useServerHeadSafe`, `useSeoMeta`, `useServerSeoMeta`, and the tag helpers `defineLink` / `defineScript`. Tag arrays inside `meta` / `link` / `script` / `noscript` / `style` and object literals inside `htmlAttrs` / `bodyAttrs` are descended automatically.

It cannot see anything that depends on the resolved tag set: cross-tag conflicts (canonical vs `og:url`), counts (too many preloads), or rendered byte budgets (meta beyond 1MB). Those checks live in the runtime [`ValidatePlugin`](/docs/typescript/head/guides/tooling/validate-plugin) and are surfaced by the [CLI](/docs/typescript/head/guides/tooling/cli)'s `validate-html` and `validate-url` commands.

Calls, identifiers, refs, and computed expressions have an unknown static shape, so this rule leaves them for runtime validation.

## Editor integration

Editors that run ESLint through the VS Code or JetBrains integration expose fixes for autofixable rules. The `numeric-tag-priority` rule also offers a suggestion for each priority alias.
