HTML Entities
HTML character references (entities) let you include characters that would otherwise be parsed as markup, or that are awkward to type. In UTF-8 documents you can usually paste real characters directly — entities remain essential for &, <, attribute-safe quotes, invisible characters, and documentation that shows markup as text.
This guide covers named, decimal, and hex references; escaping rules; XSS-related escaping; the difference between and CSS spacing; emoji; RTL marks; practical tables; and the common confusion between HTML entities and encodeURIComponent.
Three equivalent forms exist for many characters. Named references are readable; numeric forms always work for any Unicode code point.
| Form | Example | Notes |
|---|---|---|
| Named | < © | Finite set defined by HTML |
| Decimal numeric | <   | &# followed by decimal + ; |
| Hex numeric | <   | &#x followed by hex + ; |
| 1 | <!-- All represent < --> |
| 2 | < < < < |
| 3 | |
| 4 | <!-- Non-breaking space --> |
| 5 |     |
| 6 | |
| 7 | <!-- Prefer real UTF-8 when possible --> |
| 8 | <p>Copyright © 2026</p> |
| 9 | <p>Copyright © 2026</p> |
best practice
note
Context determines what must be escaped. Text content, attributes, and script/style elements have different rules.
| Context | Must escape / avoid | Details |
|---|---|---|
| Text content | & and < (and often >) | Prevents tag open / entity open |
| Attribute values | & and the quoting char | Use " or ' as needed |
| URL in href | URL encoding, not HTML entities | Percent-encode first, then attribute-escape |
| Inside <script> | Do not HTML-entity escape JS | Use proper JS escaping / JSON |
| textarea content | < and & | Shown as text to users |
| 1 | <!-- Text --> |
| 2 | <p>Use <button> for actions & links for navigation.</p> |
| 3 | |
| 4 | <!-- Attribute with double quotes --> |
| 5 | <div data-label="Tom & Jerry" title="5 < 6"></div> |
| 6 | |
| 7 | <!-- Safer: avoid embedding raw HTML in attributes entirely --> |
warning
Cross-site scripting often starts as unescaped user input inserted into HTML. Entity escaping is one layer — not a complete security program. Prefer textContent, contextual encoders, and sanitizers over hand-rolled replace chains.
| 1 | // Safe for text content |
| 2 | el.textContent = userInput; // no HTML parsing |
| 3 | |
| 4 | // Dangerous |
| 5 | el.innerHTML = userInput; |
| 6 | |
| 7 | // Minimal escape for text nodes (illustrative — use vetted libraries) |
| 8 | function escapeHtml(s) { |
| 9 | return s |
| 10 | .replace(/&/g, '&') |
| 11 | .replace(/</g, '<') |
| 12 | .replace(/>/g, '>') |
| 13 | .replace(/"/g, '"') |
| 14 | .replace(/'/g, '''); |
| 15 | } |
| 16 | |
| 17 | // Attribute context needs the same family of escapes |
| 18 | img.setAttribute('alt', userInput); // prefer setAttribute/text APIs |
| 1 | <!-- Attacker input: <img src=x onerror=alert(1)> --> |
| 2 | <!-- If injected via innerHTML, executes --> |
| 3 | <!-- If inserted as textContent, shows as text --> |
danger
best practice
(U+00A0) is a non-breaking space character. It prevents line breaks at that position and preserves a space that will not collapse. It is not a layout system. Prefer margin, gap, padding, or white-space for UI spacing.
| Approach | Use when |
|---|---|
| | Keep “10 MB” or “Mr. Smith” on one line |
| CSS margin/gap | Space between components |
| padding | Inner spacing inside a box |
| width/flex | Alignment and columns |
| white-space: pre | Preserve multiple spaces intentionally |
| 1 | <!-- OK: unit stays with number --> |
| 2 | <p>File size: 10 MB</p> |
| 3 | |
| 4 | <!-- Bad layout hack --> |
| 5 | <p>Title Price</p> |
| 6 | |
| 7 | <!-- Good --> |
| 8 | <div class="row" style="display:flex;gap:1rem;justify-content:space-between"> |
| 9 | <span>Title</span><span>Price</span> |
| 10 | </div> |
info
Emoji are Unicode characters. You can paste them directly in UTF-8 HTML or use numeric references. Named entities do not exist for most emoji. Be aware of ZWJ sequences, skin tones, and variation selectors.
| 1 | <p>Ship it 🚀</p> |
| 2 | <p>Ship it 🚀</p> |
| 3 | <p>Family: 👪 or ZWJ sequences</p> |
| 4 | <p>⚠︎ vs ⚠️ (text vs emoji presentation)</p> |
note
Bidirectional text uses invisible format characters. Prefer markup (dir, bdi, bdo) over sprinkling entities, but knowing the marks helps when debugging.
| Name | Reference | Role |
|---|---|---|
| LRM | ‎ / U+200E | Left-to-right mark |
| RLM | ‏ / U+200F | Right-to-left mark |
| LRE/RLE/PDF | legacy embeddings | Prefer dir/bdo |
| ALM | U+061C | Arabic letter mark |
| 1 | <p dir="rtl">مرحبا World</p> |
| 2 | <p>User <bdi>مرحبا</bdi> submitted a ticket.</p> |
| 3 | <p>File named <bdo dir="ltr">01-report.pdf</bdo></p> |
best practice
Everyday references you will actually type.
| Char | Named | Decimal | Hex |
|---|---|---|---|
| & | & | & | & |
| < | < | < | < |
| > | > | > | > |
| " | " | " | " |
| ' | ' | ' | ' |
| nbsp | |   |   |
| — | — | — | — |
| – | – | – | – |
| … | … | … | … |
| © | © | © | © |
| ® | ® | ® | ® |
| € | € | € | € |
| × | × | × | × |
| ÷ | ÷ | ÷ | ÷ |
| Char | Named | Use |
|---|---|---|
| ‹ › | ‹ › | Single angle quotes |
| « » | « » | Guillemets |
| · | · | Separators |
| • | • | Bullets in prose |
| ° | ° | Degrees |
| ± | ± | Plus-minus |
| ² ³ | ² ³ | Prefer <sup> for a11y math |
| ½ | ½ | Or write 1/2 |
info
encodeURIComponent percent-encodes bytes for URL components. HTML entities encode characters for HTML parsing. They solve different problems and are not interchangeable.
| 1 | const name = 'Tom & Jerry'; |
| 2 | |
| 3 | // Wrong for URLs |
| 4 | const bad = '/search?q=' + name.replace('&', '&'); |
| 5 | |
| 6 | // Right for query params |
| 7 | const good = '/search?q=' + encodeURIComponent(name); |
| 8 | // → /search?q=Tom%20%26%20Jerry |
| 9 | |
| 10 | // Right for HTML text |
| 11 | const html = `<p>${escapeHtml(name)}</p>`; |
| 12 | // → <p>Tom & Jerry</p> |
| 13 | |
| 14 | // Composing both: build URL, then put URL into an attribute |
| 15 | const href = '/search?q=' + encodeURIComponent(name); |
| 16 | el.innerHTML = `<a href="${escapeHtml(href)}">Search</a>`; |
| API | Output alphabet | Context |
|---|---|---|
| encodeURIComponent | %xx | Query, path segments |
| encodeURI | %xx (keeps :/?#) | Full URLs carefully |
| HTML entities | &…; | HTML text/attrs |
| JSON.stringify | JS string escapes | Script data / JSON |
| CSS escapes | \\ | Inside style contexts |
danger
| 1 | [ ] UTF-8 document + charset meta |
| 2 | [ ] Escape & and < in text from untrusted/data sources |
| 3 | [ ] Escape quotes in attributes (or use setAttribute) |
| 4 | [ ] Never treat entities as URL encoding |
| 5 | [ ] Prefer CSS for layout spacing over spam |
| 6 | [ ] Prefer dir/bdi over invisible bidi entities |
| 7 | [ ] Use textContent / sanitizer instead of DIY filters for XSS |
| 8 | [ ] Show markup examples with <…> so they do not become real tags |
An ampersand followed by characters that look like a character reference can confuse authors and validators. Historically, ambiguous ampersands were discouraged. In practice, write & whenever you mean a literal ampersand — especially in URLs inside HTML source.
| 1 | <!-- Fragile --> |
| 2 | <a href="/search?a=1&b=2">Search</a> |
| 3 | |
| 4 | <!-- Clear and valid style --> |
| 5 | <a href="/search?a=1&b=2">Search</a> |
| 6 | |
| 7 | <!-- Built in JS: encode then attribute-escape if needed --> |
| 8 | <a id="q">Search</a> |
| 9 | <script> |
| 10 | const url = '/search?' + new URLSearchParams({ a: '1', b: '2' }); |
| 11 | document.getElementById('q').href = url; // DOM API — no HTML entity needed |
| 12 | </script> |
info
Browsers decode character references when parsing HTML into the DOM. The DOM stores characters, not the entity syntax. Serializing with innerHTML may re-escape as needed. Do not try to “preserve” entity spelling through the DOM — it is not guaranteed.
| 1 | const div = document.createElement('div'); |
| 2 | div.innerHTML = '&lt;hi&gt;'; |
| 3 | console.log(div.textContent); // "<hi>" |
| 4 | console.log(div.innerHTML); // "<hi>" (typical re-escape) |
Community
Get help on Slack, Discord or VIP
Stuck on a guide? Join the community and ask.