React
Use Lumis in React with client `CodeBlock` and `InlineCode` components, a `useLumis` hook, and server renderers.
Examples:
@lumis-sh/react follows the same formatter-first API as the rest of Lumis, but splits client and server helpers.
Install
npm install @lumis-sh/react @lumis-sh/lumis \
@lumis-sh/themes react \
@lumis-sh/wasm-bundle-webClient component
Use CodeBlock when you want a drop-in React component.
Simple
import {CodeBlock} from '@lumis-sh/react'
import {htmlInline} from '@lumis-sh/lumis/formatters'
import latte from '@lumis-sh/themes/catppuccin_latte'
import web from '@lumis-sh/wasm-bundle-web'
const codeString = `export function Hello() {
return <h1>Hello</h1>
}`
export function Example() {
return (
<CodeBlock
formatter={htmlInline({
language: web.tsx,
theme: latte,
})}
>
{codeString}
</CodeBlock>
)
}Reuse one highlighter
import {CodeBlock} from '@lumis-sh/react'
import {createHighlighter} from '@lumis-sh/lumis'
import web from '@lumis-sh/wasm-bundle-web'
import {htmlInline} from '@lumis-sh/lumis/formatters'
import latte from '@lumis-sh/themes/catppuccin_latte'
const highlighter = createHighlighter({
languages: [web],
})
const codeString = `export function Hello() {
return <h1>Hello</h1>
}`
export function Example() {
return (
<CodeBlock
highlighter={highlighter}
formatter={htmlInline({
language: web.tsx,
theme: latte,
})}
>
{codeString}
</CodeBlock>
)
}If you do not pass highlighter, CodeBlock uses a shared internal highlighter. Passing your own highlighter is the better choice when you want to register bundles or reuse one instance across many blocks.
Inline code
Use InlineCode for code inside a sentence. It takes the same formatter as CodeBlock and renders a <code> holding the highlighted tokens, with the lumis class and the theme's colors, instead of a <pre> block.
import {InlineCode} from '@lumis-sh/react'
import {htmlInline} from '@lumis-sh/lumis/formatters'
import latte from '@lumis-sh/themes/catppuccin_latte'
import web from '@lumis-sh/wasm-bundle-web'
const formatter = htmlInline({language: web.javascript, theme: latte})
export function Example() {
return (
<p>
Call <InlineCode formatter={formatter}>{'createHighlighter()'}</InlineCode> once.
</p>
)
}Pass attrs to add attributes to the <code>. class and style merge with the ones Lumis sets:
<InlineCode formatter={formatter} attrs={{class: 'api', id: 'setup'}}>
{'createHighlighter()'}
</InlineCode>InlineCode works with htmlInline, htmlLinked and htmlMultiThemes. Other formatters have no inline form and raise an error. With htmlLinked, the theme stylesheet colors the <code> through its lumis class.
Hook
Use useLumis() when you want to control the loading UI yourself.
import {useLumis} from '@lumis-sh/react'
import {createHighlighter} from '@lumis-sh/lumis'
import web from '@lumis-sh/wasm-bundle-web'
import {htmlInline} from '@lumis-sh/lumis/formatters'
import latte from '@lumis-sh/themes/catppuccin_latte'
const highlighter = createHighlighter({
languages: [web],
})
export function Example() {
const {content, error, isLoading} = useLumis({
children: 'export const answer = 42',
formatter: htmlInline({
language: 'typescript',
theme: latte,
}),
highlighter,
})
if (isLoading) return <p>Loading syntax highlighter...</p>
if (error) return <p>{error.message}</p>
return <>{content}</>
}Pass inline: true, and optionally attrs, to get the InlineCode output instead of a block.
error is an Error when highlighting or language loading fails, and undefined otherwise.
Server rendering
Use renderCodeBlock() from the server subpath when you need highlighted output on the server.
This is a good fit for React Server Components and Next.js App Router pages.
import {renderCodeBlock} from '@lumis-sh/react/server'
import web from '@lumis-sh/wasm-bundle-web'
import {htmlInline} from '@lumis-sh/lumis/formatters'
import latte from '@lumis-sh/themes/catppuccin_latte'
const codeString = `export function Hello() {
return <h1>Hello</h1>
}`
export async function Article() {
const block = await renderCodeBlock({
children: codeString,
formatter: htmlInline({
language: web.tsx,
theme: latte,
}),
})
return <article>{block}</article>
}renderInlineCode() is its counterpart for inline code, and takes the same options plus attrs:
import {renderInlineCode} from '@lumis-sh/react/server'
import web from '@lumis-sh/wasm-bundle-web'
import {htmlInline} from '@lumis-sh/lumis/formatters'
import latte from '@lumis-sh/themes/catppuccin_latte'
export async function Setup() {
const code = await renderInlineCode({
children: 'createHighlighter()',
formatter: htmlInline({language: web.javascript, theme: latte}),
})
return <p>Call {code} once.</p>
}Reusing one highlighter
Create one Highlighter and pass it into CodeBlock or useLumis():
import {createHighlighter} from '@lumis-sh/lumis'
import web from '@lumis-sh/wasm-bundle-web'
import {CodeBlock} from '@lumis-sh/react'
import {htmlInline} from '@lumis-sh/lumis/formatters'
import latte from '@lumis-sh/themes/catppuccin_latte'
const codeString = `export const answer = 42`
const highlighter = await createHighlighter({
languages: [web],
})
export function Example() {
return (
<CodeBlock
highlighter={highlighter}
formatter={htmlInline({
language: 'tsx',
theme: latte,
})}
>
{codeString}
</CodeBlock>
)
}This is the best option when you want to register a bundle and render many code blocks on one page.
Behavior
formatteris the single source of truth for the languageCodeBlock,InlineCodeanduseLumis()are client exports from@lumis-sh/reactrenderCodeBlock()andrenderInlineCode()are server exports from@lumis-sh/react/server- the React helpers call
highlighter.loadLanguage(formatter.language)before highlighting - when the language is already loaded, Lumis reuses the cached result
- for true server-rendered highlighting, prefer
renderCodeBlock() CodeBlock,InlineCodeanduseLumis()re-highlight whenchildren,formatter, orhighlighterchanges, andInlineCodeanduseLumis({inline: true})also whenattrschanges