Adding code blocks to articles
highlight.js 11.11.1
highlight.js 11.11.1
Quick start (CDN + recommended v11 initialization)
<head></body>deferhljs.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-jslanguage-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)">githubatom-one-darkmonokai-sublimesolarized-lightvs2015
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-xxxDelayed loading.
deferLong 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.
textContentinnerHTMLconst 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)
innerHTMLtextContentcreateTextNode
<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.comscript-src 'unsafe-inline'
Templating tools and engines
Blade (Laravel):
@if{{ }}@verbatim … @endverbatimTwig/You need to literally output the tags:
verbatimVue/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
textContentinnerHTML<script>…</script>