Skip to main content
Login
Sign up

Twoslash

Opt a TypeScript or JavaScript code block into Twoslash to add interactive type hovers, inline compiler errors, and `// ^?` type queries — powered by the real TypeScript language service at build time.

Twoslash runs the real TypeScript language service over a code block at build time and bakes the results into the page: hover any identifier to see its resolved type, surface expected compiler errors inline, and pin a type with a // ^? query. It’s the same tooling the TypeScript handbook, VitePress, and Astro Starlight use — rendered with Shiki + @shikijs/twoslash.

It’s opt-in per block: add twoslash after the language on a ts, tsx, js, or jsx fence. Every other code block is untouched.

```ts twoslash
const greeting = "Hello, aardvark"
const loud = greeting.toUpperCase()
```

renders, live (hover greeting or loud):

const const greeting: "Hello, aardvark"greeting = "Hello, aardvark"
const const loud: stringloud = const greeting: "Hello, aardvark"greeting.String.toUpperCase(): string
Converts all the alphabetic characters in a string to uppercase.
toUpperCase
()

Type queries

A // ^? comment, with the ^ under the token you want, pins that token’s type below the line — handy for showing what an expression infers to:

const 
const point: {
    x: number;
    y: number;
}
point
= { x: numberx: 10, y: numbery: 20 }

Showing errors

By default Twoslash treats a compiler error as a build problem. To show an error on purpose, declare its code with // @errors: — the block then renders with the squiggle and the message inline instead of failing the build:

const count: number = "not a number"
Type 'string' is not assignable to type 'number'.

Trimming setup with // ---cut---

Real examples often need setup that distracts from the point. Everything above a // ---cut--- line is compiled (so types still resolve) but hidden from the rendered block:

const 
const user: {
    id: string;
    name: string;
}
user
=
const db: {
    find: (id: string) => {
        id: string;
        name: string;
    };
}
db
.
find: (id: string) => {
    id: string;
    name: string;
}
find
("u_1")

Multiple files

Use // @filename: to split a block into several modules — imports resolve across them, so you can demonstrate a small project:

// @filename: shapes.ts
export interface Circle { Circle.kind: "circle"kind: "circle"; Circle.radius: numberradius: number }
// @filename: area.ts
import type { Circle } from "./shapes"
export const const area: (c: Circle) => numberarea = (c: Circlec: Circle) => var Math: Math
An intrinsic object that provides basic mathematics functionality and constants.
Math
.Math.PI: number
Pi. This is the ratio of the circumference of a circle to its diameter.
PI
* c: Circlec.Circle.radius: numberradius ** 2

Configuration

Twoslash is on by default. Turn it off site-wide, or pick a different Shiki theme for the rendered blocks, in aardvark.config.yaml:

twoslash:
  theme: github-light       # Shiki light theme (default: github-light)
  themeDark: github-dark    # Shiki dark theme (default: github-dark)
  timeout: 120              # seconds to allow the render before falling back (default: 120)

Set twoslash: false to disable the feature everywhere, or add twoslash: false to a single page’s front matter to disable it just there — useful for a page of deliberately incomplete snippets. Either way, a twoslash-tagged fence falls back to a normal highlighted code block.

Requirements

Twoslash needs Node and three build-time packages — shiki, @shikijs/twoslash, and typescript — which ship in the scaffolded package.json, so npm install once and vark build renders your twoslash blocks. If Node or the packages aren’t available (or you build with --no-bundle), tagged blocks degrade gracefully to plain highlighted code and the build prints a one-line notice — it never fails the build over a missing toolchain.

Keep twoslash snippets short. So the hover and // ^? popovers can escape the block, a twoslash block isn’t horizontally scrollable the way a plain code block is — a very long line extends the page width instead of scrolling. Favor short, focused lines in twoslash blocks.

Last modified

Was this page helpful?