Multi-Tab Code Block with Syntax Highlighting — UI Snippet

Multi-Tab Code Block · Cards · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

Multi-file tabbed navigation with active underline indicator
Token-based syntax highlighting (kw, fn, str, num, prop, cm, pun)
Line number column with user-select: none
macOS traffic-light dots for the IDE aesthetic
Horizontal scroll for long lines with hidden scrollbar
Copy button reconstructing plain text from the token array
Iframe-safe copy with execCommand fallback
Dark theme designed for developer documentation

About this UI Snippet

Multi-Tab Code Block — File Tabs, Syntax Tokens, Line Numbers & Copy

Screenshot of the Multi-Tab Code Block snippet rendered live

A multi-tab code block is among the most-copied UI patterns in developer documentation, tutorials, and component libraries. Showing multiple related files side by side in a single card (JavaScript, CSS, config) gives readers the full context of a code example without cluttering the page — for a single-file version, see the code block. This snippet provides a complete, dark-themed code viewer with macOS-style traffic-light dots, file-name tabs, token-based syntax highlighting, line numbers, and a copy button — no external libraries required.

Token-based syntax highlighting

Rather than running a regex over the raw source string (which is fragile and order-dependent), each file's content is stored as an array of token objects: { t: 'kw', v: 'const' } for a keyword, { t: 'str', v: '"hello"' } for a string, and so on. The renderer maps over these tokens, wrapping each in a span with the appropriate CSS class. Token types include kw (keyword), fn (function), str (string), num (number), prop (property/selector), cm (comment), and pun (punctuation/operator). This approach is robust, predictable, and trivially extensible with new token types.

Line numbers

The line number column is rendered from the token array length and displayed in a separate container floated left of the code block. User-select: none prevents line numbers from being selected on code copy. The two elements share a horizontal scroll container so the numbers and code always scroll together.

File tabs and active state

switchTab() updates the active tab indicator, clears the copy button label, and calls render() with the new index. The tabs container uses overflow-x: auto with hidden scrollbar so long file lists are scrollable on mobile without overflowing the card.

Copy button

copyCode() reconstructs the plain-text content from the token array by joining all v values, then uses the Clipboard API with an execCommand fallback. The button briefly shows "Copied!" so the user knows the action succeeded.

The macOS aesthetic

The three traffic-light dots in the top-left corner are a widely recognised signal for "this is a code editor or terminal window." They set the visual register immediately, making the code block feel like a real IDE window rather than a styled textarea, which increases perceived code quality and authority in documentation contexts.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Rather than assuming the token-array approach is just "syntax highlighting", paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why storing code as an array of { t, v } token objects avoids the ordering bugs that a regex-based highlighter would hit, and how the render() function distinguishes a plain string line, a token array line, and a single-token line. The same assistant can help optimize it — ask whether reconstructing the entire innerHTML string on every single tab switch is the right approach compared to pre-rendering all three files once and toggling visibility, especially if there were dozens of tabs. It's also useful for extending the component: ask it to add line-highlighting for specific "changed" lines in a diff-style view, support a fourth tokenizeable language by writing a small tokenizer function, or make the copy button copy only a selected range of lines instead of the whole file. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a "multi-tab code block" in plain HTML, CSS, and JavaScript — no syntax-highlighting library, no build step.

Requirements:
- Represent each file's source code as an array where every element is either a plain string (a comment-only or unstyled line), a single token object with a type and value, or an array of token objects representing one line split into multiple styled pieces — not a raw HTML string and not a single regex pass over the whole file.
- Support at least these token types with distinct colors: keyword, function name, string, number, property/selector, comment, and punctuation — and write a single render function that walks the current file's line array and wraps each token in a span with the matching CSS class, correctly handling all three line-shape cases (string, single token, array of tokens).
- HTML-escape every token's raw value before inserting it into the DOM (ampersands, angle brackets) so source code containing HTML-like characters displays correctly instead of being interpreted as markup.
- A row of file-name tabs above the code area; clicking a tab must update the active tab's visual state, reset the copy button's label back to its default, and re-render both the code and a matching line-number column for the newly selected file.
- A copy button that reconstructs the plain, unstyled source text of the currently active file by joining just the value fields of every token (not the rendered HTML with span tags), copies it via the Clipboard API with a textarea-based execCommand fallback for browsers without clipboard API support, and shows a temporary "Copied!" confirmation that reverts after a couple of seconds.
- Style the whole block like a code editor window: macOS-style traffic-light dots, a dark background, and a horizontally scrollable line-numbers-plus-code area for long lines, with line numbers marked non-selectable so they're never included in a text selection.

Step by step

How to Use

  1. 1
    Switch filesClick any tab to view a different file. The code, line numbers, and syntax highlighting all update instantly.
  2. 2
    Copy the codeClick Copy to copy the plain-text source of the active file to your clipboard. The button briefly confirms with "Copied!".
  3. 3
    Add your own filesAppend an object to the files array with name, lang, and content (an array of token arrays). Tokenise your code into kw, fn, str, num, prop, cm, and pun spans.
  4. 4
    Swap the contentReplace the sample files array with your own code snippets. Each row in content is one line, expressed as an array of token objects.
  5. 5
    Style your themeChange the colour variables in the CSS to match your documentation site's colour palette. The dark default matches most developer docs.
  6. 6
    Export for your frameworkClick "React" for a component accepting a files prop with tab state in useState. Click "Vue" for a Vue 3 SFC with reactive activeIdx.

Real-world uses

Common Use Cases

Component library and API documentation
Show the HTML, CSS, and JavaScript (or TSX, types, and test files) of every documented component in a single, tabbed code card. Users see the complete implementation without navigating away, and the copy button lets them grab any file with one click. The macOS chrome adds authority to your documentation site.
Tutorial and course code examples
Technical tutorials frequently need to show starter code, the completed version, and a config file simultaneously. The multi-tab block puts all three in one card, keeping the tutorial page clean while giving learners access to every file. Tab labels can be "Before" and "After" for refactoring examples.
Marketing and landing page code showcase
SaaS products that sell to developers use live, syntax-highlighted code samples in hero sections and feature breakdowns to demonstrate SDK simplicity. A multi-tab block showing initialise + configure + use in three files communicates the integration flow at a glance.
Code review or diff viewer UI
Extend the tab model to show original and modified versions of a file side by side, or add a diff indicator to lines that changed. The token model makes it easy to add a "changed" background highlight to specific lines by adding a class to that line's container.
AI coding assistant output display
AI code assistants often return multiple files (component, styles, test). Render the response in a tabbed code block so users can see all generated files in one place, switch between them, and copy each individually. This is the standard display used by tools like Claude Artifacts and ChatGPT code blocks.
Embedded in a no-code or CMS platform
Add the code block component to a rich-text or Markdown editor so content authors can insert styled, tabbed code examples without writing HTML. The tabs, syntax highlighting, and copy button work automatically once the author provides the file name and content.

Got questions?

Frequently Asked Questions

Regex-based highlighters apply patterns to a raw string in sequence, and the order matters — a string literal pattern can accidentally match inside a comment, and overlapping matches corrupt the output. Storing the code as a pre-tokenised array where each token has an explicit type gives deterministic output: the renderer just wraps each token in its class. Adding a new language is adding a new tokenisation function, not fighting regex order-of-operations.

copyCode() reconstructs the plain text by mapping over the token array and joining the v (value) fields of every token. This produces the exact source code without any span tags, so the clipboard gets clean, pasteable code. If your token model is different, join the text content of all spans in the code element using textContent instead.

Add a new entry to the files array with a name, a lang, and a content array. Tokenise the source manually (matching the existing token types) or write a small tokeniser function that walks the source string character by character, building token objects as it recognises keywords, strings, numbers, and comments for your target language. For languages not in the existing set, adding a custom token type with a new CSS colour class is trivial.

Accept a files prop (array of {name, content} objects). Keep activeIdx in useState. Render the tab bar from files.map(). The code area maps content[activeIdx] to JSX spans per token type. The copy handler reconstructs plain text from the active file's token array and calls navigator.clipboard.writeText. Use a key prop on the code container matching activeIdx to reset scroll on tab change. For the Tailwind version, click "Tailwind" to get the same markup with utility classes instead of a scoped stylesheet.