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

@davaux/rich-text

v0.9.0

Published

ProseMirror-backed rich text editor for Davaux, stored and loaded as OML

Downloads

10

Readme

@davaux/rich-text

ProseMirror-backed rich text editor for Davaux. Content is stored and loaded as OML — the same serializable JSX-tree format used everywhere else in Davaux — so there's no separate markdown/HTML column and no separate parser for rich text fields.

Installation

npm install @davaux/rich-text

Setup

1. Register the plugin

Add richTextPlugin() to davaux.config.ts. This registers the editor's reactive components for client-side hydration:

// davaux.config.ts
import { defineConfig } from 'davaux/config'
import { richTextPlugin } from '@davaux/rich-text/plugin'

export default defineConfig({
  plugins: [richTextPlugin()],
})

2. Import the stylesheet

// src/routes/_layout.tsx
import '@davaux/ui/prose.css'
import '@davaux/rich-text/styles.css'

@davaux/rich-text renders its content with the dv-prose class, so @davaux/ui's prose.css must be present too (typography for headings, lists, blockquotes, etc.).

Basic usage

import { RichTextEditor } from '@davaux/rich-text'
import type { OmlNode } from 'davaux/oml'

function ArticleForm({ initialBody }: { initialBody: OmlNode }) {
  return (
    <RichTextEditor
      value={initialBody}
      onChange={(oml) => console.log('updated OML:', oml)}
    />
  )
}

value is the current OML content (or null for an empty document); onChange fires with the updated OML tree on every edit.

Using it as a form field

Pass name to render a hidden <input> that stays in sync with the current OML as JSON — this drops the editor straight into a native <form method="post">, no client-side fetch/submit wiring required:

<form method="post">
  <RichTextEditor name="body" value={initialBody} placeholder="Write your post…" />
  <button type="submit">Publish</button>
</form>

On the server, parse the submitted field back into OML before storing it. Store the raw JSON string as-is (e.g. field.body) rather than re-serializing — parseRichText (see below) expects that same string shape when reading it back, including its plain-text fallback for pre-migration content:

import { parseOml } from 'davaux/oml'

const body = parseOml(JSON.parse(field.body)) // OmlNode

Headless mode

Pass hideToolbar to render the editing surface without the built-in toolbar — useful if you want to build custom controls against the same docToOml/omlToDoc conversion:

<RichTextEditor value={initialBody} hideToolbar />

Rendering stored content (no editing)

Use RichTextReader to display stored content anywhere without pulling in ProseMirror. It takes the raw stored string directly — the same JSON that RichTextEditor's hidden input produces — and handles the plain-text fallback for content stored before a rich-text migration:

import { RichTextReader } from '@davaux/rich-text'

<RichTextReader value={article.body} />

Pass options={{ links: false }} to strip link marks down to plain text — useful when the reader itself sits inside an outer link (e.g. a feed card linking to the full article), where a nested <a> would produce invalid, unpredictably-clickable nested anchors:

<a href={`/articles/${article.slug}`}>
  <RichTextReader value={article.excerptBody} options={{ links: false }} />
</a>

If you already have a parsed OmlNode rather than the stored string (e.g. straight from RichTextEditor's onChange), use renderToHtml from davaux/oml directly instead — it's the same renderer RichTextReader and RichTextEditor use internally for their initial paint:

import { renderToHtml } from 'davaux/oml'

<div class="dv-prose" dangerouslySetInnerHTML={{ __html: renderToHtml(oml) }} />

Parsing helpers

parseRichText and richTextExcerpt work on the raw stored string, applying the same plain-text fallback as RichTextReader:

import { parseRichText, richTextExcerpt } from '@davaux/rich-text'

const body = parseRichText(article.body) // OmlNode, ready for renderToHtml or omlToDoc
const preview = richTextExcerpt(article.body) // plain-text string, for feed previews or search indexing

richTextExcerpt is also a cheap way to check whether a stored body is empty, since it flattens to '' for an empty document.

Supported formatting

  • Blocks: paragraph, heading (h1–h6), blockquote, bullet list, numbered list, code block, horizontal rule, image
  • Marks: bold, italic, underline, strikethrough, inline code, link

Marks nest as regular OML elements (e.g. bold text is a <strong> element wrapping a #text node), so no OML schema changes are needed to store rich text alongside any other Davaux content.

Converters

docToOml and omlToDoc are exported directly if you need to work with the ProseMirror document or schema yourself:

import { docToOml, omlToDoc, schema } from '@davaux/rich-text'

const doc = omlToDoc(storedOml, schema)
const oml = docToOml(doc)