Encoding / Decoding

HTML Entity Encode

Encode or decode HTML entities for safe HTML output.

Original Text
HTML Entities

Common HTML Entities

&&
&lt;<
&gt;>
&quot;"
&#39;'
&nbsp;(space)
&copy;©
&reg;®
&trade;™
&euro;€
&pound;£
&yen;¥
&deg;°
&plusmn;±
&times;×
&divide;÷
&frac12;½
&frac14;¼
&frac34;¾

What is HTML Entity Encoding?

HTML entity encoding converts special characters to HTML entity references, ensuring they display correctly in HTML documents without being interpreted as HTML tags.

Features

  • Two-way conversion: Encode and decode modes
  • Basic encoding: Encodes special characters (<, >, &, ")
  • Full encoding: Converts all characters to numeric entities ({)
  • Entity reference: Common HTML entity lookup table

Common Entities

  • &lt; - Less than (<)
  • &gt; - Greater than (>)
  • &amp; - Ampersand (&)
  • &nbsp; - Non-breaking space
  • &copy; - Copyright (©)

Use Cases

  • XSS Prevention: Encode user input to prevent XSS attacks
  • HTML Escaping: Display HTML code as text
  • Special Symbols: Display copyright, trademark symbols in HTML

This tool escapes the characters HTML would otherwise parse as markup, so tags inside a string render as text instead of being parsed. Basic mode replaces six characters, full mode rewrites each UTF-16 code unit as a decimal reference, and decode reverses both plus hex references. It fits verifying escaped values in CMS fields, email templates, and page source.

The basic pass replaces six characters, space included

Basic mode scans with /[&<>"' ]/g: five markup characters plus U+0020, and everything else passes through untouched. The HTML standard's own escape-a-string algorithm handles only the ampersand, the two angle brackets and the two quote characters, so the space is this tool's addition, turning each one into &nbsp;, U+00A0 non-breaking space. Text produced this way will not wrap at word boundaries in a browser and no longer collapses repeated spaces. The ampersand goes first, so an opening div tag carrying a class attribute becomes <div&nbsp;class="a">. Apostrophes are always written ' rather than &apos;, which HTML4 parsers never recognised. No symbol name from the reference grid (©, ®, ™, €, £, ¥, °, ±, ×, ÷, ½, ¼, ¾) can be emitted by either mode, because every one of those code points sits above U+007E.

Full mode walks UTF-16 code units, not code points

Full mode splits with split('') and reads charCodeAt(0), so iteration is per UTF-16 code unit and the emitted number always lands in 0 to 0xFFFF. A printable ASCII character keeps its named form only if the table contains it, which in practice means the space alone; every other table entry is above U+007E and falls through to the decimal branch. Characters above U+FFFF are stored as surrogate pairs, so U+1F600 comes out as two references, &#55357;&#56832;. HTML5 treats a numeric reference as a Unicode scalar value and rejects surrogates, so a lone &#55357; is a parse error that conformant parsers replace with U+FFFD. This tool round-trips it only because decode concatenates the pair again; the markup it handed you is not valid HTML.

Decode order decides whether text is unescaped twice

Decoding runs three fixed passes: the 19 named entries in table order (ampersand first, then the angle brackets, the double quote, ', &nbsp;, and the symbol names), then decimal references, then hex. Because the ampersand is unwound before anything else, &lt; collapses all the way to a literal less-than sign instead of the < the source meant to display, so decoding already-escaped text is destructive. The same string written as &#38;lt; survives, since the numeric pass runs last and its output is never rescanned. Both numeric patterns demand a trailing semicolon, leaving &#169 and &#x26 untouched, and name matching is case-sensitive: &QUOT; stays as typed. Values above U+FFFF are truncated by String.fromCharCode, so &#128512; yields U+F600.

Worked example

Full-encoding a mixed string that contains an emoji

Input
Hi 你好 😀
Output
&#72;&#105;&nbsp;&#20320;&#22909;&nbsp;&#55357;&#56832;

Eight code units expand to 55 characters. H and i are printable ASCII but absent from the entity table, so they take the decimal branch, while both spaces hit the named branch first. 你 is U+4F60, that is 20320, and 好 is U+597D, that is 22909. U+1F600 lies outside the BMP, so the split produced the surrogate code units 55357 and 56832 as two separate references.

Limits and pitfalls

  • Basic mode rewrites every space as &nbsp;, so the result will not break at spaces in a browser; remove those references when the text must wrap normally.
  • Decimal references above U+FFFF are truncated to a single 16-bit unit: decoding &#128512; returns U+F600 rather than the emoji.
  • The ampersand is unwound first, so &lt; decodes to an angle bracket; running already-escaped markup through decode a second time corrupts it.
  • Only the 19 names in the built-in table are decoded. &apos;, &mdash;, &hellip; and other HTML5 names, plus any reference missing its semicolon, pass through unchanged.
  • There is no length limit, and the whole textarea is recomputed on every keystroke, with decode running 19 sequential regular-expression replacements across it, so very large pastes stall the field.

Related tools

When it goes wrong

The decoded text looks the same as the page, yet the diff insists the strings differ.

CauseThe decode table maps &nbsp; back to an ordinary U+0020 space, while a browser parses &nbsp; as U+00A0. In UTF-8 those are the single byte 20 and the pair C2 A0, so a copy taken from this panel and a copy taken from the rendered page compare unequal while looking identical.

FixCompare bytes in the Hex tool rather than by eye. When you really need the non-breaking space, paste &#160; instead &mdash; that route does emit U+00A0 &mdash; then confirm the two bytes C2 A0.

Full encoding flattened a whole paragraph: no line breaks left in the page.

CauseFull mode sends U+000A through the decimal branch, so a line break becomes &#10;. A reference to a newline still yields whitespace, and outside <pre> the HTML rendering rules collapse it to a single space. The output panel also prints everything on one line, which hides the damage until it ships.

FixHandle line structure outside the tool: encode line by line and join with <br>, or place the value in an element with white-space: pre-wrap. Full mode makes a value ASCII-safe; it does not preserve layout.

A 120-character English sentence came back as 600 characters.

CauseIn full mode every code unit outside the 19-name table is written as &# plus its decimal value plus ;: a lowercase letter such as a (97) occupies five characters, a CJK character such as 你 (20320) occupies eight, and a space takes the named branch at six. A five-fold expansion over plain ASCII text is what this mode does by definition.

FixUse basic mode unless the receiving channel accepts nothing but ASCII. It rewrites six characters and leaves letters, digits, CJK text and emoji as typed.

&#x41; decodes, &#X41; does nothing, and they are the same value.

CauseThe hex pass is /&#x([0-9a-fA-F]+);/g: the prefix x must be lowercase, while the digits may be either case. Generators that emit an uppercase X produce references this page cannot see, and no error is raised because nothing is validated.

FixLowercase the X, or convert to decimal, since &#x41; and &#65; both give A. If the output comes from your own generator, fix the generator rather than patching the paste.

I pasted obviously broken entities and the panel reported no error at all.

CauseThe error line is only written inside a catch block, and all three decode passes are regular-expression replacements plus String.fromCharCode, none of which validate syntax or throw. Every input therefore yields "a result", including input a browser would reject outright.

FixAudit the output yourself: leftover &amp;, or a fragment like &# with no closing semicolon, means a reference was skipped. A U+0000 in the result (from &#0;) is the other tell.

Which layer should own the escaping

SituationThis HTML entity toolReach for this instead
The value goes inside a <script> block, a JSON string or an inline handlerBoth modes only emit HTML character references. A script element is raw text, so references inside it are never decoded: escaping a tag for that context hands your JavaScript the entity spelling instead of the bracket, and nothing complains. An attribute value such as onclick="..." is decoded, so the identical output is safe there.JSON String Escape for the quote and the line break: it writes \" and \n but leaves angle brackets alone. When a </script> must not read as a tag, Unicode Escape emits \u003c for every character instead.
You need exactly what the browser itself would serializeBasic mode also rewrites every space as &nbsp;, which the browser turns into U+00A0, so the result stops breaking at word boundaries. The escaping the HTML standard defines touches only &, the two angle brackets and the two quote characters.div.textContent = s; div.innerHTML in the console, or Python html.escape(s, quote=False). Both follow the standard, and neither rewrites ordinary spaces.
The source you are decoding came off a real web pageThe decode table holds 19 names, so &mdash;, &hellip;, &rsquo; and &apos; come back exactly as pasted, which reads as "the tool did nothing".Python html.unescape or the npm package he, which know every HTML5 name reference. Use them on borrowed page source; keep this page for the characters you write yourself.
You want to see what the escaped markup renders asThe output panel is text only and never parses anything, so a misspelled entity and a correct one look equally convincing until it lands in a browser.Prefix the result with data:text/html, and open it in a tab, or save it as a .html file. This page generates the string; only a renderer can judge it.
The text is a query parameter value, not page contentBasic mode escapes the ampersand into its entity, so a value holding & leaves the panel as a&amp;b and the server stores the entity spelling instead of a single ampersand. Meanwhile =, # and ? pass through untouched and split the parameter at the wrong place.URL Encode / Decode, which percent-encodes the UTF-8 bytes as %26 and %3D. Entities belong in HTML documents; percent-encoding belongs in URLs.
The same job has to run over hundreds of files in a buildOne textarea, one paste per round trip, no scriptable entry point and no record of what was converted.A build step instead: he.encode(s, { useNamedReferences: true }) or python3 -c "import html,sys;print(html.escape(sys.stdin.read()))". Keep this page for eyeballing one value.

Frequently Asked Questions

When are HTML entities actually required?
Three cases: displaying characters that would parse as markup (<, >, &), using quotes inside attribute values, and typing characters not on your keyboard (like © for ©). HTML5 also allows raw UTF-8 characters wherever they cause no ambiguity.
What is the difference between & and &?
The rendered result is identical. Named entities are readable but limited to the names HTML defines; numeric entities (decimal & or hexadecimal &) can express any Unicode code point. A practical use of numeric entities is keeping email addresses from being collected literally by scrapers.
Why does a correctly decoded entity still show as a box or mojibake?
The decoding is fine — the problem is the font or charset: the page is not served as UTF-8, or the font lacks the glyph. Confirm the code point with this tool first, then check the page charset and font coverage.

Content reviewed: