Rich Text Parser System
A comprehensive, zero-dependency parsing system for rich content including markdown, emojis, images, and existing content pills (hashtags, mentions, URLs).
Features
- Markdown Support: Bold, italic, code, links, strikethrough, underline, blockquotes, lists
- Emoji Support: Unicode emojis + custom emoji registry with runtime additions
- Image Embeds: Multiple formats (markdown, HTML, custom) with security controls
- Content Pills: Hashtags, mentions, and URL pills (existing system)
- Zero Dependencies: No external libraries required
- Security First: XSS protection, URL sanitization, domain whitelisting
- Scalable: Modular architecture, easy to extend
Quick Start
Basic Usage
import { richTextParser } from '@lib/utils/parsers/rich-text-parser';
// Parse and render content
const content =
'Hello **world**! :smile: Check out ';
const html = richTextParser.replaceWithHtml(content);
React Component
import RichTextRenderer from '@/app/components/rich-text/RichTextRenderer';
function MyComponent() {
const content = '**Bold text** with :fire: emoji and #hashtag';
return (
<RichTextRenderer
content={content}
onEmojiClick={(emojiId) => console.log('Clicked emoji:', emojiId)}
onImageClick={(src) => console.log('Clicked image:', src)}
/>
);
}
Supported Syntax
Markdown
Syntax | Result | Example |
---|---|---|
**bold** | bold | **Hello world** |
*italic* | italic | *emphasized text* |
~~strikethrough~~ | ~~deleted text~~ | |
<u>underline</u> | underline | <u>important</u> |
`code` | code | `console.log()` |
[link](url) | link | [Google](https://google.com) |
> quote | > quote | > This is a quote |
- list item | • list item | - First item |
Emojis
Syntax | Result | Notes |
---|---|---|
:smile: | 😄 | Standard Unicode emoji |
:custom_emoji: | ![custom] | Custom emoji (if registered) |
Built-in Emojis: smile, joy, heart_eyes, wink, thinking, thumbsup, thumbsdown, clap, wave, heart, fire, rocket, star, trophy, computer, sun, moon, tree, flower, and more.
Images
Format | Syntax | Example |
---|---|---|
Markdown |  |  |
HTML | <img src="url" alt="alt"> | <img src="https://example.com/image.jpg" alt="Photo"> |
Custom | {img:url|alt|title|width|height} | {img:https://example.com/pic.jpg|Photo|My Photo|400|300} |
Content Pills (Existing System)
Type | Syntax | Example |
---|---|---|
Hashtag | #tagname | #javascript |
Mention | @[username|userId] | @[john|123] |
Enhanced Mention | @[username|userId|filterType] | @[john|123|author] |
URL Pill |  |  |
API Reference
RichTextParser
class RichTextParser {
constructor(config?: RichTextConfig);
parse(text: string): RichTextToken[];
replaceWithHtml(text: string): string;
tokensToHtml(tokens: RichTextToken[]): string;
updateConfig(config: Partial<RichTextConfig>): void;
addCustomEmoji(emoji: EmojiDefinition): void;
}
Configuration
interface RichTextConfig {
enableMarkdown?: boolean; // Default: true
enableEmojis?: boolean; // Default: true
enableImages?: boolean; // Default: true
enableHashtags?: boolean; // Default: true
enableMentions?: boolean; // Default: true
enableUrls?: boolean; // Default: true
imageConfig?: {
maxWidth?: number; // Default: 800
maxHeight?: number; // Default: 600
allowedDomains?: string[]; // Default: common CDNs
};
}
Individual Parsers
MarkdownParser
class MarkdownParser {
static parse(text: string): MarkdownToken[];
static tokensToHtml(tokens: MarkdownToken[]): string;
}
EmojiParser
class EmojiParser {
parse(text: string): EmojiToken[];
replaceEmojisWithHtml(text: string): string;
getSuggestions(query: string, limit?: number): EmojiDefinition[];
addCustomEmoji(emoji: EmojiDefinition): void;
}
ImageParser
class ImageParser {
constructor(config?: ImageConfig);
parse(text: string): ImageToken[];
replaceImagesWithHtml(text: string): string;
updateConfig(config: Partial<ImageConfig>): void;
}
Advanced Usage
Custom Emoji Registry
import { emojiRegistry } from '@lib/utils/parsers/emoji-parser';
// Add custom emoji
emojiRegistry.registerEmoji({
id: 'custom_logo',
name: 'custom_logo',
imageUrl: 'https://example.com/logo.png',
category: 'custom',
tags: ['logo', 'brand'],
aliases: ['logo', 'brand']
});
// Search emojis
const results = emojiRegistry.searchEmojis('happy');
Image Security Configuration
import { imageParser } from '@lib/utils/parsers/image-parser';
imageParser.updateConfig({
maxWidth: 1200,
maxHeight: 800,
allowedDomains: ['imgur.com', 'github.com', 'your-cdn.com'],
allowedExtensions: ['jpg', 'png', 'gif', 'webp']
});
Selective Parsing
// Only enable specific features
const parser = new RichTextParser({
enableMarkdown: true,
enableEmojis: true,
enableImages: false, // Disable images
enableHashtags: false, // Disable hashtags
enableMentions: true,
enableUrls: true
});
Token-Level Processing
const tokens = richTextParser.parse('**Bold** text with :smile:');
tokens.forEach((token) => {
switch (token.type) {
case 'markdown':
console.log('Markdown:', token.markdownType, token.content);
break;
case 'emoji':
console.log('Emoji:', token.emojiId, token.emojiUnicode);
break;
case 'text':
console.log('Text:', token.content);
break;
}
});
React Components
RichTextRenderer
<RichTextRenderer
content="Your **rich** content here :smile:"
config={{
enableMarkdown: true,
enableEmojis: true,
enableImages: true
}}
onEmojiClick={(emojiId) => handleEmojiClick(emojiId)}
onImageClick={(src) => openImageModal(src)}
onLinkClick={(url) => handleLinkClick(url)}
maxLength={500}
className="my-rich-text"
/>
EmojiPicker
<EmojiPicker
onEmojiSelect={(emoji) => insertEmoji(emoji)}
categories={['faces', 'gestures', 'hearts']}
searchQuery={searchTerm}
maxResults={30}
/>
EmojiRenderer
<EmojiRenderer emojiId="smile" size="large" className="my-emoji" />
Security Features
XSS Protection
- All user input is escaped before rendering
- HTML attributes are sanitized
- Dangerous protocols (javascript:, data:) are blocked
URL Sanitization
- Only HTTPS URLs allowed for external content
- Domain whitelisting for images
- Protocol validation for all links
Content Security Policy
- Image sources are validated against allowlist
- Custom emoji URLs are sanitized
- File extension validation for images
Performance Considerations
Parsing Order
- Images (highest priority - avoid markdown conflicts)
- Content Pills (hashtags, mentions, URLs)
- Markdown (after URLs to prevent conflicts)
- Emojis (lowest priority - fill remaining text)
Optimization Tips
- Use
useMemo
in React components to cache parsed content - Implement debouncing for real-time preview
- Consider pagination for large emoji sets
- Use lazy loading for custom emoji images
Browser Support
- Modern Browsers: Full support (Chrome 80+, Firefox 75+, Safari 13+)
- Legacy Browsers: Graceful degradation (emojis show as text)
- Mobile: Responsive design with touch-friendly controls
Migration from Existing System
The new parser is fully backward compatible with existing content pills:
// Old way (still works)
import { renderContentWithPills } from '@lib/utils/content-parsers';
const html = renderContentWithPills(content);
// New way (enhanced features)
import { renderRichContent } from '@lib/utils/content-parsers';
const html = renderRichContent(content, {
enableMarkdown: true,
enableEmojis: true,
enableImages: true
});
Examples
Blog Post Content
const blogContent = `
# My Blog Post
This is **bold** and *italic* text with some \`code\`.
> Here's an important quote
Check out this image: 
And here's a custom emoji: :custom_logo:
Don't forget to follow me: @[username|123] and check out #webdev
`;
const html = richTextParser.replaceWithHtml(blogContent);
Social Media Post
const socialPost = `
Just launched my new app! :rocket: :fire:
**Features:**
- Real-time chat :speech_balloon:
- Image sharing 
- Custom emojis :custom_heart:
Check it out: 
#webapp #javascript #react
`;
Comment System
const comment = `
Great post @[author|456]! :thumbsup:
The part about **performance optimization** was especially helpful.
Here's a related article: 
`;
Troubleshooting
Common Issues
- Emojis not showing: Check emoji registry and ensure proper aliases
- Images not loading: Verify domain whitelist and HTTPS requirements
- Markdown conflicts: Review parsing order and escape special characters
- Performance issues: Implement memoization and debouncing
Debug Mode
// Enable debug logging
const parser = new RichTextParser();
const tokens = parser.parse(content);
console.log('Parsed tokens:', tokens);
Testing
// Test individual parsers
import { MarkdownParser } from '@lib/utils/parsers/markdown-parser';
const tokens = MarkdownParser.parse('**test**');
console.log(tokens); // [{ type: 'bold', content: 'test', ... }]