@laddhaanshul/content-renderer
v1.0.7
Published
Universal React and React Native components for rendering HTML, Markdown, JSON, XML, PHP, CSS and source code. Auto-detects content type. Runs on web and mobile from a single import.
Downloads
684
Maintainers
Readme
@laddhaanshul/content-renderer
Universal React components for rendering HTML, Markdown, JSON, XML, PHP, CSS, and source code — on both React DOM (web) and React Native (mobile) from a single import. Components auto-select the correct platform implementation at bundle time.
🌍 Official Website & Documentation: https://content-renderer.anshulladdha.in/
Installation
npm install @laddhaanshul/content-renderer
yarn add @laddhaanshul/content-renderer
pnpm add @laddhaanshul/content-rendererPeer Dependencies:
| Dependency | Version | Required |
|------------|---------|----------|
| react | >=17.0.0 | ✅ Always |
| react-dom | >=17.0.0 | Web only |
| react-native | >=0.68.0 | Native only |
Also installs: @laddhaanshul/content-renderer-core (same version — automatically kept in sync).
Quick Start
import { ContentRenderer } from '@laddhaanshul/content-renderer';
// Auto-detects HTML, JSON, Markdown, code, XML, CSS, PHP
<ContentRenderer content={anyString} />
// Force type + dark theme
<ContentRenderer
content={markdownString}
contentType="markdown"
theme={darkTheme}
onError={(err) => console.error(err)}
/>Components
ContentRenderer — Universal Auto-Detect
The single entry point. Detects the content type and delegates to the right renderer.
import { ContentRenderer } from '@laddhaanshul/content-renderer';
<ContentRenderer
content={anyString}
contentType="auto" // 'auto' | 'html' | 'json' | 'markdown' | 'code' | 'xml' | 'css' | 'php'
theme={darkTheme}
sanitize={true}
allowedTags={['p', 'a', 'strong']}
fallback={<MyErrorUI />}
loading={<Spinner />}
linkHandler={(href) => navigate(href)}
imageHandler={(src) => `/cdn${src}`}
onError={(err) => report(err)}
onRender={() => console.log('rendered')}
/>All props:
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| content | string | required | Content to render |
| contentType | ContentType \| 'auto' | 'auto' | Force or auto-detect type |
| theme | Partial<Theme> | — | Theme overrides |
| sanitize | boolean | true | Enable HTML sanitization |
| allowedTags | string[] | — | Allowed HTML tags |
| allowedAttributes | Record<string, string[]> | — | Allowed attributes per tag |
| maxDepth | number | — | Max render tree depth |
| components | Record<string, ComponentType> | — | Custom component per tag |
| renderers | Record<string, ComponentType> | — | Custom renderers per content type |
| transform | (node) => any | — | Node transform function |
| linkHandler | (href, e?) => void | — | Link click handler |
| imageHandler | (src, alt?) => string | — | Image URL rewriter |
| codeBlockHandler | (code, lang) => ReactNode | — | Custom code block |
| tableHandler | (table) => ReactNode | — | Custom table renderer |
| fallback | ReactNode | — | Shown on error |
| loading | ReactNode | — | Shown while loading |
| onError | (err: Error) => void | — | Error callback |
| onRender | () => void | — | Render complete callback |
| testID | string | — | Test ID (React Native) |
| accessible | boolean | — | Accessibility enable |
| accessibilityLabel | string | — | Accessibility label |
| className | string | — | CSS class (web only) |
| style | CSSProperties | — | Inline style (web only) |
HTMLRenderer
Renders raw HTML strings as React elements with full DOM support.
import { HTMLRenderer } from '@laddhaanshul/content-renderer';
<HTMLRenderer
html="<h1>Hello</h1><p>Click <a href='/page'>here</a></p>"
sanitize={true}
components={{
h1: ({ children }) => <h1 className="title">{children}</h1>,
a: CustomLink,
}}
onLinkClick={(href, event) => { event.preventDefault(); navigate(href); }}
wrapperTag="article"
transform={(node, element) => {
if (node.tagName === 'img') return <LazyImage {...element.props} />;
return element;
}}
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| html | string | required | Raw HTML |
| components | Record<string, ComponentType> | — | Tag overrides |
| onLinkClick | (href, event) => void | — | Anchor click |
| onFormSubmit | (event) => void | — | Form submit |
| sanitize | boolean | false | Enable sanitization |
| renderComments | boolean | false | Render HTML comments |
| wrapperTag | keyof JSX.IntrinsicElements | 'div' | Wrapper tag |
| allowDangerousHTML | boolean | false | dangerouslySetInnerHTML |
| transform | (node, element) => ReactElement \| null | — | Element transform |
| fallback | ReactNode | — | Parse failure fallback |
| onRenderComplete | () => void | — | Render complete callback |
MarkdownRenderer
Renders GitHub-Flavored Markdown with custom component support.
import { MarkdownRenderer } from '@laddhaanshul/content-renderer';
<MarkdownRenderer
content={markdownString}
components={{
code: ({ children, className }) => (
<SyntaxHighlighter language={className?.replace('language-', '')}>
{children}
</SyntaxHighlighter>
),
}}
linkHandler={(href) => navigate(href)}
imageHandler={(src) => `/cdn${src}`}
escapeHtml={false}
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| content | string | required | Markdown string |
| components | Record<string, ComponentType> | — | Custom node renderers |
| linkHandler | (href) => void | — | Link click handler |
| imageHandler | (src) => string | — | Image URL rewriter |
| escapeHtml | boolean | — | Escape HTML in Markdown |
| skipHtml | boolean | — | Skip HTML blocks |
| linkTarget | string | — | Default link target |
| plugins | any[] | — | Remark/rehype plugins |
JSONRenderer
Interactive, collapsible JSON tree viewer with search and copy.
import { JSONRenderer } from '@laddhaanshul/content-renderer';
<JSONRenderer
json={JSON.stringify(apiResponse, null, 2)}
theme="dark"
searchable
sortKeys
defaultCollapseDepth={1}
showTypes
showCopyButton
onValueClick={(path, value) => console.log(path, value)}
excludeKeys={['__meta', '_internal']}
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| json | string \| unknown | required | JSON string or value |
| theme | 'light' \| 'dark' | 'light' | Color theme |
| indent | number | 2 | Indent spaces |
| defaultCollapseDepth | number | Infinity | Auto-collapse depth |
| sortKeys | boolean | false | Alphabetical keys |
| showTypes | boolean | true | Type badges |
| showArrayIndices | boolean | true | Array indices |
| showCopyButton | boolean | true | Copy button |
| searchable | boolean | false | Search bar |
| maxDepth | number | 50 | Max render depth |
| onValueClick | (path, value) => void | — | Value click handler |
| excludeKeys | string[] | — | Keys to hide |
| includeKeys | string[] | — | Keys to show exclusively |
CodeRenderer
Syntax-highlighted code blocks with line numbers, diff highlighting, and copy.
import { CodeRenderer } from '@laddhaanshul/content-renderer';
<CodeRenderer
code={sourceCode}
language="typescript"
showLineNumbers
highlightLines={[3, 4, 5]}
theme="monokai"
showCopyButton
wrapLines={false}
startingLineNumber={1}
fontSize={14}
tabSize={2}
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| code | string | required | Source code |
| language | string | — | Language identifier |
| showLineNumbers | boolean | false | Show line numbers |
| highlightLines | number[] | — | Lines to highlight |
| theme | string | 'light' | light|dark|monokai|dracula|github|vscode |
| wrapLines | boolean | false | Line wrapping |
| startingLineNumber | number | 1 | First line number |
| showCopyButton | boolean | true | Copy button |
| fontSize | number | — | Font size px |
| tabSize | number | 2 | Tab width |
Supported languages (56): javascript, typescript, jsx, tsx, python, java, go, rust, c, cpp, csharp, php, ruby, swift, kotlin, scala, sql, bash, sh, yaml, json, xml, html, css, scss, graphql, markdown, dockerfile, toml, ini, and 26 more.
DiffRenderer
Side-by-side or unified diff viewer.
import { DiffRenderer } from '@laddhaanshul/content-renderer';
import type { DiffRendererProps } from '@laddhaanshul/content-renderer';
<DiffRenderer
oldContent={previousCode}
newContent={updatedCode}
mode="unified" // 'unified' | 'split'
language="javascript"
showLineNumbers
theme="dark"
/>VirtualizedCodeRenderer
Performance-optimized code renderer for files with thousands of lines.
import { VirtualizedCodeRenderer } from '@laddhaanshul/content-renderer';
import type { VirtualizedCodeRendererProps } from '@laddhaanshul/content-renderer';
<VirtualizedCodeRenderer
code={largeFile}
language="python"
height={600}
rowHeight={22}
overscanCount={10}
/>PHPRenderer
PHP source code with dedicated syntax highlighting.
import { PHPRenderer } from '@laddhaanshul/content-renderer';
<PHPRenderer content={phpCode} showLineNumbers theme="monokai" />XMLRenderer
Collapsible XML tree view with attribute highlighting.
import { XMLRenderer } from '@laddhaanshul/content-renderer';
<XMLRenderer content={xmlString} defaultCollapsed={false} theme="dark" />CSSRenderer
CSS source display with property and value highlighting.
import { CSSRenderer } from '@laddhaanshul/content-renderer';
<CSSRenderer content={cssString} showLineNumbers theme="github" />ContentServiceRenderer
Fetches content from a URL and renders it directly. Supports AEM, headless CMS, and any REST API.
import { ContentServiceRenderer } from '@laddhaanshul/content-renderer';
// Auto-detect
<ContentServiceRenderer
url="https://api.example.com/pages/home"
config={{ extractStrategy: 'auto' }}
loading={<Spinner />}
errorRenderer={(error, retry) => (
<div>
<p>{error.message}</p>
<button onClick={retry}>Retry</button>
</div>
)}
/>
// AEM
<ContentServiceRenderer
url={`https://publish-p123-e456.adobeaemcloud.com${path}.model.json`}
config={{ extractStrategy: 'aem' }}
sanitize
/>
// Headless CMS (WordPress / Strapi / Contentful)
<ContentServiceRenderer
url={`/api/posts/${id}`}
config={{
extractStrategy: 'headless-cms',
contentField: 'content',
headers: { 'X-API-Key': process.env.API_KEY },
}}
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| url | string | — | URL to fetch |
| config | Partial<ContentServiceConfig> | — | Service configuration |
| loading | ReactNode | — | Loading state |
| errorRenderer | (error, retry) => ReactNode | — | Error with retry |
| fallback | ReactNode | — | Empty content fallback |
| fetchOnMount | boolean | true | Fetch on mount |
| fetchKey | string \| number | — | Refetch trigger key |
| fetchDebounce | number | 0 | Debounce ms |
| onLoad | (result) => void | — | Success callback |
| onLoadError | (error) => void | — | Error callback |
Extraction Strategies:
| Strategy | Use Case |
|----------|----------|
| auto | General purpose — auto-detects |
| direct | Raw HTML/Markdown response |
| json-html | JSON with HTML content field |
| json-markdown | JSON with Markdown content field |
| json-field | JSON path extraction |
| json-property | Unknown JSON structures |
| aem | Adobe Experience Manager |
| headless-cms | WordPress, Strapi, Contentful |
| custom | Fully custom transformResponse |
ErrorBoundary
Catches rendering errors and displays a fallback.
import { ErrorBoundary } from '@laddhaanshul/content-renderer';
<ErrorBoundary
fallback={<div className="error">Something went wrong</div>}
onError={(error, info) => logToSentry(error, info)}
>
<ContentRenderer content={content} />
</ErrorBoundary>StreamingContentRenderer
Real-time incremental rendering for chunked HTML or AST nodes. Ideal for LLM and generative AI streaming responses.
import { StreamingContentRenderer } from '@laddhaanshul/content-renderer';
<StreamingContentRenderer
stream={htmlStream} // A ReadableStream or mock async iterator yielding strings/bytes
astStream={astStream} // Alternatively, a stream of pre-parsed AST nodes
fallback={<div>Waiting for stream...</div>}
onStreamStart={() => console.log('Stream started')}
onStreamComplete={() => console.log('Stream complete')}
onStreamError={(err) => console.error(err)}
/>| Prop | Type | Default | Description |
|------|------|---------|-------------|
| stream | ReadableStream<Uint8Array> \| any | — | Stream yielding HTML chunks |
| astStream | ReadableStream<any> \| any | — | Stream yielding pre-parsed AST nodes |
| fallback | ReactNode | — | Fallback component to show before stream starts |
| onStreamStart | () => void | — | Callback triggered when stream starts |
| onStreamComplete | () => void | — | Callback triggered when stream completes |
| onStreamError | (error) => void | — | Callback triggered on stream error |
Hooks
All hooks from @laddhaanshul/content-renderer-core are re-exported here for convenience:
import {
useContentParser, // Parse content programmatically
useExtract, // Extract links/images/meta/etc.
useTheme, // Manage light/dark theme
useContentService, // Fetch + render from URL
} from '@laddhaanshul/content-renderer';Animation Hooks
import {
useFadeIn, // Fade-in opacity animation
useSlideIn, // Slide-in transform animation
useCollapseAnimation, // Expand/collapse height animation
useTypewriter, // Typewriter text effect
useThemeTransition, // Smooth theme transition
useScrollAnimation, // Trigger animation on scroll into view
animateNumber, // Animate a numeric value over time
createStaggerAnimation, // Stagger children animations
getTransitionCSS, // Get CSS transition string
} from '@laddhaanshul/content-renderer';
import type { EasingFunction } from '@laddhaanshul/content-renderer';
// Example: fade in on mount
function MyComponent() {
const { style, ref } = useFadeIn({ duration: 300, delay: 100 });
return <div ref={ref} style={style}>Hello</div>;
}
// Example: typewriter
function TypewriterDemo() {
const { displayText } = useTypewriter({ text: 'Hello, World!', speed: 60 });
return <p>{displayText}</p>;
}Platform Utilities
Web (React DOM)
import {
styleStringToObject, // 'color: red; font-size: 14px' → { color: 'red', fontSize: '14px' }
attrToReactProp, // 'class' → 'className', 'for' → 'htmlFor'
isBooleanAttribute, // 'disabled' → true
isVoidElement, // 'img' → true
svgAttrToReact, // SVG attribute name conversion
isSVGElement, // true if tag is an SVG element
parseAttributes, // Parse raw attribute string to object
tokenize, // Tokenize source code into tokens
highlight, // Syntax highlight source code
getSupportedLanguages, // List all supported languages
isLanguageSupported, // Check language support
resolveLanguageName, // 'js' → 'javascript'
tokensToHtml, // Convert tokens to HTML string
} from '@laddhaanshul/content-renderer';
import type { Token, TokenType } from '@laddhaanshul/content-renderer';Native (React Native)
import {
HTML_TO_RN_MAP, // HTML tag → RN component mapping
styleStringToRNStyle, // CSS string → React Native StyleSheet
classToRNStyle, // CSS class → RN style object
flattenInlineNodes, // Flatten inline text nodes for Text component
isInlineNode, // true if node renders inline
tokenizeLine, // Tokenize a single line for Native code view
highlightCode, // Native syntax highlighting
detectLanguage, // Auto-detect language from code
LIGHT_SYNTAX_THEME, // Light native syntax theme
DARK_SYNTAX_THEME, // Dark native syntax theme
lightNativeTheme, // Light theme for native components
darkNativeTheme, // Dark theme for native components
mergeNativeTheme, // Deep merge two native themes
} from '@laddhaanshul/content-renderer';
import type {
HTMLTagMappingRN, HTMLNodeRN,
SyntaxToken, SyntaxTheme, TokenizerState,
NativeTheme, NativeThemeColors, NativeThemeTypography,
NativeThemeSpacing, NativeThemeCodeBlock,
} from '@laddhaanshul/content-renderer';Worker (background highlighting)
import {
createHighlightWorker, // Create a Web Worker for highlighting
highlightInWorker, // Highlight in background thread
highlightOnce, // One-shot background highlight
} from '@laddhaanshul/content-renderer';
import type { WorkerMessage, WorkerResult, HighlightToken } from '@laddhaanshul/content-renderer';Re-exports from @laddhaanshul/content-renderer-core
Everything from @laddhaanshul/content-renderer-core is re-exported for convenience:
// Parsers
import { HTMLParser, JSONParser, XMLParser, PHPParser, MarkdownParser, CSSParser }
from '@laddhaanshul/content-renderer';
// Extraction
import { extractAll, extractLinks, extractSEO, extractOpenGraph, extractStructuredData }
from '@laddhaanshul/content-renderer';
// Sanitization
import { sanitizeHTML, stripTags, escapeHTML, DEFAULT_ALLOWED_TAGS }
from '@laddhaanshul/content-renderer';
// Transform
import { minifyHTML, formatHTML, detectContentType, slugify }
from '@laddhaanshul/content-renderer';
// Validation
import { isValidHTML, isValidJSON, isValidURL }
from '@laddhaanshul/content-renderer';
// Hooks
import { useContentParser, useExtract, useTheme, useContentService }
from '@laddhaanshul/content-renderer';
// HOCs
import { withContentParser, withExtract }
from '@laddhaanshul/content-renderer';
// Provider + themes
import { ContentParserProvider, lightTheme, darkTheme }
from '@laddhaanshul/content-renderer';
// Plugin system
import { PluginManager, lineNumbersPlugin, tocPlugin, sanitizePlugin }
from '@laddhaanshul/content-renderer';
// Advanced
import { renderToString, generateHeadTags } from '@laddhaanshul/content-renderer'; // SSR
import { isRTL, formatDate, SUPPORTED_LOCALES } from '@laddhaanshul/content-renderer'; // i18n
import { downloadPDF, previewPDF } from '@laddhaanshul/content-renderer'; // PDF
import { createDiff, createUnifiedDiff } from '@laddhaanshul/content-renderer'; // Diff
import { queryPath, queryPathSingle } from '@laddhaanshul/content-renderer'; // JSONPath
import { EXTENDED_LANGUAGES, THEME_REGISTRY } from '@laddhaanshul/content-renderer'; // Syntax
import { recoverFromHTMLError, suggestFixes } from '@laddhaanshul/content-renderer'; // Errors
import { validateAccessibility, checkColorContrast } from '@laddhaanshul/content-renderer'; // a11yTypeScript
Full types ship with the package. Key types from this package:
import type {
DiffRendererProps,
VirtualizedCodeRendererProps,
EasingFunction,
HTMLTagMappingRN, HTMLNodeRN,
SyntaxToken, SyntaxTheme, TokenizerState,
NativeTheme, NativeThemeColors, NativeThemeCodeBlock,
WorkerMessage, WorkerResult, HighlightToken,
Token, TokenType,
} from '@laddhaanshul/content-renderer';
// Re-exported core types:
import type {
ContentType, ParsedContent, Theme, ContentServiceConfig,
ContentExtractStrategy, ContentServiceRendererProps,
BaseRendererProps, HTMLRendererProps, CodeRendererProps,
JSONRendererProps, MarkdownRendererProps, PHPRendererProps,
UseContentParserOptions, UseContentServiceReturn,
PluginDefinition, SanitizeOptions, SSRRenderOptions,
} from '@laddhaanshul/content-renderer';React Native Setup
Metro automatically resolves .native.tsx files when bundling for React Native — no extra configuration needed.
// Works identically on web and native
import { HTMLRenderer, CodeRenderer } from '@laddhaanshul/content-renderer';
<CodeRenderer code={snippet} language="javascript" showLineNumbers />For Expo-managed workflows, add to metro.config.js if needed:
const { getDefaultConfig } = require('expo/metro-config');
const config = getDefaultConfig(__dirname);
config.resolver.sourceExts.push('native.tsx', 'native.ts');
module.exports = config;Browser Support
| Browser | Support | |---------|---------| | Chrome / Edge (last 2) | ✅ | | Firefox (last 2) | ✅ | | Safari (last 2) | ✅ | | Node.js ≥ 18 (SSR) | ✅ |
License
MIT © content-renderer contributors
