ESC

Type to search the knowledge base.

HTML Entities and Encoding

Charset, Unicode, and entities — when to use & <  , escaping user content, and mojibake avoidance.

beginner3 min read
  • html
  • entities
  • encoding

Browsers parse bytes into characters using an encoding (almost always UTF-8 today). Character references (entities) let you write reserved or awkward characters in HTML text. Mixing these up causes mojibake (é) or broken markup.

Docs: MDN character references, MDN charset, HTML encoding.

Declare UTF-8

<meta charset="utf-8" />

Serve files as UTF-8 (Content-Type: text/html; charset=utf-8). The meta tag and HTTP header should agree. With UTF-8 you can type most characters literally in content:

<p>Price: €42 — café</p>

Reserved characters in HTML text

Character Entity When needed
& &amp; Always escape in text/attributes when starting a character reference
< &lt; In text so it isn’t a tag
> &gt; Often optional in text; good practice in some contexts
" &quot; Inside double-quoted attributes
' &apos; or &#39; Inside single-quoted attributes
<p>Use <code>&lt;div&gt;</code> for generic flow content.</p>
<p>A &amp; B comparison</p>
<a title="Quote: &quot;Hello&quot;">…</a>

In modern authoring with UTF-8, curly quotes and emoji can be literal; escaping is about syntax safety, not “fancy characters.”

Named vs numeric references

&nbsp;      <!-- named: non-breaking space -->
&#160;      <!-- decimal -->
&#xA0;      <!-- hex -->
&copy;      <!-- © -->

Prefer literal Unicode for readability except for:

  • Reserved syntax chars
  • Invisible/confusable spaces when intentional (&nbsp;)
  • Contexts that strip bare Unicode (rare)

Non-breaking spaces and soft hyphens

10&nbsp;MB
<wbr /> <!-- optional break opportunity -->
&shy; <!-- soft hyphen -->

Overusing &nbsp; breaks responsive wrapping — use sparingly for unit glue.

Escaping user content (XSS)

When injecting into HTML:

// Wrong
el.innerHTML = userName;

// Better: textContent
el.textContent = userName;

// If HTML needed: sanitize with a vetted library + CSP

Attribute injection:

<!-- user input must be escaped for attribute context -->
<img alt="USER_INPUT_ESCAPED" />

Different contexts (HTML text, attribute, JS, URL, CSS) need different escaping. Frameworks (React text children) do a lot of this; dangerouslySetInnerHTML puts you back on the hook.

Mojibake checklist

  1. File saved as UTF-8
  2. Server charset header
  3. <meta charset="utf-8"> early
  4. Don’t double-encode entities (&amp;amp;)
  5. DB connection charset UTF-8

Interview out-loud

“I serve UTF-8 and declare it with meta charset. Entities escape reserved characters like & and < in HTML. Most Unicode can be written literally. User content should use textContent or proper sanitization—not raw innerHTML. Mojibake usually means encoding mismatch across file, server, and meta.”

Footguns

  1. Forgetting to escape & in query strings displayed as text.
  2. Copy-pasting Word’s special spaces and hyphens.
  3. Latin-1 files declared as UTF-8.
  4. Using entities inside <script> incorrectly.
  5. Relying on &nbsp; for layout grids.

Attribute escaping example

function escapeAttr(s) {
  return String(s)
    .replace(/&/g, "&amp;")
    .replace(/"/g, "&quot;")
    .replace(/</g, "&lt;");
}
const html = `<img alt="${escapeAttr(userAlt)}">`;

Use framework auto-escaping when available. In plain string templates, never concatenate untrusted input into HTML. For text nodes, textContent avoids the problem entirely.

Further reading

Related guides