CodeBlock

Displays syntax-highlighted source code with optional line numbers, toolbar, copy-to-clipboard button, and error diagnostics.

Supports both static code strings and custom rendering via Tokens, LineNumbers, and Toolbar subcomponents. For JavaScript and TypeScript, code can be type-checked, formatted with Prettier (if available), and augmented with quick-info tooltips on hover.

In development, the component uses a Suspense fallback to render immediately while asynchronous syntax highlighting and analysis load in the background. In production, it renders the fully-resolved code block directly.

Style Overrides

The CodeBlock component can be styled using the, css, className, and style props to target specific descendant components. In most cases, its a good idea to create your own component that wraps the CodeBlock component and applies the style overrides you need:

import {
  type CodeBlockProps,
  CodeBlock as DefaultCodeBlock,
  Tokens,
} from 'renoun/components'
import styles from './CodeBlock.module.css'

export function CodeBlock(props: CodeBlockProps) {
  return (
    <DefaultCodeBlock
      {...props}
      css={{
        // Clear the default styles
        container: {
          boxShadow: undefined,
          borderRadius: undefined,
        },
        ...props.css,
      }}
      className={{
        container: styles.container,
        token: styles.token,
        ...props.className,
      }}
    />
  )
}

Component Overrides

If you need more customization, the CodeBlock component can be fully overridden by importing it from renoun/components and extending it as needed:

CodeBlock.tsx
import {
  type CodeBlockProps,
  CodeBlock as DefaultCodeBlock,
  Tokens,
} from 'renoun/components'

export function CodeBlock({
  children,
  ...props
}: Omit<CodeBlockProps, 'children'> & { children: string }) {
  return (
    <DefaultCodeBlock {...props}>
      <pre
        style={{
          whiteSpace: 'pre',
          wordWrap: 'break-word',
          overflow: 'auto',
        }}
      >
        <Tokens>{children}</Tokens>
      </pre>
    </DefaultCodeBlock>
  )
}

Formatting

The CodeBlock source text is formatted by default using prettier if it is installed within the workspace. The shouldFormat prop can be used to disable this behavior:

<CodeBlock language="ts" shouldFormat={false}>
  const foo = 'bar'
</CodeBlock>

Examples

  • const beep = 'boop'
    import { CodeBlock } from 'renoun'
    
    export function Basic() {
      return <CodeBlock language="ts">const beep = 'boop'</CodeBlock>
    }
  • Type Checking

    View Source
    const a = 1
    a + b
    import { CodeBlock } from 'renoun'
    
    export function TypeChecking() {
      return (
        <CodeBlock language="ts" allowCopy={false} allowErrors showErrors>
          const a = 1; a + b;
        </CodeBlock>
      )
    }
  • Ordered

    View Source
    example.ts
    const a = 1
    example.ts
    const a = 1
    const b = 2
    import { CodeBlock } from 'renoun'
    
    export function Ordered() {
      return (
        <div style={{ display: 'grid', gap: '2rem' }}>
          <CodeBlock path="01.example.ts">const a = 1;</CodeBlock>
          <CodeBlock path="02.example.ts">const a = 1; const b = 2;</CodeBlock>
        </div>
      )
    }
  • Line Numbering

    View Source
    line-numbers.ts
    1
    2
    3
    4
    5
    const a = 1
    const b = 2
    
    const add = a + b
    const subtract = a - b
    import { CodeBlock } from 'renoun'
    
    export function LineNumbering() {
      return (
        <CodeBlock path="line-numbers.ts" showLineNumbers highlightedLines="4">
          {`const a = 1;\nconst b = 2;\n\nconst add = a + b\nconst subtract = a - b`}
        </CodeBlock>
      )
    }
  • Line Highlighting

    View Source
    line-highlight.ts
    const a = 1
    const b = 2
    
    const add = a + b
    const subtract = a - b
    import { CodeBlock } from 'renoun'
    
    export function LineHighlighting() {
      return (
        <CodeBlock path="line-highlight.ts" highlightedLines="2, 4">
          {`const a = 1;\nconst b = 2;\n\nconst add = a + b\nconst subtract = a - b`}
        </CodeBlock>
      )
    }
  • Line Focusing

    View Source
    line-focus.ts
    const a = 1
    const b = 2
    
    const add = a + b
    const subtract = a - b
    import { CodeBlock } from 'renoun'
    
    export function LineFocusing() {
      return (
        <CodeBlock path="line-focus.ts" focusedLines="2, 4">
          {`const a = 1;\nconst b = 2;\n\nconst add = a + b\nconst subtract = a - b`}
        </CodeBlock>
      )
    }
  • Line Highlight and Focus

    View Source
    line-highlight-and-focus.ts
    const a = 1
    const b = 2
    
    const add = a + b
    const subtract = a - b
    import { CodeBlock } from 'renoun'
    
    export function LineHighlightAndFocus() {
      return (
        <CodeBlock
          path="line-highlight-and-focus.ts"
          highlightedLines="2, 4"
          focusedLines="2, 4"
        >
          {`const a = 1;\nconst b = 2;\n\nconst add = a + b\nconst subtract = a - b`}
        </CodeBlock>
      )
    }
  • Tokens Only

    View Source
    const a = 1
    const b = 2
    a + b
    import { Tokens } from 'renoun'
    
    export function TokensOnly() {
      return (
        <pre>
          <Tokens language="ts">{`const a = 1\nconst b = 2\na + b`}</Tokens>
        </pre>
      )
    }
  • Custom Styles

    View Source
    ./counter/Counter.tsx
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    'use client'
    import React from 'react'
    import { useCounter } from './useCounter.js'
    
    export default function Counter({ initialCount }: { initialCount: number }) {
      const { count, decrement, increment } = useCounter(initialCount)
      return (
        <div>
          <button onClick={decrement}>-</button>
          <span>{count}</span>
          <button onClick={increment}>+</button>
        </div>
      )
    }
    import { CodeBlock, LineNumbers, Tokens, Toolbar } from 'renoun'
    import { dirname, join } from 'node:path'
    import { fileURLToPath } from 'node:url'
    import { readFile } from 'node:fs/promises'
    
    const directoryPath = dirname(fileURLToPath(import.meta.url))
    
    export async function CustomStyles() {
      const code = await readFile(
        join(directoryPath, './counter/Counter.tsx'),
        'utf-8'
      )
    
      return (
        <CodeBlock path="./counter/Counter.tsx" baseDirectory={directoryPath}>
          <div
            style={{
              fontSize: '1rem',
              borderRadius: '0.25rem',
              boxShadow: '0 0 0 1px var(--color-separator)',
            }}
          >
            <Toolbar
              allowCopy
              css={{
                padding: '0.5lh',
                boxShadow: 'inset 0 -1px 0 0 var(--color-separator)',
              }}
            />
            <pre
              style={{
                display: 'grid',
                gridTemplateColumns: 'min-content max-content',
                padding: '0.5lh 0',
                lineHeight: 1.4,
                whiteSpace: 'pre',
                wordWrap: 'break-word',
                overflow: 'auto',
              }}
            >
              <LineNumbers
                css={{
                  padding: '0 0.5lh',
                  backgroundColor: 'var(--color-background)',
                }}
              />
              <code style={{ paddingRight: '0.5lh' }}>
                <Tokens>{code}</Tokens>
              </code>
            </pre>
          </div>
        </CodeBlock>
      )
    }

API Reference