Docs
Code block

Code block


The code block displays markdown code blocks in the LLM output using shiki.

Features

```typescript key=value
console.log('hello llm-ui')
```

1x

  • Code block syntax is hidden from users
  • Code highlighting for 100s of languages with shiki

Installation

pnpm add @llm-ui/code shiki

Quick start

View on GitHub

Install dependencies

pnpm add @llm-ui/code shiki @llm-ui/react @llm-ui/markdown react-markdown remark-gfm html-react-parser

Step 1: Create a markdown component

Create a component to render markdown using react-markdown.

import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { type LLMOutputComponent } from "@llm-ui/react";


// Customize this component with your own styling
const MarkdownComponent: LLMOutputComponent = ({ blockMatch }) => {
  const markdown = blockMatch.output;
  return <ReactMarkdown remarkPlugins={[remarkGfm]}>{markdown}</ReactMarkdown>;
};

Read more in the markdown block docs

Step 2: Create a code block component

Create a component to render code blocks using Shiki.

import type { CodeToHtmlOptions } from "@llm-ui/code";
import {
  loadHighlighter,
  useCodeBlockToHtml,
  allLangs,
  allLangsAlias,
} from "@llm-ui/code";
// WARNING: Importing bundledThemes increases your bundle size
// see: https://llm-ui.com/docs/blocks/code#bundle-size
import { bundledThemes } from "shiki/themes";
import { type LLMOutputComponent } from "@llm-ui/react";
import parseHtml from "html-react-parser";
import { getHighlighterCore } from "shiki/core";
import { bundledLanguagesInfo } from "shiki/langs";

import getWasm from "shiki/wasm";

const highlighter = loadHighlighter(
  getHighlighterCore({
    langs: allLangs(bundledLanguagesInfo),
    langAlias: allLangsAlias(bundledLanguagesInfo),
    themes: Object.values(bundledThemes),
    loadWasm: getWasm,
  }),
);

const codeToHtmlOptions: CodeToHtmlOptions = {
  theme: "github-dark",
};

// Customize this component with your own styling
const CodeBlock: LLMOutputComponent = ({ blockMatch }) => {
  const { html, code } = useCodeBlockToHtml({
    markdownCodeBlock: blockMatch.output,
    highlighter,
    codeToHtmlOptions,
  });
  if (!html) {
    // fallback to <pre> if Shiki is not loaded yet
    return (
      <pre className="shiki">
        <code>{code}</code>
      </pre>
    );
  }
  return <>{parseHtml(html)}</>;
};

Step 3: Render markdown and code with llm-ui

Now we’ve created our components, we’re ready to use useLLMOutput to render language model output which contains markdown and code.

import {
  codeBlockLookBack,
  findCompleteCodeBlock,
  findPartialCodeBlock,
} from "@llm-ui/code";
import { markdownLookBack } from "@llm-ui/markdown";
import { useLLMOutput, useStreamExample } from "@llm-ui/react";

const example = `## Python

\`\`\`python
print('Hello llm-ui!')
\`\`\`
...continues...
`;

const Example = () => {
  const { isStreamFinished, output } = useStreamExample(example);

  const { blockMatches } = useLLMOutput({
    llmOutput: output,
    fallbackBlock: {
      component: MarkdownComponent, // from Step 1
      lookBack: markdownLookBack(),
    },
    blocks: [
      {
        component: CodeBlock, // from Step 2
        findCompleteMatch: findCompleteCodeBlock(),
        findPartialMatch: findPartialCodeBlock(),
        lookBack: codeBlockLookBack(),
      },
    ],
    isStreamFinished,
  });

  return (
    <div>
      {blockMatches.map((blockMatch, index) => {
        const Component = blockMatch.block.component;
        return <Component key={index} blockMatch={blockMatch} />;
      })}
    </div>
  );
};

Read more in the useLLMOutput docs

Shiki setup

llm-ui’s code block uses shiki to highlight code. Shiki is often used in server side code, but llm-ui needs to highlight code blocks on the client. This can be tricky to setup, but llm-ui provides some helpers to make it easier.

Loading Shiki

Shiki highlighters are loaded asynchronously, this can be awkward to work with in practice.

llm-ui provides loadHighlighter, which proactively loads the shiki highlighter and returns a LLMUIHighlighter object:

import { loadHighlighter } from "@llm-ui/code";
import { getHighlighterCore } from "shiki/core";

const highlighter = loadHighlighter(
  getHighlighterCore({
    // shiki options here
  }),
);

// => returns: LLMUIHighlighter
{
  // Get the highlighter synchronously
  getHighlighter: () => HighlighterCore | undefined;

  // Promise that resolves when the highlighter is loaded
  highlighterPromise: Promise<HighlighterCore>;
}

You should call loadHighlighter early in your application’s lifecycle to ensure the highlighter is ready when you need it.

If the highlighter is not yet loaded you could fallback to a <pre> element or a loading spinner.

Next.js

To use shiki client-side with next.js you must use dynamic imports to avoid server-side-rendering.

// file: app/page.tsx

import dynamic from "next/dynamic";

const Page = () => {
  // Code which uses Shiki must be imported dynamically
  const Example = dynamic(() => import("./example"), { ssr: false });
  return <Example />;
};

export default Page;

Bundle size

Themes

The quick start example imports all shiki themes. This is not recommended for production. To reduce bundle size, only import the themes you need.

Change the import:

// Before:
import { bundledThemes } from "shiki/themes";

// After:
import githubDark from "shiki/themes/github-dark.mjs";

Pass the theme to the shiki highlighter:

const highlighter = loadHighlighter(
  getHighlighterCore({
    langs: allLangs(bundledLanguages),
    langAlias: allLangsAlias(bundledLanguages),
    themes: [githubDark], // <- fixed!
    loadWasm: getWasm,
  }),
);

Read the Shiki docs for more information about how to reduce bundle size.

Languages

The quick start example imports all shiki languages. You may also want to reduce the number of languages imported depending on your usecase. Read the Shiki docs for more information.

Code block functions

const codeBlock = {
  findCompleteMatch: findCompleteCodeBlock(),
  findPartialMatch: findPartialCodeBlock(),
  lookBack: codeBlockLookBack(),
  component: () => <div>Code block</div>,
};

findCompleteCodeBlock

Finds a complete code block in a string.

Example:

```ts
console.log('hello llm-ui');
```

findPartialCodeBlock

Find a partial code block in a string.

Example:

```ts
console.log(

codeBlockLookBack

Look back function for the code block.

Options

All three block functions accept the follow options:

{
  startEndChars: ["```", "~~~"],
}

Helper functions

useCodeBlockToHtml

useCodeBlockToHtml converts a markdown code block to highlighted HTML and code (string).

import { useCodeBlockToHtml } from "@llm-ui/code";

const MyComponent = () => {
  const { html, code } = useCodeBlockToHtml({
    markdownCodeBlock: "```typescript\nconsole.log('llm-ui');\n```",
    highlighter, // highlighter from loadHighlighter function
    codeToHtmlOptions, // Shiki codeToHtmlOptions
  });
  console.log(html);
  // => "<pre class="shiki"...>...</pre>"

  console.log(code);
  // => "console.log('llm-ui');"

  ...
}

useCodeToHtml

useCodeToHtml converts a markdown code block to highlighted HTML.

import { useCodeToHtml } from "@llm-ui/code";

const MyComponent = () => {

  const html = useCodeToHtml({
    code: "console.log('llm-ui');",
    highlighter, // highlighter from loadHighlighter function
    codeToHtmlOptions: { lang: 'typescript' }, // Shiki codeToHtmlOptions
  });

  console.log(html);
  // => "<pre class="shiki"...>...</pre>"

  ...
}

parseCompleteMarkdownCodeBlock

Parses a complete code block:

import { parseCompleteMarkdownCodeBlock } from "@llm-ui/code";

parseCompleteMarkdownCodeBlock(
  "```typescript title="file.ts"\nconsole.log('llm-ui');\n```",
  {startEndChars: ["```", "~~~"]}
);

// =>
// {
//   code: "console.log('llm-ui');",
//   lang: "typescript",
//   meta: 'title="file.ts"'
// }

parsePartialMarkdownCodeBlock

Parses a partial code block:

import { parsePartialMarkdownCodeBlock } from "@llm-ui/code";

parsePartialMarkdownCodeBlock(
  "```typescript title="file.ts"\nconsole.log('llm",
  { startEndChars: ["```", "~~~"] }
);

// =>
// {
//   code: "console.log('llm;",
//   lang: "typescript",
//   meta: 'title="file.ts"'
// }