npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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

Readme

@laddhaanshul/content-renderer

npm version npm downloads license TypeScript

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-renderer

Peer 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'; // a11y

TypeScript

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