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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@reeverdev/editor

v1.0.2

Published

Reever Editor - Modern, extensible rich text editor framework

Downloads

149

Readme

Reever Editor

A headless, extensible rich text editor framework built on ProseMirror. Inspired by Tiptap, designed for flexibility.

Features

  • Headless Architecture - Full control over UI, bring your own components
  • Extensible - Modular extension system for nodes, marks, and plugins
  • Framework Agnostic - Core is vanilla JS, with first-class React bindings
  • TypeScript First - Written in TypeScript with full type coverage
  • ProseMirror Foundation - Built on battle-tested ProseMirror

Installation

# npm
npm install @reeverdev/editor

# pnpm
pnpm add @reeverdev/editor

# yarn
yarn add @reeverdev/editor

Quick Start

Vanilla JavaScript

import { Editor } from '@reeverdev/editor';
import { StarterKit } from '@reeverdev/editor/starter-kit';

const editor = new Editor({
  element: document.querySelector('#editor'),
  extensions: [StarterKit],
  content: '<p>Hello World!</p>',
});

// Use commands
editor.chain().focus().toggleBold().run();

// Get content
const html = editor.getHTML();
const json = editor.getJSON();

React

import { useEditor, EditorContent } from '@reeverdev/editor/react';
import { StarterKit } from '@reeverdev/editor/starter-kit';

function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
    content: '<p>Hello World!</p>',
  });

  return <EditorContent editor={editor} />;
}

Packages

| Package | Description | |---------|-------------| | @reeverdev/core | Core editor, extension system, commands | | @reeverdev/react | React hooks and components | | @reeverdev/starter-kit | Pre-configured bundle of essential extensions | | @reeverdev/pm | ProseMirror re-exports |

Extensions

| Extension | Description | |-----------|-------------| | @reeverdev/extension-bold | Bold text formatting | | @reeverdev/extension-italic | Italic text formatting | | @reeverdev/extension-underline | Underline text formatting | | @reeverdev/extension-strike | Strikethrough text | | @reeverdev/extension-code | Inline code | | @reeverdev/extension-code-block | Code blocks | | @reeverdev/extension-heading | Headings (h1-h6) | | @reeverdev/extension-paragraph | Paragraphs | | @reeverdev/extension-blockquote | Block quotes | | @reeverdev/extension-bullet-list | Bullet lists | | @reeverdev/extension-ordered-list | Numbered lists | | @reeverdev/extension-task-list | Task/checkbox lists | | @reeverdev/extension-task-item | Task list items | | @reeverdev/extension-link | Hyperlinks | | @reeverdev/extension-image | Images with resize support | | @reeverdev/extension-highlight | Text highlighting | | @reeverdev/extension-color | Text color | | @reeverdev/extension-text-style | Text styling base | | @reeverdev/extension-history | Undo/redo | | @reeverdev/extension-placeholder | Placeholder text | | @reeverdev/extension-bubble-menu | Selection-based floating menu | | @reeverdev/extension-floating-menu | Empty line floating menu | | @reeverdev/extension-horizontal-rule | Horizontal dividers | | @reeverdev/extension-hard-break | Line breaks | | @reeverdev/extension-subscript | Subscript text | | @reeverdev/extension-superscript | Superscript text |

Creating Custom Extensions

Basic Extension

import { Extension } from '@reeverdev/core';

const MyExtension = Extension.create({
  name: 'myExtension',

  addCommands() {
    return {
      myCommand: () => ({ commands }) => {
        // Your command logic
        return true;
      },
    };
  },

  addKeyboardShortcuts() {
    return {
      'Mod-Shift-x': () => this.editor.commands.myCommand(),
    };
  },
});

Custom Node

import { Node } from '@reeverdev/core';

const CustomNode = Node.create({
  name: 'customNode',
  group: 'block',
  content: 'inline*',

  parseHTML() {
    return [{ tag: 'div[data-custom]' }];
  },

  renderHTML({ HTMLAttributes }) {
    return ['div', { 'data-custom': '', ...HTMLAttributes }, 0];
  },
});

Custom Mark

import { Mark } from '@reeverdev/core';

const CustomMark = Mark.create({
  name: 'customMark',

  parseHTML() {
    return [{ tag: 'span[data-custom-mark]' }];
  },

  renderHTML({ HTMLAttributes }) {
    return ['span', { 'data-custom-mark': '', ...HTMLAttributes }, 0];
  },

  addCommands() {
    return {
      toggleCustomMark: () => ({ commands }) => {
        return commands.toggleMark(this.name);
      },
    };
  },
});

Configuration

StarterKit Options

import { StarterKit } from '@reeverdev/editor/starter-kit';

const editor = new Editor({
  extensions: [
    StarterKit.configure({
      // Disable specific extensions
      history: false,
      codeBlock: false,

      // Configure extensions
      heading: {
        levels: [1, 2, 3],
      },
      link: {
        validate: (href) => href.startsWith('https://'),
      },
      image: {
        allowResize: true,
      },
    }),
  ],
});

Editor Events

const editor = new Editor({
  extensions: [StarterKit],
  onUpdate: ({ editor }) => {
    console.log('Content updated:', editor.getHTML());
  },
  onSelectionUpdate: ({ editor }) => {
    console.log('Selection changed');
  },
  onCreate: ({ editor }) => {
    console.log('Editor created');
  },
  onDestroy: () => {
    console.log('Editor destroyed');
  },
  onFocus: ({ editor }) => {
    console.log('Editor focused');
  },
  onBlur: ({ editor }) => {
    console.log('Editor blurred');
  },
});

React Components

BubbleMenu

A floating menu that appears when text is selected.

import { BubbleMenu } from '@reeverdev/editor/react';

function MyEditor() {
  const editor = useEditor({ extensions: [StarterKit] });

  return (
    <>
      <EditorContent editor={editor} />
      <BubbleMenu editor={editor}>
        <button onClick={() => editor.chain().focus().toggleBold().run()}>
          Bold
        </button>
        <button onClick={() => editor.chain().focus().toggleItalic().run()}>
          Italic
        </button>
      </BubbleMenu>
    </>
  );
}

FloatingMenu

A menu that appears on empty lines.

import { FloatingMenu } from '@reeverdev/editor/react';

function MyEditor() {
  const editor = useEditor({ extensions: [StarterKit] });

  return (
    <>
      <EditorContent editor={editor} />
      <FloatingMenu editor={editor}>
        <button onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}>
          H1
        </button>
        <button onClick={() => editor.chain().focus().toggleBulletList().run()}>
          List
        </button>
      </FloatingMenu>
    </>
  );
}

Commands

Commands are chainable and provide a fluent API:

// Chain multiple commands
editor
  .chain()
  .focus()
  .selectAll()
  .toggleBold()
  .run();

// Check if command can execute
if (editor.can().toggleBold()) {
  editor.commands.toggleBold();
}

// Available commands (varies by extensions)
editor.commands.setBold();
editor.commands.unsetBold();
editor.commands.toggleBold();
editor.commands.setItalic();
editor.commands.toggleHeading({ level: 1 });
editor.commands.setLink({ href: 'https://example.com' });
editor.commands.insertContent('<p>New content</p>');
editor.commands.setContent('<p>Replace all</p>');
editor.commands.clearContent();
editor.commands.undo();
editor.commands.redo();

Working with Content

// Get content
const html = editor.getHTML();
const json = editor.getJSON();
const text = editor.getText();

// Set content
editor.commands.setContent('<p>New content</p>');
editor.commands.setContent({ type: 'doc', content: [...] });

// Insert content
editor.commands.insertContent('<p>Inserted</p>');
editor.commands.insertContentAt(10, '<p>At position</p>');

// Clear content
editor.commands.clearContent();

Development

# Install dependencies
pnpm install

# Run development server
pnpm dev

# Build all packages
pnpm build

# Run tests
pnpm test

# Lint
pnpm lint

Project Structure

packages/
├── core/                    # Core editor
├── react/                   # React bindings
├── starter-kit/             # Pre-configured extensions
├── pm/                      # ProseMirror re-exports
├── extension-bold/          # Bold extension
├── extension-italic/        # Italic extension
├── extension-*/             # Other extensions
examples/
└── react-basic/             # React example app

License

MIT

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting a PR.

Links