UNPKG

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
# 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 &amp; World &lt;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 &amp; 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)