Elements

Elements · Renderers

Syntax highlighter

Prism-based code highlighting for assistant markdown code blocks.

tsx
export function Assistant() {
  return <Thread />;
}
fig. 01

Installation

npx shadcn@latest add "@assistant-ui/syntax-highlighter"
First time? Set up a runtime

Runtime components read their state from an assistant-ui runtime. Add one to an existing project:

npx assistant-ui@latest init

Then wrap your app in a runtime provider:

import { AssistantRuntimeProvider } from "@assistant-ui/react";
import { useChatRuntime, AssistantChatTransport } from "@assistant-ui/ai-sdk";

export default function App() {
  const runtime = useChatRuntime({
    transport: new AssistantChatTransport({ api: "/api/chat" }),
  });

  return (
    <AssistantRuntimeProvider runtime={runtime}>
      {/* your components */}
    </AssistantRuntimeProvider>
  );
}

The installation guide covers new projects, templates, and API routes.

SyntaxHighlighter tokenizes one fenced code block with Prism, through react-syntax-highlighter's async light build, and renders a light and a dark themed variant at once so a theme switch needs no re-render. With a runtime it replaces the plain code Markdown text renders by default; standalone you call it directly with the code, the language, and the tag components it mounts onto.

Getting started

Add it to MarkdownText's components

components/assistant-ui/elements/markdown-text.tsx
import { SyntaxHighlighter } from "./syntax-highlighter";

const defaultComponents = memoizeMarkdownComponents({
  SyntaxHighlighter,
  h1: /* ... */,
  // ...other elements...
});

Every fenced code block in an assistant message now highlights, using the light and dark Coldark Prism themes. Shiki highlighter is the newer, runtime-aware alternative: reach for this one if you are already on react-syntax-highlighter.

Anatomy

<>
  <Pre className="dark:hidden">{/* Coldark Cold tokens */}</Pre>
  <Pre className="hidden dark:block">{/* Coldark Dark tokens */}</Pre>
</>

Both the light and the dark highlighter mount on every render; Tailwind's dark: classes hide whichever one does not match the active theme, so switching themes toggles visibility instead of re-tokenizing.

Examples

Register another language

components/assistant-ui/elements/syntax-highlighter.tsx
import { PrismAsyncLight } from "react-syntax-highlighter";
import go from "react-syntax-highlighter/dist/esm/languages/prism/go";

PrismAsyncLight.registerLanguage("go", go);

Only js, jsx, ts, tsx (all mapped to the TSX grammar) and python are registered by default. A fenced block whose language is not registered still renders, unhighlighted.

Load every language instead

components/assistant-ui/elements/syntax-highlighter.tsx
import { makePrismAsyncSyntaxHighlighter } from "@assistant-ui/react-syntax-highlighter/full";

Swap the light-build import for the full build to skip per-language registration entirely, at the cost of a larger bundle.

Choose a different theme

components/assistant-ui/elements/syntax-highlighter.tsx
import { oneLight, oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";

const LightSyntaxHighlighter = makePrismAsyncLightSyntaxHighlighter({
  style: oneLight,
  customStyle: syntaxHighlighterCustomStyle,
  className: "dark:hidden",
});

Pass any react-syntax-highlighter Prism style in place of coldarkCold/coldarkDark; do the same for the dark variant.

API reference

MarkdownText's code-block pipeline supplies every prop below automatically, from the fenced block being rendered; you never call SyntaxHighlighter yourself once it is wired into components.

SyntaxHighlighter

PropTypeDefaultDescription
languagestringrequiredThe fence's language, for example "tsx". Selects the Prism grammar.
codestringrequiredThe code to highlight.
components{ Pre: ComponentType; Code: ComponentType }requiredTag components the highlighter renders into, as PreTag/CodeTag.