react-safe-html-parser
Version:
A secure, lightweight HTML parser for React with XSS protection and SSR support
483 lines (372 loc) • 11.1 kB
Markdown
# React Safe HTML Parser
A secure, lightweight HTML parser for React with XSS protection and SSR support. Perfect for Vercel deployments and Next.js applications.
## ✨ Features
- ✅ **Secure by default** - XSS protection with configurable sanitization
- ✅ **React 18+ compatible** - Supports concurrent rendering and modern features
- ✅ **SSR/SSG ready** - Works seamlessly with Next.js and Vercel
- ✅ **TypeScript support** - Full type definitions included
- ✅ **Custom components** - Replace HTML tags with your own React components
- ✅ **Performance optimized** - Memoized parsing and tree-shakeable
- ✅ **Isomorphic** - Works in both browser and Node.js environments
## 🚀 Installation
```bash
npm install react-safe-html-parser
# or
yarn add react-safe-html-parser
# or
pnpm add react-safe-html-parser
```
## 📦 Peer Dependencies
This library requires React 18+ as a peer dependency:
```json
{
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
}
}
```
## 🎯 Basic Usage
### Simple HTML Parsing
```tsx
import { HtmlParser } from 'react-safe-html-parser';
function App() {
const html = '<h1>Hello World</h1><p>This is <strong>bold</strong> text.</p>';
return (
<div>
<HtmlParser html={html} />
</div>
);
}
```
### With Custom Components
```tsx
import { HtmlParser } from 'react-safe-html-parser';
const CustomHeading = ({ children, ...props }) => (
<h1 className="custom-heading" {...props}>
{children}
</h1>
);
function App() {
const html = '<h1>Custom Heading</h1><p>Regular paragraph</p>';
return (
<HtmlParser
html={html}
options={{
components: {
h1: CustomHeading
}
}}
/>
);
}
```
### With Transform Functions
```tsx
import { HtmlParser } from 'react-safe-html-parser';
function App() {
const html = '<p>Transform this</p><div>Keep this</div>';
return (
<HtmlParser
html={html}
options={{
transform: (node, index) => {
if (node.tagName === 'p') {
return React.createElement('h2', {}, 'Transformed!');
}
return null; // Return null to skip the node
}
}}
/>
);
}
```
## 🔧 Advanced Configuration
### Custom Sanitization
```tsx
import { HtmlParser } from 'react-safe-html-parser';
function App() {
const html = '<div onclick="alert(1)" style="color: red;">Content</div>';
return (
<HtmlParser
html={html}
options={{
sanitize: {
allowedTags: ['div', 'span', 'p'],
allowedAttributes: ['class', 'id', 'style'],
allowedStyles: ['color', 'background-color'],
removeScripts: true,
removeComments: true
}
}}
/>
);
}
```
### Disable HTML Entity Decoding
```tsx
import { HtmlParser } from 'react-safe-html-parser';
function App() {
const html = '<p>Hello & World <3</p>';
return (
<HtmlParser
html={html}
options={{
decodeHtmlEntities: false
}}
/>
);
}
```
### Custom Style Parsing
```tsx
import { HtmlParser } from 'react-safe-html-parser';
function App() {
const html = '<div style="color: red; font-size: 16px;">Styled content</div>';
return (
<HtmlParser
html={html}
options={{
parseStyle: true, // Converts style string to object
sanitize: {
allowedStyles: ['color', 'font-size']
}
}}
/>
);
}
```
## 🛡️ Security Features
### XSS Protection
The library automatically protects against common XSS attacks:
- **Event handlers removed**: All `on*` attributes are stripped
- **Dangerous protocols blocked**: `javascript:`, `data:`, `vbscript:` URLs are removed
- **Script tags removed**: All `<script>` tags and content are stripped
- **Form elements removed**: `<form>`, `<input>`, `<button>` etc. are removed
- **Configurable allowlists**: Control which tags and attributes are allowed
### Security Configuration
```tsx
import { HtmlParser, DEFAULT_SECURITY_CONFIG } from 'react-safe-html-parser';
// Use default security settings
const html = '<div onclick="alert(1)">Content</div>';
<HtmlParser html={html} /> // onclick will be removed
// Custom security settings
<HtmlParser
html={html}
options={{
sanitize: {
allowedTags: ['div', 'span'],
allowedAttributes: ['class'],
removeScripts: true
}
}}
/>
```
## 🔄 SSR/SSG Support
### Next.js Integration
```tsx
// pages/index.tsx
import { HtmlParser } from 'react-safe-html-parser';
export default function HomePage() {
const html = '<h1>Server-side rendered content</h1>';
return (
<div>
<HtmlParser html={html} />
</div>
);
}
```
### Vercel Deployment
The library works seamlessly with Vercel deployments:
```tsx
// components/ContentRenderer.tsx
import { HtmlParser } from 'react-safe-html-parser';
interface ContentRendererProps {
content: string;
}
export function ContentRenderer({ content }: ContentRendererProps) {
return (
<HtmlParser
html={content}
fallback={<div>Loading content...</div>}
showErrors={process.env.NODE_ENV === 'development'}
/>
);
}
```
## 🎨 Styling
### Wrapper Styling
```tsx
import { HtmlParser } from 'react-safe-html-parser';
function App() {
const html = '<p>Content</p>';
return (
<HtmlParser
html={html}
className="content-wrapper"
style={{
padding: '20px',
backgroundColor: '#f5f5f5'
}}
/>
);
}
```
### CSS-in-JS Integration
```tsx
import styled from 'styled-components';
import { HtmlParser } from 'react-safe-html-parser';
const StyledWrapper = styled.div`
padding: 20px;
border: 1px solid #ccc;
h1 {
color: #333;
font-size: 24px;
}
p {
line-height: 1.6;
}
`;
function App() {
const html = '<h1>Title</h1><p>Content</p>';
return (
<StyledWrapper>
<HtmlParser html={html} />
</StyledWrapper>
);
}
```
## 🔧 API Reference
### HtmlParser Props
```tsx
interface HtmlParserProps {
html: string; // HTML string to parse
options?: ParserOptions; // Parser configuration
className?: string; // CSS class for wrapper
style?: React.CSSProperties; // Inline styles for wrapper
fallback?: ReactNode; // Content to show if parsing fails
showErrors?: boolean; // Whether to log errors to console
}
```
### ParserOptions
```tsx
interface ParserOptions {
components?: ComponentMap; // Custom component replacements
transform?: TransformFunction; // Node transformation function
sanitize?: SanitizeOptions; // Sanitization settings
decodeHtmlEntities?: boolean; // Whether to decode HTML entities
parseStyle?: boolean; // Whether to parse style attributes
entityDecoder?: (entity: string) => string; // Custom entity decoder
}
```
### Utility Functions
```tsx
import {
parseHtmlToReact,
parseHtmlToElement,
sanitizeAttributes,
sanitizeStyles,
decodeHtmlEntities
} from 'react-safe-html-parser';
// Parse to multiple elements
const result = parseHtmlToReact('<h1>Title</h1><p>Content</p>');
// Parse to single element (wrapped in Fragment if multiple)
const element = parseHtmlToElement('<div>Content</div>');
// Manual sanitization
const cleanAttrs = sanitizeAttributes({ onclick: 'alert(1)' }, 'div');
const cleanStyles = sanitizeStyles('color: red; font-size: 16px;');
const decoded = decodeHtmlEntities('Hello & World');
```
## 🧪 Testing
```tsx
import { render, screen } from '@testing-library/react';
import { HtmlParser } from 'react-safe-html-parser';
test('renders parsed HTML', () => {
render(<HtmlParser html="<h1>Test</h1>" />);
expect(screen.getByRole('heading')).toBeInTheDocument();
});
test('sanitizes dangerous content', () => {
render(<HtmlParser html="<div onclick='alert(1)'>Content</div>" />);
const div = screen.getByText('Content');
expect(div).not.toHaveAttribute('onclick');
});
```
## 🚀 Performance
### Memoization
The component automatically memoizes parsed output for performance:
```tsx
import { HtmlParser } from 'react-safe-html-parser';
function App() {
const html = '<div>Static content</div>';
// This will only parse once, even if the component re-renders
return <HtmlParser html={html} />;
}
```
### Tree Shaking
The library is fully tree-shakeable. Only import what you need:
```tsx
// Only import the component
import { HtmlParser } from 'react-safe-html-parser';
// Or import utilities separately
import { parseHtmlToReact } from 'react-safe-html-parser';
```
## 🔧 Troubleshooting
### Common Issues
**1. React 18+ Required**
```bash
# Make sure you have React 18+ installed
npm install react@^18.0.0 react-dom@^18.0.0
```
**2. SSR Issues**
```tsx
// For Next.js, ensure you're using the component in a client component
'use client';
import { HtmlParser } from 'react-safe-html-parser';
```
**3. TypeScript Errors**
```tsx
// Make sure you have React types installed
npm install --save-dev @types/react @types/react-dom
```
### Debug Mode
Enable error logging in development:
```tsx
<HtmlParser
html={html}
showErrors={process.env.NODE_ENV === 'development'}
fallback={<div>Parsing failed</div>}
/>
```
## 🤝 Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests
5. Submit a pull request
## 📄 License
MIT License - see [LICENSE](LICENSE) for details.
## 🙏 Acknowledgments
- Built with ❤️ for the React community
- Inspired by the need for secure HTML parsing in modern React applications
- Designed for seamless Vercel and Next.js integration
## Usage
### For SSR/PDF/Non-DOM (NO hooks, safe everywhere)
```tsx
import { parseHtmlToReact, parseHtmlToElement } from 'react-safe-html-parser';
const elements = parseHtmlToReact(htmlString, options); // returns array
const element = parseHtmlToElement(htmlString, options); // returns single element or Fragment
```
### For React DOM (with hooks, memoized)
```tsx
import { HtmlParser } from 'react-safe-html-parser';
<HtmlParser html={htmlString} options={...} />
```
### For SSR/PDF as a component (no hooks)
```tsx
import { HtmlParserStatic } from 'react-safe-html-parser';
<HtmlParserStatic html={htmlString} options={...} />
```
## When to use what?
- **SSR/PDF/Node.js**: Use `parseHtmlToReact` or `parseHtmlToElement` (pure functions, no hooks)
- **React DOM**: Use `HtmlParser` (uses hooks for memoization)
- **React PDF/SSR as a component**: Use `HtmlParserStatic` (no hooks)