
Code Syntax Highlighter
Paste code, choose a language, get highlighted HTML for blogs, emails, and documentation.
Last reviewed: April 2026New to this tool? Click here for instructions
Paste any source code, pick its language (or let auto-detection guess), and walk away with cleanly highlighted HTML you can drop into a blog post, an email newsletter, a documentation page, or a slide deck. Everything runs in your browser — no source code is ever uploaded.
What This Tool Does
This tool takes a block of source code, splits it into semantic tokens (keywords, strings, numbers, comments, function names, operators), wraps each token in a <span> with a class that identifies its role, and returns the resulting HTML in one of three flavors: a class-only version that needs an external stylesheet, an inline-styled version where every color is baked directly onto each span, and a stylesheet-paired version that uses semantic class names like kw, str, and cmt alongside a small built-in theme. You see the rendered preview on the right side of the workspace, and the raw HTML appears below for copying or downloading.
The five built-in language grammars cover the most common scenarios for blog and documentation authors: JavaScript (including arrow functions, template literals, and async/await), Python (including triple-quoted strings and decorators), HTML and XML (tags, attributes, attribute values), CSS (selectors, properties, units), and SQL (case-insensitive keywords plus comment styles). The Auto-detect mode looks for distinctive lexical markers — angle brackets and slashes for HTML, def and import for Python, SELECT...FROM for SQL, brace-and-colon-and-semicolon patterns for CSS — and falls back to JavaScript when nothing distinctive appears. Every computation runs client-side on the page you are reading; nothing is sent to a server.
For output, the three modes serve different distribution channels. Styled HTML writes <pre class="hl-dark"> wrappers with semantic span classes — the right choice when you control the destination CSS. Inline Styled writes every color directly onto the span as a style attribute, which is the only mode that survives most email clients and the only mode that renders correctly when the destination page has no syntax-highlighting stylesheet. CSS Classes writes the same class names as Styled HTML but expects you to provide your own theme — useful when you want to match your site's existing palette rather than the built-in dark or light themes.
How to Use It
The interface is split into a code input on the left and a rendered preview on the right, with the raw HTML output beneath. To highlight a snippet, paste it into the input pane and the preview updates within 200 milliseconds — there is no Render button to click. Above the input you will find three rows of option chips that control language, output format, and theme.
The language row begins with Auto and continues through the five supported grammars: JavaScript, Python, HTML, CSS, SQL. Auto inspects the first several hundred characters of your input for distinctive patterns and chooses the closest match. If your snippet is short or uses generic identifiers, Auto can guess wrong — explicitly clicking the correct language chip will always override detection. The status bar at the bottom of the workspace shows which grammar was applied for the current render, including the (auto-detected) suffix when Auto made the call.
The output row selects between Styled HTML, Inline Styled, and CSS Classes. Styled HTML and CSS Classes both emit the same span markup; the difference is that Styled HTML pairs with a built-in hl-dark or hl-light wrapper class that activates the theme colors via this page's stylesheet, while CSS Classes emits semantic class names alone and assumes the destination page provides its own stylesheet. Inline Styled bakes every color into a style="color:#..." attribute on each span — bulkier output, but it renders correctly inside any HTML rendering surface, including email clients that strip <style> blocks.
The options row toggles the theme (dark or light) and turns line numbers on or off. The dark theme draws from the Catppuccin Mocha palette — soft purple keywords on a slate background — while the light theme uses GitHub-style coloring with high contrast against a near-white background. Line numbers are rendered via CSS counters rather than as text content, so copying the highlighted HTML still produces clean lines without numeric prefixes when the destination page does not include the with-ln class.
When the output looks correct, click Copy HTML to put the markup on the clipboard or Download to save it as highlighted.html. For documentation builds, you typically want the Styled HTML or CSS Classes flavor paired with a theme stylesheet committed alongside your content. For one-off blog posts and emails, Inline Styled is almost always the right choice because it removes the dependency on an external stylesheet entirely. The Try Example button loads a 19-line async/await JavaScript function that exercises every token category — useful for previewing how a theme will look before committing to it.
Worked Example: Highlighting an Async JavaScript Function
To make the token classification concrete, work through this 12-line example. The snippet fetches a paginated list of users from an API and normalizes the response. Before highlighting it appears as a wall of unstyled monospace text:
async function fetchUsers(page = 1) {
// Fetch paginated user list from API
const BASE_URL = "https://api.example.com";
const url = `${BASE_URL}/users?page=${page}&limit=20`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
return data.users.map(user => ({
id: user.id,
email: user.email.toLowerCase()
}));
}
After running it through the highlighter with JavaScript selected and the dark theme active, the tool classifies each token and wraps it in a span. Keywords — async, function, const, await, if, throw, new, return — render in soft purple via the kw class. String literals — both the double-quoted "https://api.example.com" and the template literal with embedded interpolation — render in green via the str class; note that the ${BASE_URL} placeholder inside the template literal is colored as part of the string rather than re-tokenized as an expression, which is a known limitation of regex-based tokenization. Numbers — 1 in the default parameter and 20 in the URL — render in orange via the num class. Comments — the single line beginning with // — render in muted gray italic via the cmt class. Function names — fetchUsers, fetch, Error, json, map, toLowerCase — render in light blue via the fn class because they appear immediately before an open parenthesis.
The order of classification matters. The tokenizer scans comments first, replacing each // line with a placeholder so that any keywords or numbers inside the comment text are not re-classified. Strings come next, with the same placeholder strategy — this is what prevents the word throw inside an error message from being colored as a keyword. Numbers, function names, keywords, and operators are then applied to whatever remains. Finally, all placeholders are expanded back into their original span markup. The single-pass design makes the tokenizer fast enough to run on every keystroke without perceptible lag, even for files in the thousands of lines.
Common Use Cases
Highlighted code shows up across many writing contexts, and each context has a slightly different rendering surface. Understanding which output mode fits each surface saves repeated guesswork.
Documentation Sites and README files
Static-site documentation generators — MkDocs, Docusaurus, VitePress, Hugo, Jekyll — typically run a syntax highlighter at build time and emit class-only spans paired with a theme stylesheet shipped in the site bundle. If you author content in a different environment but need to paste highlighted code into a doc page directly, the CSS Classes output mode integrates cleanly: the spans match the same class names most generators emit (token keyword, token string, etc., or in this tool's case kw, str), and your site's existing theme stylesheet styles them automatically.
Blog posts on platforms that lack a built-in highlighter
Medium, Substack, Ghost, and most WordPress installations without a code-block plugin treat fenced code blocks as plain monospace text. The Inline Styled output mode is the only reliable option here: paste the styled HTML directly into the rich-text editor (most editors accept HTML via Paste Special or via their HTML editor toggle) and the colors render without any further setup. Inline styles also survive WYSIWYG editors that strip <style> blocks but preserve style attributes.
Email newsletters and transactional emails
Almost no email client supports <style> blocks reliably — Gmail strips them, Outlook desktop strips most CSS that is not in a style attribute, and Apple Mail handles them inconsistently. Inline Styled is mandatory for code samples that need to look highlighted in an email. The bulkier output is a small price compared to plain-text fallback. For developer-focused newsletters (changelog announcements, security advisories, API migration guides), highlighted code is the difference between a readable email and an unreadable one.
Stack Overflow answers and technical Q&A
Stack Overflow runs its own highlighter on fenced code blocks, but the result is sometimes wrong for languages outside its default detection set. Pasting pre-highlighted HTML from this tool does not help on Stack Overflow specifically — its sanitizer strips most HTML — but for other Q&A platforms (Discourse-based forums, internal wikis, GitHub Discussions, Notion) the Styled HTML or Inline Styled output renders cleanly inside Markdown.
Technical interviews and screen-share whiteboarding
When walking a candidate or interviewer through a code snippet on a shared screen, paste the snippet into this tool, switch to the dark theme, and share the highlighted preview pane. The contrast is high enough to survive video compression, and being able to point at colored tokens (the purple async, the green template literal, the gray comment) makes the conversation faster than referring to "line 4, the third token from the left."
Code review screenshots and bug reports
For bug reports and pull request descriptions where the project's renderer does not support fenced code blocks, capturing a screenshot of the highlighted preview pane is often clearer than embedding plain text. The Diff Checker serves the same role for showing before-and-after comparisons. For social-media share images announcing a release or feature, the highlighted preview combined with a screenshot tool produces a usable image without any external design work.
Edge Cases and Tokenizer Limitations
Several language constructs are inherently hard for regex-based tokenizers and the limitations are worth understanding before you hit them in real code.
Multi-line strings with escape sequences. JavaScript template literals (`...`), Python triple-quoted strings ("""..."""), and PHP heredocs all span multiple lines and can contain embedded quotes, escape sequences, and even interpolations. The tokenizer treats the entire string body as a single string token — escape sequences like \n or \t inside the string are colored as string content rather than highlighted separately. This is intentional: many highlighters add a second pass to color escape sequences distinctly, but the visual gain is marginal and the implementation cost in regex complexity is significant.
Template literals with embedded expressions. JavaScript template literals support ${expression} interpolation, where the expression inside the braces is full JavaScript — potentially including function calls, ternaries, and nested template literals. A correct highlighter must re-enter language mode inside the braces. This tool colors the entire template literal as one string token, which means identifiers and operators inside ${...} are not separately colored. Prism.js and highlight.js handle this case correctly; Tree-sitter handles it perfectly via grammar-defined nested scopes.
Regex literals versus division operators in JavaScript. The forward slash character (/) is both the regex delimiter and the division operator. Distinguishing a/b/c (division) from return /a/b/g (regex with flags) requires tracking the previous token's syntactic role — division can only follow an expression that produces a value, while a regex literal can follow operators, statements, or commas. Pure regex tokenizers cannot resolve this cleanly, so this tool conservatively treats / as an operator. Regex literals therefore appear as a mix of operator characters and identifier characters rather than as a single string-colored token.
JSX and TSX where < is both operator and JSX delimiter. In JSX or TSX, <Button> is a component tag, but a < b is a less-than comparison. Resolving the difference requires parser context — specifically, knowing whether the parser is currently in expression context or JSX context. This tool's JavaScript grammar treats < uniformly as an operator, so JSX tags are not highlighted as HTML. If you need JSX highlighting, paste the JSX-only portion into the tool with HTML selected; the tag structure will render correctly even though the surrounding JavaScript loses its highlighting.
Python f-strings with embedded expressions. An f-string like f"Hello, {name.upper()}!" mixes string content with executable expressions inside braces. Like template literals, correctly highlighting an f-string requires re-entering Python mode inside each pair of braces. The tool colors the entire f-string as one string token. The visual approximation is acceptable for most readers, who can mentally parse the f-string body once they see the surrounding string color.
Bash heredocs and Perl POD blocks. Shell heredocs (<<EOF ... EOF) and Perl POD documentation blocks define their own terminator strings dynamically, so a tokenizer cannot pattern-match the closing delimiter with a fixed regex. Bash and Perl are not built-in languages here; for documentation that includes shell scripts with heredocs, falling back to JavaScript mode produces a reasonable approximation (single-line comments and strings are colored correctly), and the heredoc body itself ends up as undecorated text.
SQL keyword case-sensitivity. SQL has historically been written with uppercase keywords (SELECT, FROM, WHERE) and lowercase identifiers, but the standard treats keywords as case-insensitive. The tool's SQL grammar matches keywords with the /gi flag, so select and SELECT are both highlighted. Identifiers that happen to share a name with a keyword (a column literally named order) will be colored as a keyword — a known accuracy tradeoff that affects most SQL highlighters in the same way.
Behind the Scenes: How Syntax Highlighting Actually Works
Syntax highlighting is a two-phase pipeline, even when the implementation collapses both phases into a single function. The first phase is tokenization: splitting the raw character stream into discrete tokens (a keyword, a string literal, an identifier, an operator, a comment). The second phase is classification: assigning each token a semantic role (keyword, string, function name) that maps to a visual style. Real-world implementations vary mainly in how rich the tokenizer is and how much context it tracks across token boundaries.
Lexer/Tokenizer vs. Full Parser
A lexer (also called a tokenizer) produces a flat sequence of tokens with no nesting structure. A parser consumes that token sequence and produces a tree — for syntax highlighting purposes, a concrete syntax tree where every interior node is a grammar rule and every leaf is a token with its semantic role. The distinction matters because some classification decisions require tree context that a flat token sequence cannot provide. The foo in foo() is a function call; the foo in foo = 1 is an assignment target. A regex-based tokenizer can sometimes approximate this using lookahead in its patterns (this tool uses (?=\() to detect function calls), but the approximation fails on edge cases like x = foo followed on the next line by (args) as a separate statement.
How Prism.js Works
Prism.js uses a token-tree approach: each language grammar is defined as a JavaScript object where keys are token types and values are regex patterns that match those tokens. Prism applies these patterns in priority order to build a nested token tree, then walks the tree emitting <span class="token keyword"> wrappers. The library supports inside sub-grammars, allowing nested language modes (CSS inside HTML <style> blocks, JavaScript inside <script> blocks). Prism's main strength is its plugin ecosystem (line numbers, copy-to-clipboard, autoloader, command-line styling) and its support for 280+ language grammars.
How highlight.js Works
highlight.js takes a similar regex-based approach but emphasizes language auto-detection. Each language grammar declares a set of patterns the lexer recognizes, plus optional sub-modes for contexts like string interpolation and embedded languages. The auto-detection scorer runs every language grammar against the input and picks the highest-confidence match. This works well on long code samples but produces noisy results on short snippets — which is why most documentation tools that use highlight.js force an explicit language hint via the language-foo class.
How Pygments Works
Pygments is the Python-language highlighter that powers most static site generators (Sphinx, Pelican, MkDocs without its newer Markdown extension), GitHub's source rendering for many languages, and Jupyter notebooks. Pygments uses a state-machine lexer rather than a pure regex tokenizer: each lexer state defines what tokens can appear next, and transitions between states are themselves triggered by regex matches. This allows Pygments to track context that pure regex tokenizers cannot — opening and closing brackets, string interpolation, language transitions — without writing a full parser. Pygments supports 500+ languages and emits flexible output formats (HTML, LaTeX, RTF, ANSI terminal color).
How Tree-sitter Works
Tree-sitter is fundamentally different from the other three. It uses a parser generator (specifically, a generalized LR parser) that produces a complete concrete syntax tree for each language. Tree-sitter grammars are defined in JavaScript-DSL files that specify the language's grammar rules; the parser generator compiles these rules into a fast C parser. Editors then query the parse tree using S-expression-style queries to produce highlight captures. Tree-sitter's killer feature is incremental re-parsing: when you type a character, Tree-sitter re-parses only the affected subtree rather than the whole file, which is why every modern editor (Neovim, Helix, Emacs, GitHub's web code viewer, Zed) uses Tree-sitter for syntax highlighting.
Why Exhaustive Grammars Are Impossible
Some languages cannot be fully tokenized without semantic context. Perl is the canonical example — its grammar is famously undecidable, meaning no parser can correctly classify every token in arbitrary Perl source code. The classic example is /foo/: is it a regex match, a regex literal in list context, or a numerical division? Determining the answer requires knowing the runtime values of surrounding identifiers, which is impossible at parse time. Most Perl highlighters apply heuristics that work in practice but produce occasional misclassifications. Other languages with notoriously hard grammars include C++ (template parsing requires knowing whether identifiers are types or values), Ruby (heredocs and method-vs-local-variable resolution), and TypeScript (type expressions vs. value expressions share much of the same syntax). For these languages, even Tree-sitter occasionally falls back to heuristics rather than complete classification.
Regex Tokenizers Versus PEG Parsers
Regex-based tokenizers (this tool, Prism, highlight.js) compose multiple regex patterns and resolve overlaps via priority order. They are simple, fast, and easy to extend with a new language pattern, but they cannot express grammar rules that depend on context. PEG (Parsing Expression Grammar) parsers and generalized LR parsers like Tree-sitter's can express any context-free grammar and most context-sensitive ones, at the cost of grammar-authoring complexity. For most blog and documentation use cases, regex tokenizers are good enough — the misclassifications they produce are visually acceptable, and the engineering simplicity is worth the accuracy tradeoff.
This Tool vs. Prism.js vs. highlight.js vs. Pygments vs. Tree-sitter
Choosing a syntax highlighter depends on where the highlighting runs (build time, page load, on every keystroke), what languages you need, and how accurate the output must be. The table below summarizes the practical differences.
| Tool | Where It Runs | Languages | Accuracy | Best For |
|---|---|---|---|---|
| This tool (browser regex) | In-page, on user input | 5 (JS, Python, HTML, CSS, SQL) | Approximate — single-pass regex | One-off highlighting for blog posts, emails, docs; embedding inline-styled HTML on platforms without their own highlighter |
| Prism.js | Browser at page load, or via build step | 280+ via official and community plugins | Good — token-tree with sub-grammars | Blogs, static sites, marketing pages; plugin ecosystem covers line numbers, copy buttons, language autoloading |
| highlight.js | Browser at page load, or Node build step | 190+ built-in | Good — state-machine lexer with auto-detection | Wikis, forums, comment sections where users paste arbitrary code without specifying a language |
| Pygments (Python) | Build-time only (Python runtime) | 500+ via lexer modules | Very good — state-machine lexer | Documentation generators (Sphinx, MkDocs, Pelican), GitHub-style source rendering, Jupyter notebook exports |
| Tree-sitter | Native C library, embedded in editors | 50+ first-party grammars, 200+ community | Excellent — full parse tree | Code editors and IDEs where incremental re-parsing matters; GitHub's web code viewer; modern terminals |
For a blog or documentation site, Prism.js or Pygments is almost always the right answer. Prism wins when you want client-side highlighting that responds to user-driven dark/light mode switches without rebuilding the site; Pygments wins when you want highlighting baked into the static HTML at build time, which is faster on page load and works without JavaScript.
For a forum or wiki where users paste arbitrary code, highlight.js is the safer choice because of its language auto-detection. Prism requires the author to specify the language with a class name; highlight.js will pick something reasonable even when no hint is provided.
For a code editor or IDE, Tree-sitter is essentially the only modern answer. The incremental re-parsing model is a hard requirement for sub-keystroke latency on large files, and the parse tree enables features beyond highlighting — code folding, structural navigation, semantic selection, refactoring. The setup cost is higher than regex-based libraries, but the capability difference is enormous.
For one-off highlighting — a code snippet for a single blog post, a sample for an email newsletter, a screenshot for a bug report — this tool fits the niche without requiring any setup, build step, or external dependency. The regex tokenizer is approximate but visually acceptable for short snippets in common languages, and the inline-styled output mode produces self-contained HTML that works anywhere.
Frequently Asked Questions
/* comment blocks..kw, .str, .num, .cmt, .fn, .op, .tag, .attr, and .val class names. The generated HTML carries semantic classes only, so any color palette can be applied externally without modifying the markup./), and distinguishing them requires tracking the previous token's role — a problem that pure regex tokenizers cannot solve cleanly. The expression a/b/c is division, but /a/b/ in a return statement is a regex. This tool conservatively treats / as an operator, which means regex literals will not get string-style coloring. Tree-sitter and highlight.js handle this correctly because they retain parser state across tokens.<span> tags that carry highlight colors. To preserve highlighting, generate the highlighted HTML separately and embed it as a raw HTML block (with the appropriate processor flag — for example, MDX accepts HTML directly, while remark requires the rehype-raw plugin). Alternatively, let your Markdown processor pass the fence body untouched to a highlight library at render time using the language hint after the opening triple backticks.