Adding code blocks to articles
highlight.js 11.11.1
highlight.js 11.11.1
Quick start (CDN + recommended v11 initialization)
<head>
</body>
defer
hljs.highlightAll()
<!-- Theme (light by default) -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github.min.css">
<!-- Library (full build) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js" defer></script>
<!-- Initialization for v11 -->
<script>
document.addEventListener('DOMContentLoaded', function () {
hljs.highlightAll();
});
</script>
Minimum markup of the code block
<pre><code class="language-php">
<?php
echo "Hello, world!\n";
</code></pre>
Recommendations:
class="language-php"
language-js
language-bash
<
>
Markdown and "clean" HTML
Markdown:
```php
echo "Hello, world!\n";
```
HTML:
<pre><code class="language-html"><div class="card">…</div></code></pre>
Dynamically added code (SPA/editor/AJAX)
hljs.highlightElement(el)
<script>
function highlightNewCode(root = document) {
root.querySelectorAll('pre code').forEach(function (block) {
hljs.highlightElement(block);
});
}
// Пример:
// const frag = document.createRange().createContextualFragment('<pre><code class="language-js">const x=42;</code></pre>');
// document.body.appendChild(frag);
// highlightNewCode(frag);
</script>
Themes, light/dark scheme
prefers-color-scheme
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github.min.css"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/monokai-sublime.min.css"
media="(prefers-color-scheme: dark)">
github
atom-one-dark
monokai-sublime
solarized-light
vs2015
The "Copy" button (UX improvement)
<style>
pre { position: relative; }
.copy-btn {
position: absolute; top: .5rem; right: .5rem;
padding: .25rem .5rem; border: 0; border-radius: .375rem;
background: rgba(0,0,0,.6); color: #fff; font: 12px/1 system-ui, sans-serif;
cursor: pointer; opacity: .8;
}
.copy-btn:focus, .copy-btn:hover { opacity: 1; }
pre code { display: block; padding: 1rem; overflow: auto; }
</style>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('pre').forEach(function (pre) {
if (pre.querySelector('.copy-btn')) return;
const btn = document.createElement('button');
btn.className = 'copy-btn';
btn.type = 'button';
btn.setAttribute('aria-label', 'Скопировать код');
btn.textContent = 'Copy';
btn.addEventListener('click', async function () {
const text = pre.querySelector('code')?.innerText || '';
try {
await navigator.clipboard.writeText(text);
btn.textContent = 'Copied!';
} catch {
const ta = document.createElement('textarea');
ta.value = text; document.body.appendChild(ta);
ta.select(); document.execCommand('copy'); ta.remove();
btn.textContent = 'Copied!';
}
setTimeout(() => btn.textContent = 'Copy', 1200);
});
pre.appendChild(btn);
});
});
</script>
Line numbering (optional)
highlightjs-line-numbers.js
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.8.0/styles/line-numbers.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlightjs-line-numbers.js/2.8.0/highlightjs-line-numbers.min.js" defer></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
hljs.highlightAll(); // подсветка
if (hljs.initLineNumbersOnLoad) {
hljs.initLineNumbersOnLoad(); // нумерация
}
});
</script>
<!-- Добавьте класс line-numbers к <pre> -->
<pre class="line-numbers"><code class="language-js">const x = 42;</code></pre>
dynamically
hljs.highlightElement(block); if (window.hljs?.lineNumbersBlock) hljs.lineNumbersBlock(block);
Efficiency
Only the necessary languages.
<!-- Минимальное ядро --> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/core.min.js" defer></script> <!-- Нужные языки --> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/php.min.js" defer></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/javascript.min.js" defer></script> <script> document.addEventListener('DOMContentLoaded', () => hljs.highlightAll()); </script>
Disable auto-detection.
language-xxx
Delayed loading.
defer
Long fragments.
overflow: auto;
Accessibility (a11y) and amenities
<figure>
<figure> <figcaption>Example of an API request (PHP)</figcaption> <pre><code class="language-php">...</code></pre> </figure>
Give the "Copy" button visible focus.
tab-size: 2;
pre code { tab-size: 2; }
Security when inserting the code
If the code comes from users (comments, CMS plugins):
Sanitize the input HTML on the server/client.
textContent
innerHTML
const code = document.createElement('code'); code.className = 'language-js'; code.textContent = 'alert("hi")'; // safe
Common mistakes when switching to v11
The old initialization.
hljs.initHighlightingOnLoad()
hljs.highlightBlock(...)
hljs.highlightAll()
hljs.highlightElement(...)
There is no explicit language.
Unshielded HTML code.
Reconnecting themes/scripts.
We connected the "full" bundle on each page.
Alternatives
Prism.js
Shiki
hljs 11.11.1
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Демо подсветки кода (hljs 11.11.1)</title>
<!-- Светлая/тёмная темы -->
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/github.min.css"
media="(prefers-color-scheme: light)">
<link rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/monokai-sublime.min.css"
media="(prefers-color-scheme: dark)">
<style>
body { margin: 2rem; font: 16px/1.6 system-ui, -apple-system, Segoe UI, Roboto, sans-serif; }
pre { position: relative; margin: 1rem 0; border-radius: .5rem; }
pre code { display: block; padding: 1rem; overflow: auto; }
.copy-btn {
position: absolute; top: .5rem; right: .5rem;
padding: .25rem .5rem; border: 0; border-radius: .375rem;
background: rgba(0,0,0,.6); color: #fff; font-size: 12px; cursor: pointer; opacity: .85;
}
.copy-btn:focus, .copy-btn:hover { opacity: 1; outline: 2px solid rgba(0,0,0,.2); }
</style>
<!-- Библиотека: ядро + нужные языки -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/core.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/php.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/languages/javascript.min.js" defer></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
hljs.highlightAll();
// Кнопки «Copy»
document.querySelectorAll('pre').forEach(function (pre) {
const btn = document.createElement('button');
btn.className = 'copy-btn'; btn.type = 'button'; btn.textContent = 'Copy';
btn.setAttribute('aria-label', 'Скопировать код');
btn.addEventListener('click', async function () {
const text = pre.querySelector('code')?.innerText || '';
try { await navigator.clipboard.writeText(text); btn.textContent = 'Copied!'; }
catch {}
setTimeout(() => btn.textContent = 'Copy', 1200);
});
pre.appendChild(btn);
});
});
</script>
</head>
<body>
<h1>Пример подсветки</h1>
<figure>
<figcaption>PHP: базовый вывод</figcaption>
<pre><code class="language-php"><?php echo "Hello, world!\n"; ?></code></pre>
</figure>
<figure>
<figcaption>JavaScript: функция суммы</figcaption>
<pre><code class="language-javascript">const sum = (a, b) => a + b;</code></pre>
</figure>
</body>
</html>
didn't run in the browser?
show code as text
Static markup (Markdown/HTML)
Markdown-fences
```html <script>alert(1)</script> ```
screen it
<
>
&
<pre><code class="language-html"><script>alert(1)</script></code></pre>
Dynamic insertion (SPA, AJAX, editors)
innerHTML
textContent
createTextNode
<script>
function insertCode(lang, rawText, root = document.body) {
const pre = document.createElement('pre');
const code = document.createElement('code');
code.className = 'language-' + lang; // example: language-html
code.textContent = rawText; // ← safe: tags are not parsed
pre.appendChild(code);
root.appendChild(pre);
hljs.highlightElement(code); // hljs 11.x
}
// Пример:
// insertCode('html', '<script>alert(1)</script>');
</script>
Sanitizing user input
sanitize<script>
on*
javascript:
<style>
<iframe>
textContent
CSP is a protective "grid" at the header level
Content-Security-Policy
# Пример для Nginx (адаптируйте под свой стек)
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' https://cdnjs.cloudflare.com;
style-src 'self' https://cdnjs.cloudflare.com 'unsafe-inline';
img-src 'self' data:;
object-src 'none';
base-uri 'none';
frame-ancestors 'none';
" always;
https://cdnjs.cloudflare.com
script-src 'unsafe-inline'
Templating tools and engines
Blade (Laravel):
@if
{{ }}
@verbatim … @endverbatim
Twig/You need to literally output the tags:
verbatim
Vue/React in the text:
{{ mustache }}
Sandbox (for previewing someone else's HTML)
ready-made HTML<iframe sandbox>
withoutallow-scripts
<iframe
sandbox
srcdoc="<h1>This is just a preview</h1><script>alert(1)</script>">
</iframe>
Scripts inside such an iframe will not be executed. To highlight the "code" it is better to stick to 13.1–13.2.
Total
textContent
innerHTML
<script>…</script>