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

@voilabs/mark

v0.0.5

Published

A modern React rich-text editor library by VoiLabs.

Readme

@voilabs/mark

A modern React rich text editor library built on top of Tiptap, Tailwind CSS, Radix UI, and TypeScript.

It is designed as a reusable package rather than a demo application, and ships a polished editing shell with a formatting toolbar, slash commands, media support, paragraph styles, theme switching, translations, and a Tailwind integration helper.

Table of Contents

Overview

@voilabs/mark is a library-first editor package that exposes a ready-to-use Editor component and several supporting exports for advanced integration scenarios.

The package focuses on:

  • rich text authoring with Tiptap
  • consistent, modern UI primitives using Radix UI
  • Tailwind-friendly styling and host-project theming
  • configurable typography and named paragraph styles
  • image and video insertion workflows
  • English and Turkish localization support
  • distribution as a typed library with multiple entry points

The editor accepts initial content as HTML or Markdown, can auto-detect the incoming format, and emits content updates as Markdown through onChange.

Quick Start

import { Editor } from "@voilabs/mark";

Basic Usage

import { useState } from "react";
import { Editor } from "@voilabs/mark";
import translations from "@voilabs/mark/translations/en-US";

export function ArticleEditor() {
    const [value, setValue] = useState("# Hello world");

    return (
        <Editor
            value={value}
            format="auto"
            theme="auto"
            translations={translations}
            onChange={setValue}
        />
    );
}

With Upload Support

import { Editor } from "@voilabs/mark";

async function uploadFile(file: File): Promise<{ url: string }> {
    const formData = new FormData();
    formData.append("file", file);

    const response = await fetch("/api/upload", {
        method: "POST",
        body: formData,
    });

    if (!response.ok) {
        throw new Error("Upload failed");
    }

    return response.json();
}

export function MediaEnabledEditor() {
    return (
        <Editor
            value="Start writing..."
            onUpload={uploadFile}
            onChange={(markdown) => {
                console.log(markdown);
            }}
        />
    );
}

With Custom Paragraph Styles

import {
    Editor,
    DEFAULT_PARAGRAPH_STYLES,
    type ParagraphStyle,
} from "@voilabs/mark";

const customStyles: ParagraphStyle[] = [
    ...DEFAULT_PARAGRAPH_STYLES,
    {
        id: "callout",
        label: "Callout",
        preview: "Callout",
        type: "blockquote",
        fontSize: "18px",
        fontWeight: "600",
        color: "#0f172a",
    },
];

export function StyledEditor() {
    return <Editor styles={customStyles} />;
}

With Translation Overrides

import { Editor } from "@voilabs/mark";
import trTR from "@voilabs/mark/translations/tr-TR";

export function TurkishEditor() {
    return (
        <Editor
            translations={{
                ...trTR,
                placeholder: "Yazmaya baslayin",
            }}
        />
    );
}

Highlights

Editing Experience

  • Toolbar with undo/redo, headings, lists, quote, code block, inline marks, text alignment, links, highlight colors, font family, and font size controls
  • Slash command menu triggered by / for quick insertion of text blocks, headings, lists, images, tables, quotes, and code blocks
  • Word count footer with zoom controls and fullscreen toggle
  • Global drag handle for block insertion, duplication, reordering, anchor-link copying, color actions, and deletion

Media Support

  • Custom image node implementation with resize handles, crop mode, rotation, object-fit frame mode, pan controls, upload state, and alt text editing
  • Video node support for uploaded video insertion
  • Drag-and-drop and paste image upload handling when an onUpload callback is provided
  • Slash-command image insertion with file picker support or URL prompt fallback

Styling and Customization

  • Tailwind-based UI with overridable class slots via EditorClassNames
  • Built-in light and dark themes with persisted theme preference in cookie/localStorage when theme="auto"
  • Named paragraph styles with a Word-like quick style gallery
  • Right-click style editing with local persistence in localStorage
  • Translation override support through translations

Packaging

  • Typed library output generated by Vite and vite-plugin-dts
  • ESM, UMD, and declaration outputs under dist

Prerequisites

Before using or developing this package, make sure you have:

  • Node.js 20 or newer recommended
  • npm 10 or newer recommended
  • a React application using React 19
  • TypeScript 5 if you want full type support

If you plan to use the Tailwind helper export, your host application should also be configured for Tailwind CSS.

Installation

Library Consumers

Install the package and its peer dependencies:

npm install @voilabs/mark react react-dom typescript

Tailwind Plugin Consumers

If you use the exported Tailwind plugin helper, also ensure your project has the typography plugin available:

npm install @tailwindcss/typography

Repository Setup

To work on the library itself:

bun install

Tailwind Integration

The editor UI is built with Tailwind utility classes. In host projects, the package export @voilabs/mark/plugin can help Tailwind discover the library's compiled class usage.

Example configuration:

import editorTailwindPlugin from "@voilabs/mark/plugin";

export default {
    plugins: [editorTailwindPlugin],
};

What the plugin does:

  • resolves the installed package path
  • points Tailwind content scanning to the package dist files
  • enables tailwindcss-animate
  • enables @tailwindcss/typography

API Reference

Editor

import { Editor } from "@voilabs/mark";

EditorProps

| Prop | Type | Description | | -------------------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------ | | value | string | Initial editor content. Supports HTML or Markdown depending on format. | | onChange | (value: string) => void | Receives Markdown output whenever the editor updates. | | format | "html" \| "markdown" \| "auto" | Controls how the initial value is interpreted. auto detects Markdown-like input. | | theme | "light" \| "dark" \| "auto" | Forces a theme or enables persisted user switching. | | onUpload | (file: File) => Promise<{ url: string }> | Upload handler used by toolbar uploads, slash-command image insertion, paste uploads, and drag/drop uploads. | | disableVideoUpload | boolean | Disables video upload actions in the toolbar. | | disableImageUpload | boolean | Disables image upload actions and image upload-driven insertion flows. | | classNames | EditorClassNames | Fine-grained class slot overrides for editor shell, toolbar, menus, footer, drag handle, and image UI. | | translations | Partial<EditorTranslations> | Partial translation override merged over built-in English strings. | | styles | ParagraphStyle[] | Replaces the default quick style set shown in the toolbar style gallery. |

ParagraphStyle

Each paragraph style can define:

  • id
  • label
  • preview
  • type
  • level
  • fontFamily
  • fontSize
  • fontWeight
  • fontStyle
  • color
  • letterSpacing
  • lineHeight
  • textTransform

EditorClassNames

EditorClassNames exposes slot-level styling hooks for nearly every visible part of the editor.

Shell and Content Slots

| Slot | Purpose | | ----------------- | ------------------------------ | | root | Outer editor wrapper | | content | Main scrollable content region | | contentScroller | Content scrolling container | | contentInner | Inner centered content wrapper | | editorContent | EditorContent host container | | prose | Tiptap prose typography layer |

Footer Slots

| Slot | Purpose | | ------------------------ | ------------------------------------ | | footer | Footer root container | | footerWordCount | Word count label area | | footerControls | Footer controls wrapper | | footerButton | Shared footer button styling | | footerButtonDisabled | Disabled footer button state | | footerZoomGroup | Zoom controls group | | footerZoomButton | Zoom increment and decrement buttons | | footerZoomValue | Zoom percentage button | | footerFullscreenButton | Fullscreen toggle button |

Toolbar Slots

| Slot | Purpose | | ----------------------- | -------------------------------- | | toolbar | Toolbar root container | | toolbarInner | Toolbar scroll container | | toolbarGroup | Toolbar button group wrapper | | toolbarButton | Shared toolbar button styling | | toolbarButtonActive | Active toolbar button state | | toolbarButtonDisabled | Disabled toolbar button state | | toolbarButtonIcon | Toolbar button icon wrapper | | toolbarTrigger | Dropdown trigger buttons | | toolbarTriggerActive | Active dropdown trigger state | | toolbarTriggerIcon | Dropdown trigger icon wrapper | | toolbarTriggerLabel | Dropdown trigger label text | | toolbarTriggerChevron | Dropdown trigger chevron | | toolbarSeparator | Separator between toolbar groups | | toolbarFileInput | Hidden media file input | | toolbarUploadButton | Upload button | | toolbarUploadIcon | Upload button icon | | toolbarUploadLabel | Upload button label |

Style Bar Slots

| Slot | Purpose | | -------------------- | ----------------------------- | | styleBar | Quick style gallery container | | styleBarItem | Individual style card | | styleBarItemActive | Active style card |

Style Editor Slots

| Slot | Purpose | | ---------------------- | ------------------------------------ | | styleEditPopover | Right-click style edit popover | | styleEditInput | Inputs inside the style editor | | styleEditSaveButton | Save button inside the style editor | | styleEditResetButton | Reset button inside the style editor |

Font Family Slots

| Slot | Purpose | | ----------------------------- | ---------------------------- | | toolbarFontFamilyTrigger | Font family combobox trigger | | toolbarFontFamilyPopover | Font family popover body | | toolbarFontFamilySearch | Font family search input | | toolbarFontFamilyList | Font family list container | | toolbarFontFamilyItem | Individual font option | | toolbarFontFamilyItemActive | Active font option |

Font Size Slots

| Slot | Purpose | | ------------------------------ | ------------------------------ | | toolbarFontSizeControl | Font size control wrapper | | toolbarFontSizeControlActive | Active font size control state | | toolbarFontSizeIcon | Font size icon wrapper | | toolbarFontSizeInput | Font size text input | | toolbarFontSizeUnit | Font size unit label | | toolbarFontSizeStepButton | Increase and decrease buttons | | toolbarFontSizeResetButton | Reset font size button |

Link Popover Slots

| Slot | Purpose | | ------------------------- | ---------------------------- | | toolbarLinkButton | Link toolbar button | | toolbarLinkPopoverBody | Link popover content wrapper | | toolbarLinkPopoverLabel | Link popover heading | | toolbarLinkInput | Link URL input | | toolbarLinkActions | Link action buttons wrapper | | toolbarLinkApplyButton | Apply link button | | toolbarLinkUnlinkButton | Remove link button |

Highlight Slots

| Slot | Purpose | | ------------------------------ | -------------------------------- | | toolbarHighlightButton | Highlight toolbar button | | toolbarHighlightIndicator | Color indicator under the button | | toolbarHighlightPopoverBody | Highlight palette popover body | | toolbarHighlightPopoverLabel | Highlight palette label | | toolbarHighlightGrid | Highlight swatch grid | | toolbarHighlightSwatch | Individual highlight swatch | | toolbarHighlightSwatchActive | Active highlight swatch | | toolbarHighlightClearButton | Clear highlight button |

Shared Overlay Slots

| Slot | Purpose | | --------------------------- | --------------------------------- | | tooltipContent | Tooltip content container | | tooltipArrow | Tooltip arrow | | popoverContent | Shared popover content container | | dropdownContent | Main dropdown menu content | | dropdownSubContent | Nested dropdown menu content | | dropdownSubTrigger | Nested dropdown trigger item | | dropdownSubTriggerChevron | Nested dropdown chevron | | dropdownItem | Standard dropdown item | | dropdownItemActive | Active dropdown item state | | dropdownItemIcon | Dropdown item icon | | dropdownItemLabel | Dropdown item label | | dropdownItemCheck | Dropdown selected-state checkmark | | dropdownCheckboxItem | Checkbox dropdown item | | dropdownRadioItem | Radio dropdown item | | dropdownIndicator | Dropdown indicator wrapper | | dropdownLabel | Dropdown section label | | dropdownSeparator | Dropdown separator | | dropdownShortcut | Dropdown shortcut hint text |

Slash Command Slots

| Slot | Purpose | | ------------------------ | ---------------------------- | | slashCommandMenu | Slash command menu container | | slashCommandItem | Slash command item | | slashCommandItemActive | Active slash command item | | slashCommandIcon | Slash command icon wrapper | | slashCommandTitle | Slash command item label | | slashCommandEmpty | Empty state wrapper | | slashCommandEmptyIcon | Empty state icon |

Drag Handle Slots

| Slot | Purpose | | ----------------------------- | ----------------------------------- | | dragHandleRoot | Floating drag handle root | | dragHandleAddButton | Add-block button | | dragHandleMenuButton | Grip/menu button | | dragHandleMenuContent | Drag handle dropdown content | | dragHandleSubContent | Nested drag handle dropdown content | | dragHandleColorSectionLabel | Text/background color section label | | dragHandleColorTextPreview | Text color preview glyph | | dragHandleColorSwatch | Background color swatch | | dragHandleCheckIcon | Selected-state check icon | | dragHandleDeleteItem | Delete item styling |

Image Node Slots

| Slot | Purpose | | ------------------------------ | ---------------------------- | | imageNodeWrapper | Outer image node wrapper | | imageContainer | Image selection frame | | imageCropArea | Crop area wrapper | | imageCropToolbar | Crop mode toolbar | | imageCropButton | Shared crop toolbar button | | imageCropCancelButton | Cancel crop button | | imageCropApplyButton | Apply crop button | | imageFrame | Image frame container | | imageElement | Rendered <img> element | | imageLoadingOverlay | Upload/loading overlay | | imageLoadingBox | Loader badge container | | imageLoadingIcon | Loader icon | | imagePanHintOverlay | Pan hint overlay | | imagePanHint | Pan hint text | | imageToolbar | Image hover toolbar | | imageToolbarButton | Shared image toolbar button | | imageToolbarSeparator | Divider inside image toolbar | | imageDeleteButton | Image delete button | | imageAltButton | Alt text button | | imageAltPopover | Alt text popover body | | imageAltLabel | Alt text popover label | | imageAltInput | Alt text input | | imageAltActions | Alt text action row | | imageAltApplyButton | Apply alt text button | | imageAltClearButton | Clear alt text button | | imageResizeHandle | Shared resize handle styling | | imageResizeHandleRight | Right resize handle | | imageResizeHandleBottom | Bottom resize handle | | imageResizeHandleBottomRight | Bottom-right resize handle | | imageModeBadge | Mode hint wrapper | | imageModeText | Mode hint label |

This API is intended for design system integration without forcing a fork of the editor internals.

Translations

The library ships with:

  • en-US
  • tr-TR

You can either import a full locale object or pass a partial object to override selected labels.

Development Workflow

This repository is configured as a library project, not as a local preview app.

Install Dependencies

bun install

Development Build Watch

bun run dev

This runs:

vite build --watch

Use this mode when you want incremental rebuilds of the library output while developing the package.

Production Build

bun run build

This runs:

vite build && tsc -p tsconfig.build.json

The command generates JavaScript bundles and declaration files into dist/.

Build and Distribution

The Vite configuration builds multiple library entry points:

  • src/index.ts
  • src/plugin.ts
  • src/translations/en-US.ts
  • src/translations/tr-TR.ts

The package metadata exposes:

  • main editor entry
  • Tailwind plugin entry
  • locale entries

Distributed assets include:

  • ESM bundles
  • UMD bundles
  • .d.ts type declarations

The package is configured to publish:

  • dist
  • README.md
  • LICENSE
  • package.json

Dependency Management

Peer Dependencies

The package declares the following peers:

  • react
  • react-dom
  • typescript

Runtime Dependencies

The runtime layer relies heavily on:

  • Tiptap extensions and editor packages
  • Radix UI primitives
  • Tailwind merge helpers
  • icon and motion libraries
  • media and popup utilities such as react-easy-crop and tippy.js

Development Dependencies

Development and build-time tooling includes:

  • Babel and Rollup-related packages
  • Vite and React integration tooling
  • TypeScript
  • declaration generation plugins

Lockfiles

The repository currently includes both package-lock.json and bun.lock, which suggests npm and Bun have both been used during development. This README uses Bun for repository workflows and npm for consumer installation examples.

Architecture

The codebase is organized as a reusable component library with a single primary editor shell and a set of supporting modules.

High-Level Design

  1. src/components/Editor.tsx creates the Tiptap editor instance and composes the full editor shell.
  2. src/components/editor/Toolbar.tsx provides the main authoring controls and style management UI.
  3. src/components/editor/GlobalDragHandleMenu.tsx adds block-level actions beside the editing surface.
  4. src/components/editor/extensions/* defines custom Tiptap extensions for images, video, font size, font family, and extra text-style attributes.
  5. src/components/editor/slash-command/* implements the / suggestion system.
  6. src/components/editor/EditorContext.tsx shares theme, translations, class names, and styles with nested UI primitives.
  7. src/plugin.ts exports a Tailwind helper plugin intended to help host apps include the editor's utility classes.

Content Flow

The editor follows this runtime flow:

  1. Initial value is read as HTML, Markdown, or auto-detected content.
  2. Tiptap extensions are registered, including the custom image/video and typography extensions.
  3. User actions update the internal ProseMirror document.
  4. onChange is called with editor.getMarkdown().

Important behavior:

  • Input supports HTML, Markdown, or auto-detection via the format prop.
  • Output is Markdown on every update, regardless of the original input format.
  • Auto theme mode persists the chosen theme using the VOILABS_REACT_EDITOR_THEME key.
  • Style overrides are stored locally using the voilabs-editor-style-overrides key.

Extensibility Points

  • onUpload for custom media upload pipelines
  • styles for custom paragraph style sets
  • classNames for granular visual overrides
  • translations for partial locale overrides
  • exported Tiptap extensions such as AdvancedImage, FontSize, and Video

Project Structure

editor/
|- src/
|  |- components/
|  |  |- Editor.tsx
|  |  |- editor/
|  |  |  |- EditorContext.tsx
|  |  |  |- Toolbar.tsx
|  |  |  |- StyleBar.tsx
|  |  |  |- StyleEditPopover.tsx
|  |  |  |- GlobalDragHandleMenu.tsx
|  |  |  |- classNames.ts
|  |  |  |- paragraphStyles.ts
|  |  |  |- translations.ts
|  |  |  |- useStyleStorage.ts
|  |  |  |- extensions/
|  |  |  |  |- AdvancedImage.tsx
|  |  |  |  |- ImageNodeView.tsx
|  |  |  |  |- FontFamily.ts
|  |  |  |  |- FontSize.ts
|  |  |  |  |- TextStyleAttributes.ts
|  |  |  |  `- Video.tsx
|  |  |  `- slash-command/
|  |  |     |- commands.ts
|  |  |     |- commands-list.tsx
|  |  |     `- suggestion.tsx
|  |  `- ui/
|  |     |- Dropdown.tsx
|  |     |- DropdownMenu.tsx
|  |     |- Popover.tsx
|  |     `- Tooltip.tsx
|  |- lib/
|  |  `- utils.ts
|  |- translations/
|  |  |- en-US.ts
|  |  `- tr-TR.ts
|  |- index.ts
|  `- plugin.ts
|- package.json
|- vite.config.ts
|- tsconfig.json
`- tsconfig.build.json

Tech Stack and Dependencies

Core Runtime

  • React 19
  • Tiptap 3 ecosystem
  • ProseMirror through @tiptap/pm
  • Tailwind CSS 4
  • Radix UI popover, dropdown menu, and tooltip primitives
  • lucide-react for editor icons
  • react-easy-crop for image cropping
  • tippy.js for slash command popup positioning

Build Tooling

  • Vite library mode
  • TypeScript 5
  • vite-plugin-dts for declaration generation
  • @vitejs/plugin-react
  • @tailwindcss/vite

Noteworthy Implementation Details

  • The editor uses a custom image node implementation instead of the default Tiptap image extension.
  • The Video extension renders native HTML <video> nodes with controls enabled.
  • The toolbar uses context-aware command selection so inline formatting can apply to the current word even when no explicit range is selected.
  • The drag handle menu performs block-level actions by resolving the top-level node under the cursor.
  • The Tailwind helper plugin tries to resolve the installed package path dynamically and scans the built dist files for content extraction.

Contributing

Contributions should preserve the package's library-first design, typed public API, and editor usability.

Recommended contribution process:

  1. Fork or branch from the latest mainline source.
  2. Install dependencies with npm install.
  3. Make focused changes with clear intent.
  4. Verify the package builds successfully with npm run build.
  5. Update documentation when behavior or public API changes.
  6. Open a pull request with a concise explanation of the change.

Suggested contribution guidelines:

  • keep public API changes intentional and documented
  • avoid breaking translation keys without updating shipped locales
  • preserve Markdown output behavior unless a deliberate API change is introduced
  • keep Tailwind class slot compatibility in mind when changing UI structure
  • prefer incremental, reviewable pull requests

At the moment, the repository does not define dedicated lint or test scripts in package.json, so build validation is the primary verification step.

License

This project is licensed under the MIT License.

See LICENSE for the full text.