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

quill-delta-renderer

v0.1.5

Published

A framework-agnostic, AST-based engine for converting Quill Deltas into HTML, Markdown, React components, or any other format.

Downloads

303

Readme

quill-delta-renderer

CI npm version license

The modern, type-safe way to render Quill Deltas anywhere — server, client, or CLI.

Render rich Quill content as HTML, React components, or Markdown without needing the Quill editor or a browser DOM. Fully tree-shakeable. Zero dependencies. Written in TypeScript.

Try the live demo

Why this library?

If you're using Quill, you've likely hit one of these walls:

| Problem | How quill-delta-renderer helps | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | "I need to render Deltas on the server." Quill requires a browser DOM, so SSR in Next.js, Nuxt, or plain Node is painful. | Works in any JavaScript runtime — Node, Deno, Bun, edge functions — no DOM required. | | "My React preview re-renders everything on each edit." Using dangerouslySetInnerHTML replaces the entire DOM subtree whenever the Delta changes — no reconciliation, no partial updates. | The React renderer returns a native ReactNode tree. React only updates the nodes that actually changed, and you can swap in custom components like <CustomImage> or <LinkPreview>. | | "I need to convert Deltas to Markdown." Mapping rich formatting (colors, underlines, tables) to Markdown is surprisingly hard. | Three Markdown renderers handle this out of the box — strict, HTML-flavored, or bracket-tagged. | | "Adding a custom embed or block type is way too hard." Extending older converters often means monkey-patching or forking. | An extensible renderer API with full TypeScript autocomplete — register a custom block handler in a few lines. |

At a glance

import { parseQuillDelta } from "quill-delta-renderer";
import { SemanticHtmlRenderer } from "quill-delta-renderer/html";

const delta = {
  ops: [
    { insert: "Hello" },
    { insert: ", world!", attributes: { bold: true } },
    { insert: "\n" },
  ],
};

const html = new SemanticHtmlRenderer().render(parseQuillDelta(delta));
// → '<p>Hello<strong>, world!</strong></p>'

Two lines. Delta in, HTML out. Works the same on the server and in the browser.

Install

npm install quill-delta-renderer

The React renderer is optional — add React only if you use it:

npm install react react-dom

Usage

HTML (server or client)

import { parseQuillDelta } from "quill-delta-renderer";
import { SemanticHtmlRenderer } from "quill-delta-renderer/html";

const ast = parseQuillDelta(delta);
const html = new SemanticHtmlRenderer().render(ast);

React — no dangerouslySetInnerHTML

Render Deltas directly into a React component tree. By default, it outputs standard HTML tags (<p>, <h1>, <strong>, etc.). You can override any block with your own component:

import { parseQuillDelta } from "quill-delta-renderer";
import { ReactRenderer } from "quill-delta-renderer/react";

const renderer = new ReactRenderer({
  components: {
    image: ({ node }) => <CustomImage src={node.data} />,
    video: ({ node }) => <VideoPlayer url={node.data} />,
    paragraph: ({ children, className }) => (
      <p className={className}>{children}</p>
    ),
  },
});

const element = renderer.render(parseQuillDelta(delta));
// Use `element` directly in JSX — it's a ReactNode

Every block type (paragraph, header, blockquote, code-block, list, list-item, image, video, table, table-row, table-cell, formula, mention) can be overridden via components.

Markdown

import { parseQuillDelta } from "quill-delta-renderer";
import { MarkdownRenderer } from "quill-delta-renderer/markdown";

const md = new MarkdownRenderer().render(parseQuillDelta(delta));

Three flavors are available:

| Renderer | Non-standard formats (underline, color, etc.) | | ------------------------- | --------------------------------------------------------- | | MarkdownRenderer | Stripped — strict standard Markdown only | | HtmlMarkdownRenderer | Rendered as inline HTML (<u>, <sub>, <span>) | | BracketMarkdownRenderer | Rendered as bracket tags ([STYLE color=red]...[/STYLE]) |

See the format docs: HTML Markdown · Bracket Markdown

Migrating from quill-delta-to-html

SemanticHtmlRenderer is designed to produce output compatible with quill-delta-to-html, so migration can be as simple as swapping the import:

Before:

import { QuillDeltaToHtmlConverter } from "quill-delta-to-html";

const converter = new QuillDeltaToHtmlConverter(delta.ops, {});
const html = converter.convert();

After:

import { parseQuillDelta } from "quill-delta-renderer";
import { SemanticHtmlRenderer } from "quill-delta-renderer/html";

const html = new SemanticHtmlRenderer().render(parseQuillDelta(delta));

What you gain immediately:

  • Full TypeScript autocomplete for every config option and custom handler
  • Tree-shakeable — import only the renderer you use; unused code is never bundled
  • The same extensibility API works across HTML, React, and Markdown renderers
  • Significantly faster rendering (see Performance)

Compatibility note: Default configuration targets high compatibility with quill-delta-to-html output. For edge cases, see the config options (inlineStyles, classPrefix, linkTarget, etc.) and the configuration reference below. We recommend comparing output on a few representative documents during migration.

Configuration

All renderers accept an optional config object. Every option has a sensible default.

SemanticHtmlRenderer

new SemanticHtmlRenderer({
  classPrefix: "ql", // CSS class prefix (default: 'ql')
  paragraphTag: "p", // Tag for paragraphs (default: 'p')
  linkTarget: "_blank", // Link target attribute (default: '_blank')
  linkRel: "noopener", // Link rel attribute
  inlineStyles: false, // Use inline styles instead of classes
  encodeHtml: true, // HTML-encode text content (default: true)
  customTag: (fmt, node) => {
    /* return a custom tag string or undefined */
  },
});

ReactRenderer

new ReactRenderer({
  classPrefix: "ql",
  linkTarget: "_blank",
  linkRel: "noopener",
  customTag: (fmt, node) => {
    /* return a custom tag string or undefined */
  },
  components: {
    image: ({ node }) => <CustomImage src={node.data} />,
    // override any block type
  },
});

MarkdownRenderer

new MarkdownRenderer({
  bulletChar: "*", // Unordered list character (default: '*')
  fenceChar: "```", // Fenced code block delimiter (default: '```')
  embedHandler: (node) => {
    /* return string for custom embeds */
  },
  embedAttributesHandler: (node) => {
    /* return { key: value } for attribute-only embeds */
  },
});

HtmlMarkdownRenderer and BracketMarkdownRenderer accept the same config.

parseQuillDelta

parseQuillDelta(delta, {
  extraBlockAttributes: { ... },   // Additional block attribute handlers
  blockEmbeds: ['video'],          // Block-level embed types (default: ['video'])
  extraTransformers: [myGrouper],  // Appended after standard transformers
  transformers: [...],             // Replace standard transformers entirely
});

Extending the library

Custom block handling with transformers

A transformer is a function that receives the AST's children array and returns a new array. Use this to group, wrap, or reorganize nodes before rendering:

import type { TNode, Transformer } from "quill-delta-renderer/core";

const imageGallery: Transformer = (children: TNode[]) => {
  // group adjacent images into a gallery container
  return groupImages(children);
};

const ast = parseQuillDelta(delta, {
  extraTransformers: [imageGallery],
});

Handling custom embeds

Quill allows custom embeds (e.g., a custom video player, a mention, or a poll). To render them, first register the embed type in parseQuillDelta, then provide a render handler for your chosen output format:

// 1. Tell the parser about your custom block-level embed
const ast = parseQuillDelta(delta, {
  blockEmbeds: ['video', 'poll'], // 'video' is default, add yours
});

// 2. Handle it in your renderer (React example)
const element = new ReactRenderer({
  components: {
    poll: ({ node }) => <Poll id={node.data.id} />,
  },
}).render(ast);

Writing a custom renderer

For output formats that need HTML-style attribute collection, extend BaseRenderer. For simpler formats (plain text, Markdown-like), extend SimpleRenderer — you only need two methods:

import { SimpleRenderer } from "quill-delta-renderer/core";

class PlainTextRenderer extends SimpleRenderer<string> {
  protected joinChildren(children: string[]) {
    return children.join("");
  }
  protected renderText(text: string) {
    return text;
  }
}

Tree-shakeable imports

Import only what you need — unused renderers are never bundled:

| Import path | Contents | | ------------------------------- | --------------------------------------------------------------------- | | quill-delta-renderer | Barrel export including parseQuillDelta | | quill-delta-renderer/core | parseDelta, BaseRenderer, SimpleRenderer, types | | quill-delta-renderer/common | Transformers, sanitizers, shared utilities | | quill-delta-renderer/html | SemanticHtmlRenderer, QuillHtmlRenderer | | quill-delta-renderer/markdown | MarkdownRenderer, HtmlMarkdownRenderer, BracketMarkdownRenderer | | quill-delta-renderer/react | ReactRenderer |

Performance

For a single blog post, rendering speed is rarely a bottleneck. Where performance matters is SSR throughput — rendering hundreds of pages per second — and bulk processing large document sets.

In those scenarios, quill-delta-renderer delivers measurable gains:

  • HTML: 2–5× faster than quill-delta-to-html (3.65× on a realistic mixed-content document)
  • React: 1.5–2.9× faster than quill-delta-to-react (2.22× on a realistic mixed-content document)

Full methodology and results: BENCHMARKS.md

API documentation

The library is fully typed. We recommend relying on your IDE's IntelliSense to explore the available configuration options, AST node types, and renderer methods.

Contributing

Contributions are welcome! To get started:

npm install
npm run build
npm test

Please ensure all tests pass and the code is formatted before submitting a pull request.

License

MIT