Why is assigning user input to innerHTML dangerous? Run the demo — the safe box shows text, the unsafe box executes an injected handler.
Whether you default to textContent and treat innerHTML as a code-execution surface.
Cross-Site Scripting (XSS) is when attacker-controlled input is interpreted as CODE in another user's browser, running with that page's privileges — letting it read the DOM, steal non-HttpOnly cookies/tokens, and act as the user. The core mistake is putting untrusted data into an HTML-parsing sink. innerHTML PARSES its string as HTML, so markup like an <img> with an onerror handler (or <svg>, event attributes, etc.) executes — note that a raw <script> inserted via innerHTML does NOT run, but onerror/onload attributes do, which is the common vector. textContent (and .innerText) treat the value as PLAIN TEXT — tags are shown literally, never parsed, so injection is inert. Rule: default to textContent / createTextNode; only use innerHTML with trusted or sanitized content. Defenses in depth: (1) prefer textContent and DOM APIs; (2) if you must render HTML, sanitize with a vetted library (DOMPurify) or the Sanitizer API; (3) frameworks like React escape by default — the danger there is dangerouslySetInnerHTML; (4) a Content-Security-Policy header limits what can execute even if injection slips through; (5) keep session tokens in HttpOnly cookies so XSS can't read them; (6) escape/encode by context (HTML, attribute, URL, JS). Never trust input length or a blocklist — allowlist and encode.
Rendering any user-supplied string into the DOM: reach for textContent; gate innerHTML behind sanitization.