Use innerHTML to Render HTML Content in Angular

Comprehensive guide for safely rendering HTML content using Angular's innerHTML directive with proper escaping and security considerations

๐Ÿ”’ Using innerHTML to Render HTML Content in Angular

The [innerHTML] directive in Angular provides a way to render HTML content dynamically, but it requires careful handling to ensure security and proper display.

๐ŸŽฏ When to Use innerHTML

โœ… Appropriate Use Cases

โŒ When NOT to Use

๐Ÿ›ก๏ธ Security Considerations

Critical Security Rules

  1. Never trust user input - Always sanitize content from external sources
  2. Use Angular's built-in sanitization - Angular automatically sanitizes [innerHTML] content
  3. Validate content sources - Ensure content comes from trusted sources only

Angular's Built-in Sanitization

Angular automatically sanitizes [innerHTML] content by:

๐Ÿ’ป Implementation Examples

Basic Usage

<!-- Component template -->
<div class="content-container">
  <h2>Documentation Content</h2>
  <div [innerHTML]="documentationContent"></div>
</div>
// Component class
export class DocumentationComponent {
  documentationContent = `
    <h3>Getting Started</h3>
    <p>This is <strong>formatted</strong> content with <em>emphasis</em>.</p>
    <ul>
      <li>Feature 1</li>
      <li>Feature 2</li>
    </ul>
  `;
}

With Dynamic Content

export class DynamicContentComponent {
  content = signal<string>('');

  loadContent() {
    // Simulate API call
    this.content.set(`
      <article>
        <h2>Dynamic Article</h2>
        <p>Content loaded from API...</p>
      </article>
    `);
  }
}
<div [innerHTML]="content()" class="dynamic-content"></div>

๐Ÿ”ง Content Escaping for Code Display

When displaying code snippets, you need to escape HTML entities:

Manual Escaping Utility

export class CodeDisplayComponent {
  escapeHtml(html: string): string {
    return html
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/{/g, '&#123;')
      .replace(/}/g, '&#125;')
      .replace(/@/g, '&#64;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');
  }

  displayCode(code: string) {
    const escapedCode = this.escapeHtml(code);
    return `<pre><code>${escapedCode}</code></pre>`;
  }
}

Using Angular's DomSanitizer (Advanced)

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

export class AdvancedContentComponent {
  constructor(private sanitizer: DomSanitizer) {}

  getSafeHtml(content: string): SafeHtml {
    // Only use this if you need to bypass sanitization
    // and you're 100% sure the content is safe
    return this.sanitizer.bypassSecurityTrustHtml(content);
  }
}
<div [innerHTML]="getSafeHtml(trustedContent)"></div>

๐ŸŽจ Styling Considerations

CSS Scoping

.content-container {
  // Style the container
  padding: 1rem;
  border-radius: 8px;
  background: var(--ux-surface);

  // Style content inside innerHTML
  :global {
    h1, h2, h3 {
      color: var(--ux-primary);
      margin-bottom: 0.5rem;
    }

    p {
      line-height: 1.6;
      margin-bottom: 1rem;
    }

    code {
      background: var(--ux-surface-variant);
      padding: 0.25rem 0.5rem;
      border-radius: 4px;
      font-family: 'Fira Code', monospace;
    }

    pre {
      background: var(--ux-surface-variant);
      padding: 1rem;
      border-radius: 8px;
      overflow-x: auto;
    }
  }
}

Theme Integration

<div class="ux-card ux-shadow-md">
  <div [innerHTML]="content" class="ux-p-4"></div>
</div>

๐Ÿšจ Common Pitfalls and Solutions

โŒ Problem: Content Not Rendering

<!-- Wrong: Using interpolation for HTML -->
<div>{{ htmlContent }}</div>
<!-- Correct: Using innerHTML directive -->
<div [innerHTML]="htmlContent"></div>

โŒ Problem: Styling Not Applied

// Wrong: Styles not applied to innerHTML content
.my-component h1 {
  color: red; // Won't work
}
// Correct: Use :global or deep selectors
.my-component {
  :global h1 {
    color: red; // Will work
  }
}

โŒ Problem: Security Vulnerabilities

// Wrong: Using untrusted content directly
const userInput = this.getUserInput(); // Could contain malicious HTML
this.content = userInput;
// Correct: Sanitize or validate content
const userInput = this.getUserInput();
if (this.isTrustedSource(userInput)) {
  this.content = userInput;
} else {
  this.content = this.sanitizeContent(userInput);
}

๐Ÿงช Testing innerHTML Content

Unit Testing

describe('ContentComponent', () => {
  it('should render HTML content', () => {
    const fixture = TestBed.createComponent(ContentComponent);
    const component = fixture.componentInstance;

    component.content = '<h1>Test Title</h1><p>Test content</p>';
    fixture.detectChanges();

    const compiled = fixture.nativeElement;
    expect(compiled.querySelector('h1').textContent).toBe('Test Title');
    expect(compiled.querySelector('p').textContent).toBe('Test content');
  });

  it('should sanitize dangerous content', () => {
    const fixture = TestBed.createComponent(ContentComponent);
    const component = fixture.componentInstance;

    component.content = '<script>alert("xss")</script><p>Safe content</p>';
    fixture.detectChanges();

    const compiled = fixture.nativeElement;
    expect(compiled.querySelector('script')).toBeNull();
    expect(compiled.querySelector('p').textContent).toBe('Safe content');
  });
});

๐Ÿ“š Best Practices Summary

  1. Always validate content sources before using with [innerHTML]
  2. Use Angular's built-in sanitization - don't bypass unless absolutely necessary
  3. Style content appropriately using :global or deep selectors
  4. Test thoroughly to ensure content renders and sanitizes correctly
  5. Consider performance - innerHTML can impact change detection
  6. Document content sources for maintainability
  7. Use TypeScript for type safety with content properties

๐Ÿ”— Related Resources