Preventing XSS
Security is not one feature you add at the end.
It is a way of thinking about trust.
Every web app receives data from places it does not fully control:
- form fields
- URL query parameters
- API responses
- browser storage
- third-party packages
- copied text
- uploaded files
Secure JavaScript code treats that data carefully before using it in the DOM, in requests, or in application state.
What Is XSS?
XSS stands for cross-site scripting.
It happens when untrusted content is interpreted as code by the browser.
The most common beginner mistake is inserting user-controlled text as HTML.
const comment = new URLSearchParams(location.search).get("comment");
document.querySelector("#preview").innerHTML = comment;This is dangerous because innerHTML asks the browser to parse the string as HTML.
If the string came from a user, an API, or a URL, your code may accidentally give that string the power to change the page.
Use Text APIs for Text
If you want to display text, use textContent.
const comment = new URLSearchParams(location.search).get("comment") ?? "";
document.querySelector("#preview").textContent = comment;textContent treats the value as text.
Characters like < and > are displayed as characters, not parsed as tags.
Safe DOM Creation
Create elements with DOM APIs when you can.
function renderComment(comment) {
const article = document.createElement("article");
const author = document.createElement("h3");
author.textContent = comment.author;
const body = document.createElement("p");
body.textContent = comment.body;
article.append(author, body);
return article;
}This pattern separates structure from data.
Your code controls the elements.
User data becomes text inside those elements.
Avoid HTML Templates with Untrusted Data
Template literals are useful, but they do not make HTML safe.
function renderUser(user) {
return `
<li>
<strong>${user.name}</strong>
<span>${user.bio}</span>
</li>
`;
}If user.name or user.bio came from an untrusted source, this can become unsafe when inserted with innerHTML.
Use DOM APIs or a framework that escapes text by default.
When Is innerHTML Acceptable?
Sometimes an app intentionally renders HTML.
Examples:
- a trusted documentation page
- content generated by your own markdown renderer
- a server-rendered template you control
Even then, be careful.
If any part of the HTML includes user input, sanitize it with a trusted sanitizer before rendering.
Do not write your own sanitizer as a beginner project.
HTML parsing has many edge cases.
Escaping vs Sanitizing
Escaping changes special characters so they are treated as text.
For example, < may become <.
Sanitizing removes or limits unsafe HTML while allowing some safe markup.
For example, a sanitizer might allow <strong> but remove event handler attributes.
For plain text display, prefer text APIs instead of manually escaping.
For user-generated rich text, use a well-maintained sanitizer on the server or client.
Event Handler Attributes
Do not build event handlers inside strings.
// Avoid this pattern
buttonContainer.innerHTML = `<button onclick="${action}">Run</button>`;Use addEventListener.
const button = document.createElement("button");
button.textContent = "Run";
button.addEventListener("click", () => {
runSelectedAction();
});
buttonContainer.append(button);This keeps code as code and data as data.
Defensive Rendering Checklist
Before putting data on the page, ask:
- Where did this value come from?
- Am I displaying text or rendering HTML?
- Am I using
textContentwhen I only need text? - Is this value being used in a URL, CSS, or attribute?
- Does a trusted library already handle escaping for this context?
Different output locations need different safety rules.
Text content, HTML attributes, URLs, CSS, and JavaScript strings are different contexts.
Common Mistakes
Do not assume API data is safe just because it came from your backend.
Do not assume hidden form fields are safe.
Do not put raw user input into innerHTML.
Do not store unsafe HTML in localStorage and render it later.
Do not disable framework escaping unless you fully understand the risk.
Summary
XSS happens when untrusted data becomes executable browser content.
Use textContent and DOM creation APIs for ordinary text.
Avoid innerHTML for untrusted data.
When you must render rich HTML, use a trusted sanitizer and keep the allowed markup narrow.