← Back to Blog
2026-02-283 min readby DevUtilz

HTML Entity Encoding and Escaping

HTMLSecurityXSSTutorial

HTML Entity Encoding and Escaping

HTML escaping is your primary defense against XSS attacks. Learn when and how to use it correctly.

Why Escape HTML?

HTML has special characters that change how content is interpreted. Without escaping:

  • <script> becomes executable code
  • onclick becomes an event handler
  • <img onerror=alert(1)> runs JavaScript

Common HTML Entities

| Character | Entity | Description | |-----------|--------|-------------| | < | &lt; | Less than | | > | &gt; | Greater than | | & | &amp; | Ampersand | | " | &quot; | Double quote | | ' | &#39; | Single quote | | | &nbsp; | Non-breaking space |

JavaScript Implementation

Escape Function

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

// Usage
const userInput = '<script>alert("xss")</script>';
const safe = escapeHtml(userInput);
// Result: &lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;

Context-Aware Escaping

function escapeHtmlAttribute(str) {
  return str.replace(/[&<>"']/g, char => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;'
  })[char]);
}

function escapeHtmlUrl(str) {
  return encodeURIComponent(str);
}

Framework Solutions

React (Automatic)

React escapes by default:

const userInput = '<script>alert(1)</script>';
return <div>{userInput}</div>;
// Output: &lt;script&gt;alert(1)&lt;/script&gt;

Vue (v-text directive)

<!-- Automatically escapes -->
<span v-text="userInput"></span>

<!-- Raw HTML - dangerous! -->
<span v-html="userInput"></span>

Angular (Sanitization)

import { DomSanitizer } from '@angular/platform-browser';

constructor(private sanitizer: DomSanitizer) {}

safeHtml = this.sanitizer.bypassSecurityTrustHtml(userInput);

Where to Escape

1. User Input Display

// Always escape user-generated content
comment.textContent = userInput;

2. Dynamic URLs

// Encode URL parameters
const url = `https://example.com/search?q=${encodeURIComponent(query)}`;

3. JSON Data in HTML

// Escape when embedding JSON
const json = JSON.stringify(data).replace(/</g, '\\u003c');

Complete Example

function sanitizeUserInput(input) {
  if (!input || typeof input !== 'string') {
    return '';
  }
  
  return input
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
    .replace(/\n/g, '<br>');
}

Common Mistakes

1. Double Encoding

// Wrong - double encoding
const bad = escapeHtml(escapeHtml('<div>'));
// Output: &amp;lt;div&amp;gt;

// Correct - escape once
const good = escapeHtml('<div>');
// Output: &lt;div&gt;

2. Wrong Context

// Dangerous - unsanitized input in onclick
const bad = `<button onclick="alert('${userInput}')">Click</button>`;

// Safe - use data attributes
const safe = `<button data-message="${escapeHtml(userInput)}">Click</button>`;

3. Forgetting to Escape URLs

// Dangerous
const bad = `<a href="${userUrl}">Link</a>`;

// Safe
const safe = `<a href="${encodeURI(userUrl)}">Link</a>`;

Testing for XSS

const testCases = [
  '<script>alert(1)</script>',
  '<img src=x onerror=alert(1)>',
  '<svg onload=alert(1)>',
  'javascript:alert(1)',
  '<a href="javascript:alert(1)">'
];

testCases.forEach(test => {
  const escaped = escapeHtml(test);
  console.log(`Input: ${test}`);
  console.log(`Escaped: ${escaped}`);
});

Conclusion

Always escape user input before displaying it. Use your framework's built-in sanitization when available. Never trust user-provided HTML without proper sanitization.