Code Block v0.3.0

Styled with Varvara CSS v0.9.5

A reusable Web Component that renders code blocks with syntax highlighting and a copy button.

<details class="va-collapse">
  <summary>
    <strong>The purpose of thought</strong>
  </summary>
  <p>
    We can attain wisdom by three ways: first, by way of experience,
    this is the most difficult way; second, by way of imitation,
    this is the easiest way; and third by way of meditation,
    and this is the noblest way.
  </p>
  <p>Confucius</p>
</details>
<va-code-block>
  <pre><code class="language-html">&lt;details class="va-collapse"&gt;
  &lt;summary&gt;
    &lt;strong&gt;The purpose of thought&lt;/strong&gt;
  &lt;/summary&gt;
  &lt;p&gt;
    We can attain wisdom by three ways: first, by way of experience,
    this is the most difficult way; second, by way of imitation,
    this is the easiest way; and third by way of meditation,
    and this is the noblest way.
  &lt;/p&gt;
  &lt;p&gt;Confucius&lt;/p&gt;
&lt;/details&gt;</code></pre>
</va-code-block>
This component is in an early development stage. Check the CHANGELOG.md to stay up to date with the latest changes.

Installation

Using a CDN

Add the script to your page and the <va-code-block> element is ready to use:

<script src="https://unpkg.com/va-code-block"></script>

Using NPM

npm i va-code-block

Then import the package once in your JavaScript. Importing it registers the element:

import 'va-code-block'

Styles are bundled inside the component, so you don’t need to include Varvara CSS to use it.

Usage

With a code fallback

Place the code inside the element as a regular <pre><code> block, with the language as a language-* class. It stays readable for crawlers, AI agents and browsers without JavaScript, and is replaced by the highlighted version once the component loads:

<va-code-block>
  <pre><code class="language-html">&lt;button class="va-button"&gt;Example button&lt;/button&gt;</code></pre>
</va-code-block>

As in any HTML, write < and & inside the code as &lt; and &amp;.

With the text attribute

Alternatively, pass the code as a string in the text attribute:

<va-code-block
  language="html"
  text='<button class="va-button">Example button</button>'
></va-code-block>

The code can’t contain the quote character that wraps the attribute: use single quotes around code with double quotes, or the other way round. If both text and child content are present, text takes precedence.

Multiple languages

Place several <pre><code> blocks inside the element and a select appears to switch between them. Each block takes its language from its language-* class, and its label from data-label on the <pre> (or the uppercased language):

<button class="va-button">Example button</button>
const Button = () => <button className="va-button">Example button</button>
.va-button {
  --va-button-padding: var(--va-space-4);
}
<va-code-block>
  <pre><code class="language-html">&lt;button class="va-button"&gt;Example button&lt;/button&gt;</code></pre>
  <pre data-label="JSX"><code class="language-jsx">const Button = () =&gt; &lt;button className="va-button"&gt;Example button&lt;/button&gt;</code></pre>
  <pre><code class="language-css">.va-button {
  --va-button-padding: var(--va-space-4);
}</code></pre>
</va-code-block>

Labels are useful when several blocks share a language. Use selected to choose the initial block:

npm i va-code-block
pnpm add va-code-block
bun add va-code-block
<va-code-block selected="bun">
  <pre data-label="npm"><code class="language-bash">npm i va-code-block</code></pre>
  <pre data-label="pnpm"><code class="language-bash">pnpm add va-code-block</code></pre>
  <pre data-label="bun"><code class="language-bash">bun add va-code-block</code></pre>
</va-code-block>

The copy button copies the active block. Without JavaScript, all blocks remain readable one after another.

Dynamic updates

The component reacts to changes in its attributes and in its child content, so you can update it after the initial render:

const el = document.querySelector('va-code-block')
el.setAttribute('text', 'console.log("hello")')
el.setAttribute('language', 'js')

Attributes

Attribute Description Default value
text The code snippet to display. Falls back to the child content. ''
language Language for syntax highlighting. A block’s language-* class takes precedence. Also available as lang. 'text'
selected Active block when there are several, matched by data-label or language. Updated when the user picks another block. First block

Events

Event Description
change Fired when the user selects another block. event.detail contains label, language and value.
el.addEventListener('change', (event) => {
  console.log(event.detail.value)
})

Styling

The component renders inside a shadow root. Its internal elements are exposed as parts that you can style with ::part():

Part Element
card The outer container.
pre-content The <pre> that holds the highlighted code.
select The language select, shown when there are several blocks.
va-code-block::part(pre-content) {
  max-height: 20rem;
  overflow: auto;
}

Supported languages

Syntax highlighting is built-in with a lightweight tokenizer. No external dependencies required.

Language Values
JavaScript javascript, js, jsx
TypeScript typescript, ts, tsx
CSS css
HTML html, markup, xml

Other languages, such as bash, render as plain text without errors and still show their label.

More information