Next.js starter your AI actually understands. Ship internal tools in days not weeks. Pre-order $199 $499 → [Get it now]

How to prevent XSS attacks in JavaScript

Cross-Site Scripting (XSS) attacks are one of the most common web security vulnerabilities, allowing attackers to inject malicious scripts into web pages viewed by other users. With over 25 years of experience in software development and as the creator of CoreUI, a widely used open-source UI library, I’ve implemented XSS prevention measures in countless production applications. From my expertise, the most effective approach is to sanitize all user input and use safe DOM manipulation methods that prevent script execution. This method is reliable, widely supported, and should be your first line of defense against XSS attacks.

Use textContent instead of innerHTML and sanitize user input before rendering to prevent XSS attacks.

const userInput = '<script>alert("XSS")</script>'
const container = document.getElementById('content')
container.textContent = userInput

How It Works

The textContent property sets the text content of an element without parsing HTML, which means any HTML tags in the string are treated as plain text rather than executed. In the example above, the malicious script tag is displayed as text instead of being executed. This is in contrast to innerHTML, which would parse and execute the script.

Sanitizing User Input

For cases where you need to allow some HTML but prevent malicious scripts, use a sanitization library:

import DOMPurify from 'dompurify'

const userInput = '<p>Safe content</p><script>alert("XSS")</script>'
const clean = DOMPurify.sanitize(userInput)
document.getElementById('content').innerHTML = clean

Here DOMPurify removes the dangerous <script> tag while preserving the safe <p> tag. The sanitized output will be <p>Safe content</p> with the script completely removed.

Content Security Policy (CSP)

Implement a Content Security Policy header to add an extra layer of protection:

// Server-side (Express example)
app.use((req, res, next) => {
  res.setHeader(
    'Content-Security-Policy',
    "default-src 'self'; script-src 'self'"
  )
  next()
})

This CSP header restricts the browser to only execute scripts from your own domain, blocking any inline scripts or scripts from external sources. Even if an attacker manages to inject a script tag, the browser will refuse to execute it.

Escaping HTML Entities

When displaying user input in HTML attributes or text, escape special characters:

const escapeHtml = (text) => {
  const map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#x27;',
    '/': '&#x2F;'
  }
  return text.replace(/[&<>"'/]/g, (char) => map[char])
}

const userInput = '<img src=x onerror=alert("XSS")>'
const safe = escapeHtml(userInput)
document.getElementById('content').textContent = safe

This function converts dangerous characters into their HTML entity equivalents. The < becomes &lt;, the > becomes &gt;, and so on, preventing any HTML from being interpreted as code.

Validating User Input

Always validate and filter user input on both client and server side:

const isValidEmail = (email) => {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  return regex.test(email)
}

const userEmail = document.getElementById('email').value
if (!isValidEmail(userEmail)) {
  throw new Error('Invalid email format')
}

This validation ensures that the email input matches the expected format, rejecting any input that contains unexpected characters or patterns that could be used for XSS attacks.

Using Modern Framework Features

Modern frameworks like React provide built-in XSS protection:

const UserComment = ({ comment }) => {
  return <div>{comment}</div>
}

React automatically escapes any values embedded in JSX, so the comment variable will be rendered as text even if it contains HTML or script tags. To render raw HTML in React, you would need to explicitly use dangerouslySetInnerHTML, which serves as a warning that you’re bypassing the built-in protection.

Securing Event Handlers

Never use user input directly in event handlers:

// ❌ Dangerous
element.setAttribute('onclick', userInput)

// ✅ Safe
element.addEventListener('click', () => {
  console.log('Safe handler')
})

The setAttribute approach with user input can allow attackers to inject malicious JavaScript code. Using addEventListener keeps the code separate from the data, making it impossible for user input to be interpreted as code.

Best Practice Note

This is the same security-first approach we use in CoreUI components to protect against XSS attacks and ensure that user data is always handled safely. For production applications, consider using a combination of input validation, output encoding, CSP headers, and sanitization libraries. Security is layered - no single technique is enough on its own. Always validate on the server side as well, since client-side validation can be bypassed. For input validation, check our guide on how to validate email in JavaScript for related validation techniques.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.

Answers by CoreUI Core Team