Core Concepts

Language Registration

Language Registration

Language registration is the main bundle-size control in TanStack Highlight.

Create a selective highlighter

ts
import { createHighlighter } from '@tanstack/highlight/core'
import { json } from '@tanstack/highlight/languages/json'
import { shell } from '@tanstack/highlight/languages/shell'
import { tsx } from '@tanstack/highlight/languages/tsx'

export const highlighter = createHighlighter({
  languages: [json, shell, tsx],
})

The resulting registry contains three definitions plus their aliases. It does not load a global grammar catalog.

ts
highlighter.listLanguages()
// ['json', 'shell', 'tsx']

highlighter.normalizeLanguage('bash')
// 'shell'

highlighter.normalizeLanguage('python')
// 'plaintext'

Aliases belong to definitions. bash resolves only because shell was registered.

Fallback behavior

The default fallback name is plaintext:

ts
const result = highlighter.highlight('<unsafe>', {
  lang: 'not-registered',
})

result.lang
// 'plaintext'

result.html
// The angle brackets are escaped.

You can choose another registered fallback language:

ts
const highlighter = createHighlighter({
  fallbackLanguage: 'json',
  languages: [json],
})

Unknown inputs will then be tokenized as JSON. An unregistered fallback name still produces escaped plaintext under that name, but registering plaintext is clearer when plain output is intentional.

Root convenience entry

The package root constructs a highlighter with allLanguages and exports bound helpers:

ts
import {
  defaultHighlighter,
  highlight,
  highlightToHtml,
  listLanguages,
  normalizeLanguage,
  renderCodeBlockData,
  tokenize,
} from '@tanstack/highlight'

This is convenient, but importing any bound helper retains the all-language registry. Use @tanstack/highlight/core when browser size matters.

Share one registry

Create the highlighter once at module scope:

ts
// code-highlighter.ts
export const highlighter = createHighlighter({
  languages: [json, shell, tsx],
})

There is no benefit to creating one per block. The registry is immutable after construction and safe to reuse across server requests and client renders.

Aggregate imports

The aggregate entry exposes every shipped language module:

ts
import { json, shell, tsx } from '@tanstack/highlight/languages'

A tree-shaking bundler can remove unused definitions because the package is side-effect free. Prefer direct subpaths when the bundle must make isolation explicit:

ts
import { json } from '@tanstack/highlight/languages/json'

See Language Support for every name and alias.