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

wysiwyg-markdown-editor

v0.1.1

Published

Markdown-first WYSIWYG React editor with WYSIWYG/Markdown/HTML view modes

Readme

wysiwyg-markdown-editor

A production-quality, Markdown-first WYSIWYG editor for React. Markdown is the canonical storage format — every edit round-trips cleanly through Markdown.

Features

  • Three view modes: WYSIWYG (rich text), Markdown source, and HTML output
  • Strictly Markdown-compatible formatting: bold, italic, strikethrough, headings, lists, blockquotes, code blocks, links, images, horizontal rules
  • Controlled & uncontrolled API modes
  • Image upload callback
  • HTML sanitization (XSS-safe by default via sanitize-html)
  • Clean, accessible toolbar with keyboard shortcut hints
  • Full TypeScript types
  • ESM + CJS builds, separate CSS file

Installation

npm install wysiwyg-markdown-editor

Peer dependencies (must be installed separately):

npm install react react-dom

Quick Start

import { useState } from 'react'
import { MarkdownMailerEditor } from 'wysiwyg-markdown-editor'
import 'wysiwyg-markdown-editor/dist/index.css'

function App() {
  const [markdown, setMarkdown] = useState('# Hello world')

  return (
    <MarkdownMailerEditor
      valueMarkdown={markdown}
      onChangeMarkdown={setMarkdown}
    />
  )
}

Props

| Prop | Type | Default | Description | |---|---|---|---| | valueMarkdown | string | — | Controlled mode: the current Markdown value | | defaultMarkdown | string | '' | Uncontrolled mode: the initial Markdown value | | onChangeMarkdown | (md: string) => void | required | Called on every content change | | onChangeHtml | (html: string) => void | — | Also receive the sanitized HTML on change | | placeholder | string | — | Placeholder shown in empty editor | | readOnly | boolean | false | Disables editing | | className | string | — | Extra class on the root element | | toolbar | 'basic' \| 'full' | 'full' | 'basic' shows fewer toolbar options | | viewMode | 'wysiwyg' \| 'markdown' \| 'html' | — | Controlled view mode | | onViewModeChange | (mode: ViewMode) => void | — | Called when user switches view | | sanitizeHtmlOutput | boolean | true | Sanitize HTML output via sanitize-html | | sanitizerConfig | SanitizerConfig | — | Custom sanitizer config (merged with defaults) | | onImageUpload | (file: File) => Promise<{ url: string; alt?: string }> | — | Image upload handler |

View Modes

WYSIWYG (default)

Full rich-text editing experience with the toolbar. All formatting is constrained to what Markdown supports.

Markdown

Editable monospace textarea showing the raw Markdown. Changes here sync back to the WYSIWYG editor.

HTML

Read-only view of the sanitized HTML output with a copy button.

Image Upload

async function handleImageUpload(file: File) {
  const formData = new FormData()
  formData.append('image', file)

  const res = await fetch('/api/upload', { method: 'POST', body: formData })
  const { url } = await res.json()

  return { url, alt: file.name }
}

<MarkdownMailerEditor
  onChangeMarkdown={setMarkdown}
  onImageUpload={handleImageUpload}
/>

Utility Exports

The package also exports the underlying utility functions:

import { markdownToHtml, htmlToMarkdown, sanitizeHtml } from 'wysiwyg-markdown-editor'

// Convert Markdown to HTML (with XSS sanitization by default)
const html = markdownToHtml('**bold** and _italic_')

// Convert HTML to Markdown (best-effort, via turndown)
const md = htmlToMarkdown('<strong>bold</strong>')

// Sanitize HTML with default allowed tags
const safe = sanitizeHtml('<script>alert(1)</script><p>safe</p>')

Styling

Import the CSS once in your app entry point:

import 'wysiwyg-markdown-editor/dist/index.css'

All styles use the .wmme- prefix and CSS custom properties for easy theming:

.wmme-root {
  --wmme-border: #e0e0e0;
  --wmme-active-bg: #e8f0fe;
  --wmme-active-color: #1a73e8;
  --wmme-hover-bg: #f5f5f5;
}

Architecture

| Layer | Technology | |---|---| | Rich editor | TipTap v2 (ProseMirror) | | MD → editor | markdown-it → HTML → editor.commands.setContent() | | Editor → MD | Custom JSON walker (serializer.ts) | | MD → HTML display | markdown-it + sanitize-html | | HTML → MD import | turndown | | Build | tsup (ESM + CJS + types + CSS) | | Tests | vitest |

Markdown is the canonical format. The editor never stores HTML or ProseMirror JSON as the source of truth — only Markdown.

Development

# Install dependencies
npm install

# Run tests
npm test

# Watch mode
npm run test:watch

# Build library
npm run build

# Run demo
cd demo && npm install && npm run dev

Releasing

This package uses release-it with Keep a Changelog format.

Prerequisites (one-time)

# Authenticate with npm
npm login

# Set a GitHub personal access token (repo scope) for GitHub Releases
export GITHUB_TOKEN=ghp_...

Workflow

1. Document your changes in CHANGELOG.md under ## [Unreleased]:

## [Unreleased]

### Added
- Support for tables

### Fixed
- Bold escaping in nested lists

2. Preview the release (no side effects):

npm run release:dry

3. Publish:

npm run release
# Prompts for: patch / minor / major
# Then automatically:
#   → runs tests + build
#   → moves [Unreleased] → [x.y.z] in CHANGELOG.md
#   → git commit + tag vx.y.z
#   → git push --follow-tags
#   → creates GitHub Release
#   → npm publish

Versioning rules (semver)

| Change | Version bump | Example | |---|---|---| | Bug fixes, doc updates | patch | 0.1.00.1.1 | | New features, new props | minor | 0.1.00.2.0 | | Breaking API changes | major | 0.1.01.0.0 |

License

MIT