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
- Documentation rendering: Displaying code snippets, tutorials, or help content
- Rich text content: Rendering formatted text from a CMS or API
- Dynamic templates: Showing pre-formatted HTML content
- Code examples: Displaying syntax-highlighted code blocks
โ When NOT to Use
- User-generated content: Never use for untrusted user input
- Direct template literals: Avoid unescaped HTML in template strings
- Simple text content: Use interpolation
{{ }}for plain text
๐ก๏ธ Security Considerations
Critical Security Rules
- Never trust user input - Always sanitize content from external sources
- Use Angular's built-in sanitization - Angular automatically sanitizes
[innerHTML]content - Validate content sources - Ensure content comes from trusted sources only
Angular's Built-in Sanitization
Angular automatically sanitizes [innerHTML] content by:
- Removing potentially dangerous elements (
<script>,<iframe>, etc.) - Sanitizing attributes that could execute code
- Preserving safe HTML elements and attributes
๐ป 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, '<')
.replace(/>/g, '>')
.replace(/{/g, '{')
.replace(/}/g, '}')
.replace(/@/g, '@')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
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
- Always validate content sources before using with
[innerHTML] - Use Angular's built-in sanitization - don't bypass unless absolutely necessary
- Style content appropriately using
:globalor deep selectors - Test thoroughly to ensure content renders and sanitizes correctly
- Consider performance - innerHTML can impact change detection
- Document content sources for maintainability
- Use TypeScript for type safety with content properties