---
title: "Twoslash Highlight"
description: "Twoslash Highlight is a sophisticated documentation enrichment subsystem that bridges the gap between static code samples and interactive TypeScript analysis. By integrating the twoslash engine dir..."
last_updated: "2026-07-02T09:46:39.37205+00:00"
canonical_url: "https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/rendering-ui/twoslash-highlight"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/twoslash/styles/twoslash.css](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/styles/twoslash.css)
- [packages/api-docs/src/components/schema/client.tsx](https://github.com/blade47/fumadocs/blob/main/packages/api-docs/src/components/schema/client.tsx)
- [packages/core/src/toc.tsx](https://github.com/blade47/fumadocs/blob/main/packages/core/src/toc.tsx)
- [packages/base-ui/src/components/toc/clerk.tsx](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/components/toc/clerk.tsx)
- [packages/twoslash/src/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/index.ts)
- [packages/twoslash/.oxlintrc.json](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/.oxlintrc.json)
- [packages/story/src/type-tree/builder.ts](https://github.com/blade47/fumadocs/blob/main/packages/story/src/type-tree/builder.ts)
- [packages/base-ui/src/layouts/notebook/page/slots/toc.tsx](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/layouts/notebook/page/slots/toc.tsx)
- [packages/base-ui/src/layouts/shared/slots/language-select.tsx](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/layouts/shared/slots/language-select.tsx)
- [packages/base-ui/src/components/dynamic-codeblock.core.tsx](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/components/dynamic-codeblock.core.tsx)
- [packages/radix-ui/src/components/dynamic-codeblock.core.tsx](https://github.com/blade47/fumadocs/blob/main/packages/radix-ui/src/components/dynamic-codeblock.core.tsx)
- [packages/twoslash/src/ui/popup.tsx](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/ui/popup.tsx)
- [packages/core/src/mdx-plugins/rehype-code/shiki.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/mdx-plugins/rehype-code/shiki.ts)
- [packages/core/src/highlight/shiki/react.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/highlight/shiki/react.ts)
- [packages/twoslash/src/ui/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/ui/index.ts)
- [packages/base-ui/tsdown.config.ts](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/tsdown.config.ts)
- [packages/base-ui/src/components/codeblock.rsc.tsx](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/components/codeblock.rsc.tsx)
- [packages/radix-ui/src/components/codeblock.rsc.tsx](https://github.com/blade47/fumadocs/blob/main/packages/radix-ui/src/components/codeblock.rsc.tsx)
- [packages/base-ui/src/layouts/flux/page/slots/toc.tsx](https://github.com/blade47/fumadocs/blob/main/packages/base-ui/src/layouts/flux/page/slots/toc.tsx)
- [packages/core/src/highlight/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/highlight/index.ts)
- [packages/twoslash/src/cache-fs.ts](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/cache-fs.ts)
- [packages/core/src/mdx-plugins/remark-llms.runtime.ts](https://github.com/blade47/fumadocs/blob/main/packages/core/src/mdx-plugins/remark-llms.runtime.ts)
- [packages/twoslash/package.json](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/package.json)
- [packages/preview/src/components/markdown.tsx](https://github.com/blade47/fumadocs/blob/main/packages/preview/src/components/markdown.tsx)
- [packages/python/src/components/index.tsx](https://github.com/blade47/fumadocs/blob/main/packages/python/src/components/index.tsx)
- [packages/twoslash/tsconfig.json](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/tsconfig.json)
- [packages/twoslash/tsdown.config.ts](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/tsdown.config.ts)
- [packages/typescript/src/cache/index.ts](https://github.com/blade47/fumadocs/blob/main/packages/typescript/src/cache/index.ts)
- [packages/typescript/src/markdown.ts](https://github.com/blade47/fumadocs/blob/main/packages/typescript/src/markdown.ts)
- [packages/twoslash/src/ui/cn.ts](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/ui/cn.ts)
</details>

Twoslash Highlight is a sophisticated documentation enrichment subsystem that bridges the gap between static code samples and interactive TypeScript analysis. By integrating the `twoslash` engine directly into the documentation rendering pipeline, it enables features like hover-based type information, automatic error highlighting, and code completion previews. It is designed to work in conjunction with the Fumadocs `rehype-code` pipeline, transforming standard markdown code blocks into dynamic, information-rich UI elements.

The architecture centers on a Shiki-based transformer factory. When a documentation page is processed, Twoslash interprets special comments within code blocks to perform TypeScript language service operations. The resultant data—containing type definitions, diagnostics, and metadata—is then mapped into HTML structures (via Shiki's renderer) that React components (like the custom `Popup`) use to present interactive overlays.

A key design decision in this subsystem is the "lazy-loaded" instance pattern. By delaying the initialization of the heavy `twoslash` engine until requested, the system ensures that build times remain performant and serverless environments remain lightweight. This implementation detail prevents unnecessary instantiation while allowing the system to scale to large documentation sets containing thousands of code snippets.

## The Transformer Factory Mechanism

The transformer is orchestrated by the `transformerTwoslash` function, which serves as the entry point for Fumadocs code block processing. This function leverages `createTransformerFactory` to bridge the gap between static Shiki highlighting and dynamic Twoslash analysis.

```mermaid
flowchart TD
    A["rehype-code plugin"] --> B["transformerTwoslash"]
    B --> C["lazyInstance()"]
    C --> D["createTwoslasher"]
    B --> E["rendererRich"]
    E --> F["hast:hoverToken"]
    E --> G["hast:hoverPopup"]
```
Sources: [packages/twoslash/src/index.ts:31-131](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/index.ts#L31-L131)

The mechanism for lazy loading is crucial for resource management. The `lazyInstance` function uses a cached singleton `cachedInstance`. If the cache is empty, it invokes `createTwoslasher`, configuring it with a `Bundler` module resolution strategy. This ensures that the TypeScript compiler options are correctly set for modern documentation environments.

Sources: [packages/twoslash/src/index.ts:25-52](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/index.ts#L25-L52)

> [!NOTE]
> The `compilerOptions` override sets `moduleResolution` to `100` (Bundler). This is a rigid invariant to ensure that `twoslash` accurately resolves imports that are standard in modern frontend documentation projects, regardless of the user's specific `tsconfig.json` settings.

## Rendering Pipeline and HAST Transformation

The transformation process relies on `rendererRich` from `@shikijs/twoslash`. It defines how Twoslash metadata is converted into HAST (Hypertext Abstract Syntax Tree) nodes that Fumadocs renders.

The core mapping logic intercepts standard HAST tokens and maps them to HAST properties configured via the renderer's `hast` option.

Sources: [packages/twoslash/src/index.ts:54-108](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/index.ts#L54-L108)

For line-specific queries (such as diagnostics or errors), the code performs an explicit HAST node modification:
```typescript
const fn = renderer.lineQuery!;
renderer.lineQuery = function (this: ShikiTransformerContext, ...args) {
  const result = fn.call(this, ...args);
  // Re-wrap to ensure proper styling as a span
  const child = result[0].children[0];
  result[0].children[0] = {
    type: 'element',
    tagName: 'span',
    children: [child],
  };
  return result;
};
```
Sources: [packages/twoslash/src/index.ts:110-123](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/index.ts#L110-L123)

## Interactive Popup Component Lifecycle

The interaction layer is defined in `packages/twoslash/src/ui/popup.tsx`. It provides a context-aware `Popup` component that manages hover-state timing with `setTimeout` to prevent UI jitter.

```mermaid
sequenceDiagram
    participant User
    participant PopupTrigger
    participant Popup
    participant PopoverContent

    User->>PopupTrigger: pointerEnter
    Popup->>Popup: set timer (delay 300ms)
    alt Time elapsed
      Popup->>PopoverContent: setOpen(true)
    end
    User->>PopupTrigger: pointerLeave
    Popup->>Popup: clear timers, setOpen(false)
```
Sources: [packages/twoslash/src/ui/popup.tsx:1-61](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/ui/popup.tsx#L1-L61)

The system uses pointer events (`onPointerEnter`, `onPointerLeave`) rather than simple `onMouseEnter` to better handle different input devices, specifically ignoring `touch` events in the implementation logic to prevent unintentional interactions on mobile devices.

Sources: [packages/twoslash/src/ui/popup.tsx:38-46](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/ui/popup.tsx#L38-L46)

## Caching Strategy

Performance is maintained via a file-system-based cache for type checking results. This is critical because `twoslash` runs the TypeScript language server on code snippets, which is computationally expensive.

The `createFileSystemTypesCache` provides an `init`, `read`, and `write` interface:
1. **Hash Generation:** It uses `SHA256` to create a 12-character identifier based on the source code string.
2. **Persistence:** Files are stored in `.next/cache/twoslash` by default, allowing subsequent builds or server restarts to bypass expensive type re-analysis.

Sources: [packages/twoslash/src/cache-fs.ts:1-50](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/cache-fs.ts#L1-L50)

> [!WARNING]
> While `read` and `write` are synchronous, ensure that the cache directory has appropriate write permissions. Failure to write will not crash the compilation but will significantly degrade documentation build performance.

## Styling System

The subsystem uses CSS custom properties and scoped classes to integrate into Fumadocs' theme system. The styles are defined in `twoslash.css`.

| Class Name | Purpose | Key Styling |
| :--- | :--- | :--- |
| `.fd-twoslash-popover` | Root popover container | `z-index: 50`, `border-radius: xl` |
| `.twoslash-error` | Wavy diagnostic underline | `text-decoration: wavy underline` |
| `.twoslash-tag-line` | Custom tag blocks | `border-left: 3px solid` |

Sources: [packages/twoslash/styles/twoslash.css:199-225](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/styles/twoslash.css#L199-L225)

The use of `currentColor` for hover transitions ensures that the interactive UI indicators inherit the document's theme color automatically, preventing manual synchronization overhead in the theme configuration.

Sources: [packages/twoslash/styles/twoslash.css:88-101](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/styles/twoslash.css#L88-L101)

## Implementation Pattern

To integrate Twoslash in a documentation project, developers rely on the transformer export.

```typescript
import { transformerTwoslash } from 'fumadocs-twoslash';

// Inside your shiki configuration
const transformer = transformerTwoslash({
  twoslashOptions: {
    // Add custom compiler options here
  }
});
```
Sources: [packages/twoslash/src/index.ts:31-45](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/index.ts#L31-L45)

The renderer is inherently decoupled, meaning you can extend the `hast` configuration if you need to add custom UI triggers or tooltips beyond the standard `Popup` component, as shown in the renderer setup in the index source file.

Sources: [packages/twoslash/src/index.ts:106-107](https://github.com/blade47/fumadocs/blob/main/packages/twoslash/src/index.ts#L106-L107)

## Related

- [Syntax Highlighting](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/rendering-ui/syntax-highlighting)
- [TypeScript Tables](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/rendering-ui/typescript-tables)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/llms.txt) for all pages in this wiki.
