Code block
The code block displays markdown code blocks in the LLM output using shiki.
Features
```typescript key=value console.log('hello llm-ui') ```
0.4x
- Code block syntax is hidden from users
- Code highlighting for 100s of languages with shiki
Installation
pnpm add @llm-ui/react @llm-ui/code shiki
Quick start
Copy and paste this file into your project to get started.
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 { allThemes } from "@llm-ui/code/shikiBundles/allThemes";
// After:
import githubDark from "shiki/themes/github-dark.mjs";
Pass the theme to the shiki highlighter:
const highlighter = loadHighlighter(
getHighlighterCore({
langs: allLangs,
langAlias: allLangsAlias,
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.
findPartialCodeBlock
Find a partial code block in a string.
codeBlockLookBack
Look back function for the code block.
Options
All three block functions accept the follow options:
{
startEndChars: ["```", "~~~"],;
}
Helper functions
useCodeBlockToHtml
import { useCodeBlockToHtml } from "@llm-ui/code";
const MyComponent = () => {
const { html, code } = useCodeBlockToHtml({
markdownCodeBlock: "```typescript\nconsole.log('llm-ui');\n```",
highlighter, // highlighter from loadHighlighter function
codeToHtmlProps, // Shiki codeToHtmlProps
});
console.log(html);
// => "<pre class="shiki"...>...</pre>"
console.log(code);
// => "console.log('llm-ui');"
...
}
useCodeBlockToHtml
converts a markdown code block to highlighted HTML and code (string).
useCodeToHtml
import { useCodeToHtml } from "@llm-ui/code";
const MyComponent = () => {
const html = useCodeToHtml({
markdownCodeBlock: "```typescript\nconsole.log('llm-ui');\n```",
highlighter, // highlighter from loadHighlighter function
codeToHtmlProps, // Shiki codeToHtmlProps
});
console.log(html);
// => "<pre class="shiki"...>...</pre>"
...
}
useCodeToHtml
converts a markdown code block to highlighted HTML.
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"'
// }