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

chaeditor

v0.1.4

Published

Composable markdown editor toolkit for React applications.

Downloads

632

Readme

chaeditor

English | 한국어

Build composable markdown editors for React with authoring helpers, rich embeds, and overridable styling. chaeditor ships as one package. Use subpath imports like chaeditor/react and chaeditor/core to pull in only what you need.

Links

Guides

Recommended order:

If you are integrating for the first time, start with the Next.js guide. If uploads, image insertion, preview cards, or route wiring are part of the job, the wiki is more useful than the README alone. If you are using React but do not need a Next.js-specific walkthrough, open Introduction / Host Adapters in Storybook first.

Quick Start

Choose the integration path that matches your current scope.

Renderer only

Use this when you only need markdown display.

import 'chaeditor/styles.css';

import { MarkdownRenderer } from 'chaeditor/react';

const Example = async () => {
  return <MarkdownRenderer markdown="# Hello chaeditor" />;
};

Editor without host adapters

Use this to try out text input, toolbar actions, and preview before wiring up any upload logic.

'use client';

import { useState } from 'react';

import 'chaeditor/styles.css';

import { MarkdownEditor } from 'chaeditor/react';

const Example = () => {
  const [value, setValue] = useState('# Hello chaeditor');

  return <MarkdownEditor contentType="article" onChange={setValue} value={value} />;
};

Full integration

Use this when you need uploads, attachments, videos, and preview cards.

'use client';

import { useState } from 'react';

import 'chaeditor/styles.css';

import { createDefaultHostAdapters } from 'chaeditor/default-host';
import { MarkdownEditor } from 'chaeditor/react';

const adapters = createDefaultHostAdapters();

const Example = () => {
  const [value, setValue] = useState('');

  return (
    <MarkdownEditor adapters={adapters} contentType="article" onChange={setValue} value={value} />
  );
};

Then follow Integrating chaeditor in Next.js to create the expected host routes and verify the real app flow.

Host Adapter Checklist

chaeditor keeps upload logic, preview metadata, and framework-specific rendering out of the package — those belong to your app. This is what makes the package reusable across different products without modifications.

For most React apps, a solid editor integration covers these adapters:

| Adapter | Unlocks | If omitted | Typical decision | | ----------------------- | ------------------------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | | uploadImage | file-backed image insertion in editor flows | image upload UI can render, but file-backed insertion needs a host implementation | add this if users should upload images from the editor | | uploadFile | attachment upload | attachment UI can still appear, but upload-specific actions should stay disabled | add this if attachments are part of your content model | | uploadVideo | video file upload | video helpers can fall back to URL-based insertion | add this only when your product supports video uploads | | fetchLinkPreviewMeta | rich link preview cards | preview cards fall back to plain links | add this if OG-style previews matter | | resolveAttachmentHref | host-aware attachment URLs | renderer uses markdown href as-is | add this when stored files resolve through your app routing or CDN rules | | renderImage | framework-specific image primitive | package uses its default image renderer | optional, but recommended when your app standardizes on next/image or a branded media primitive | | imageViewerLabels | built-in viewer copy override | package uses built-in labels | optional label override only |

For most products, the following combination covers the baseline host integration:

  • uploadImage
  • uploadFile
  • uploadVideo if your product supports uploaded video
  • fetchLinkPreviewMeta
  • resolveAttachmentHref
  • renderImage if image rendering consistency matters in your app

A React-first adapter example:

import 'chaeditor/styles.css';

import { MarkdownEditor } from 'chaeditor/react';

const adapters = {
  uploadImage: async ({ contentType, file, imageKind }) => {
    return uploadImageToYourApi({ contentType, file, imageKind });
  },
  uploadFile: async ({ contentType, file }) => {
    return uploadFileToYourApi({ contentType, file });
  },
  uploadVideo: async ({ contentType, file, onProgress, signal }) => {
    return uploadVideoToYourApi({ contentType, file, onProgress, signal });
  },
  fetchLinkPreviewMeta: async (url, signal) => {
    return fetchPreviewMetaFromYourApi(url, signal);
  },
  resolveAttachmentHref: ({ href }) => href,
  renderImage: ({ alt, className, fill, sizes, src }) => (
    <img
      alt={alt}
      className={className}
      sizes={sizes}
      src={typeof src === 'string' ? src : src.src}
      style={fill ? { inset: 0, position: 'absolute' } : undefined}
    />
  ),
};

For a deeper look at the adapter contract, read Introduction / Host Adapters in Storybook.

Installation

Install chaeditor once, then import only the subpaths you need. For most app code, chaeditor/react and chaeditor/core are the right starting points. The root chaeditor entrypoint still works, but it mixes React and core exports into one surface — use the subpaths above instead.

npm install react react-dom chaeditor
pnpm add react react-dom chaeditor
yarn add react react-dom chaeditor
bun add react react-dom chaeditor

CSS choice

| Entry | Use when | Includes | | --------------------------- | ---------------------------------------------------------- | ------------------------------------------------------- | | chaeditor/styles.css | you want the safest default, or you render math | package styles, Panda output, KaTeX styles, KaTeX fonts | | chaeditor/styles-lite.css | you intentionally own KaTeX CSS, or you do not render math | package styles without KaTeX runtime styles |

Recommended default:

import 'chaeditor/styles.css';

Lighter option:

import 'chaeditor/styles-lite.css';

If you choose styles-lite.css and render math, also import:

import 'chaeditor/styles-lite.css';
import 'katex/dist/katex.min.css';

See Styling and CSS setup for a fuller decision guide and consumer-side checklists.

Package Surface

chaeditor is one npm package with selective subpath imports. You do not install subpaths separately.

| Import path | Provides | Use when | | ---------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------ | | chaeditor/react | MarkdownEditor, MarkdownToolbar, MarkdownRenderer, and React registries | most app integrations | | chaeditor/core | pure helpers, markdown contracts, and theme utilities | logic-only usage or server-safe helpers | | chaeditor/default-host | packaged upload and preview helpers | quick start, demos, or route-convention wiring | | chaeditor/panda-primitives | packaged Panda primitive shells | only when you want to reuse or wrap the default shells | | chaeditor/styles.css | full default CSS bundle | safest styling path | | chaeditor/styles-lite.css | lighter default CSS bundle | when your app owns KaTeX styling |

For the full breakdown, read Choosing import paths.

What To Read Next

Theme And Host Customization

chaeditor separates theme overrides from host-owned integration logic.

  • Use createChaeditorThemeVars() when you want to override semantic tokens such as primary, surface, text, or font stacks.
  • Use primitiveRegistry when you need to replace the actual Button, Input, Textarea, Popover, Modal, or Tooltip shells.
  • Use createDefaultHostAdapters() or custom adapters when uploads, href resolution, image rendering, or link preview metadata belong to your app layer.

Minimal theme override example:

import 'chaeditor/styles.css';

import { createChaeditorThemeVars } from 'chaeditor/core';
import { MarkdownEditor } from 'chaeditor/react';

const themeVars = createChaeditorThemeVars({
  primary: '#0f766e',
  primarySubtle: '#ccfbf1',
  surface: '#f8fafc',
  text: '#0f172a',
  sansFont: 'var(--app-font-sans), system-ui, sans-serif',
});

const Example = () => (
  <div style={themeVars}>
    <MarkdownEditor contentType="article" onChange={() => {}} value="" />
  </div>
);

The full primitive replacement walkthrough lives in Replacing default UI primitives.

Local Development

pnpm install
pnpm lint
pnpm check-types
pnpm test
pnpm build
pnpm run verify:package-surface

If a change is visual or touches docs-facing examples, also run:

pnpm storybook

Reporting Issues

Before opening a new issue, check whether the same problem is already reported. Bug reports are much easier to act on when they include:

  • package version
  • framework or runtime version
  • a clear reproduction path
  • error text or screenshots when relevant
  • whether the bug happens in the real app, Storybook, or only after packing

Contributing

If you want to contribute, start with CONTRIBUTING.md.