Bootstrap Code Block With Copy + Line Numbers — Free HTML CSS JS Snippet
Bootstrap Code Block With Copy + Line Numbers · Layouts · Plain HTML, CSS & JS · Live preview
Copy & paste
Source Code
<div class="container py-5 d-flex justify-content-center">
<div class="card bscode-card">
<div class="bscode-header">
<span class="small fw-semibold">install.sh</span>
<button type="button" class="btn btn-sm btn-outline-light" id="bscodeCopy">Copy</button>
</div>
<div class="bscode-body">
<div class="bscode-gutter" id="bscodeGutter"></div>
<pre class="bscode-pre" id="bscodePre"></pre>
</div>
</div>
</div>.bscode-card { width: 460px; max-width: 100%; border-radius: 14px; overflow: hidden; border: 1px solid #2c2e36; }
.bscode-header {
display: flex; justify-content: space-between; align-items: center;
padding: 8px 14px; background: #1e1f26; color: #e3e4e8;
}
.bscode-body { display: flex; background: #14151a; }
.bscode-gutter {
padding: 12px 10px; text-align: right; color: #5b5f6b;
font: 12.5px/1.6 ui-monospace, Menlo, Consolas, monospace; user-select: none; flex-shrink: 0;
}
.bscode-pre {
margin: 0; padding: 12px 14px; color: #e1e4e8; overflow-x: auto; flex: 1;
font: 12.5px/1.6 ui-monospace, Menlo, Consolas, monospace; white-space: pre;
}
.bscode-pre .bscode-comment { color: #6b7280; }
.bscode-pre .bscode-string { color: #86efac; }const CODE = [
'#!/usr/bin/env bash',
'# Installs the CLI and links it onto your PATH',
'set -euo pipefail',
'',
'curl -fsSL "https://example.com/cli.tar.gz" -o cli.tar.gz',
'tar -xzf cli.tar.gz',
'sudo mv cli /usr/local/bin/cli',
'',
'echo "Installed. Run \'cli --version\' to confirm."',
];
const gutter = document.getElementById('bscodeGutter');
const pre = document.getElementById('bscodePre');
const copyBtn = document.getElementById('bscodeCopy');
function highlight(line) {
if (line.trim().startsWith('#')) return '<span class="bscode-comment">' + line + '</span>';
return line.replace(/"([^"]*)"/g, '<span class="bscode-string">"$1"</span>');
}
gutter.innerHTML = CODE.map((_, i) => (i + 1)).join('<br>');
pre.innerHTML = CODE.map(highlight).join('\n');
copyBtn.addEventListener('click', () => {
navigator.clipboard.writeText(CODE.join('\n')).then(() => {
const original = copyBtn.textContent;
copyBtn.textContent = 'Copied!';
setTimeout(() => { copyBtn.textContent = original; }, 1500);
});
});Extend with AI
Take This Snippet Further With AI
The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.
What's included
Features
About this UI Snippet
Bootstrap Code Block With Copy + Line Numbers — HTML, CSS & JavaScript

Keeping the line-number gutter and the code perfectly aligned is the entire design challenge here, and this snippet solves it by generating both from the exact same CODE array — gutter.innerHTML maps over CODE.length to produce sequential numbers, and pre.innerHTML maps over the same array to render each line, so a gutter number and its code line can never drift apart even if lines are added or removed, since both are derived from one array's length and index rather than counted separately by hand.
Highlighting is deliberately minimal and line-based: highlight() checks whether a trimmed line starts with # to color it as a comment, and separately wraps any double-quoted text in a string-colored span — good enough for a shell script or a config snippet without pulling in a full tokenizing syntax highlighter for a handful of lines.
The important correctness detail is what "Copy" actually copies: CODE.join('\n') — the original, plain-text array — not the DOM's rendered innerHTML. Copying rendered HTML would paste literal <span class="bscode-comment"> tags into whatever the user pastes into; copying from the source array guarantees the clipboard always holds exactly the real code, with no highlighting markup ever leaking into it.
Step by step
How to Use
- 1Load the snippetA dark code block appears with line numbers 1 through 9 in the gutter, comments and quoted strings colored differently from plain code.
- 2Scroll the code horizontally if a line is longThe gutter stays fixed in place while only the code pane scrolls, since they're two separate flex children.
- 3Click "Copy"The button briefly reads "Copied!" before reverting, and the clipboard holds the plain script text with no HTML markup in it.
- 4Paste it anywhereIt pastes as clean, runnable shell script text, line numbers and coloring both correctly excluded.
Real-world uses
Common Use Cases
Got questions?
Frequently Asked Questions
For a short, illustrative code block, a full tokenizer is often more weight than the content warrants — the line-based comment/string check here covers the common visual cases with zero dependencies, though swapping in highlight.js is a reasonable upgrade for genuinely complex multi-language code.
Nothing else needs to change — both the gutter and the code pane re-derive their length and numbering from CODE.length automatically the next time the page loads.
No — CODE.join('\n') copies only the actual script content; the numbers exist purely in the separate gutter element and were never part of the copied text to begin with.
Yes. Keep CODE as a plain array, map it to both the gutter numbers and the highlighted lines in the render function, and keep the Copy handler joining the original array rather than reading back from the rendered DOM.