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

@vibeflui/editors

v1.1.0

Published

Rich-text (TipTap), code (CodeMirror), markdown (react-md-editor), and Editor.js (with full block-tool wiring) field adapters for VibeFlui / FluiKit.

Readme

@vibeflui/editors

Rich-text, code, markdown, and Editor.js field adapters for VibeFlui / FluiKit.

| Field type | Adapter key | Library | | --- | --- | --- | | richtext | richtext | TipTap (@tiptap/react, @tiptap/starter-kit, @tiptap/pm) | | code | code | CodeMirror (@uiw/react-codemirror) | | markdown | markdown | react-md-editor (@uiw/react-md-editor) | | json | editorjs | Editor.js (@editorjs/editorjs, plus any @editorjs/* block tool you wire with buildEditorJsTools — see the Editor.js section below) |

The editor libraries are optional peer dependencies — install only the ones you use. Core FluiKit never pulls them in. @vibeflui/editors itself never imports @editorjs/* tool packages (Header, List, Image, ...) either — those are wired from your own registry file, so your bundle only includes the tools you actually installed.

The included adapters follow the active light/dark theme: TipTap receives dark-aware container classes, CodeMirror switches between light and dark themes, react-md-editor receives the matching data-color-mode, and the Editor.js holder receives dark-aware container classes.

Install

# pick what you need
npm install @vibeflui/editors @uiw/react-codemirror
npm install @vibeflui/editors @tiptap/react @tiptap/starter-kit @tiptap/pm
npm install @vibeflui/editors @uiw/react-md-editor
npm install @vibeflui/editors @editorjs/editorjs

Usage

Register a per-editor plugin (tree-shakeable — only the imported editor's library ends up in your bundle):

import { FluiKit, FluiKitProvider } from "@vibeflui/core"
import { codePlugin } from "@vibeflui/editors"

<FluiKitProvider plugins={[codePlugin()]}>
  <FluiKit schema={schema} />
</FluiKitProvider>

Reference the adapter from schema:

form: {
  fields: [
    { name: "config", label: "Config", type: "code", adapter: "code" },
    { name: "body", label: "Body", type: "richtext", adapter: "richtext" },
    { name: "notes", label: "Notes", type: "markdown", adapter: "markdown" },
    { name: "content", label: "Content", type: "json", adapter: "editorjs" }
  ]
}

Use createEditorsPlugin() to register all four at once (requires all four libraries).

Editor.js

Editor.js is a block editor — its field value is Editor.js's own OutputData shape ({ time, blocks, version }), not a string, which is why it pairs with type: "json" rather than a dedicated field type.

npm install @vibeflui/editors @editorjs/editorjs
import { FluiKitProvider } from "@vibeflui/core"
import { editorJsPlugin } from "@vibeflui/editors"

<FluiKitProvider plugins={[editorJsPlugin()]}>
  <FluiKit schema={schema} />
</FluiKitProvider>

With no tools configured, Editor.js only renders its built-in tools: paragraph blocks, plus inline Bold/Italic/Link formatting. That's it — no Header, List, Quote, Table, Image, or anything else, since every other Editor.js block is a separate npm package. This is the "only text" behavior you get out of the box.

Enabling the full tool set

Header, List, Checklist, Quote, Warning, Table, Code, Embed, Image, Link preview, Raw HTML, Marker, Inline Code, and Underline are each a separate @editorjs/* package. Install the ones you want, import their classes in your own registry file, and wire them with buildEditorJsTools@vibeflui/editors itself never imports these packages, so your bundle only ever includes the tools you actually installed:

npm install @editorjs/header @editorjs/list @editorjs/checklist @editorjs/quote \
  @editorjs/warning @editorjs/delimiter @editorjs/table @editorjs/code \
  @editorjs/embed @editorjs/image @editorjs/link @editorjs/raw \
  @editorjs/marker @editorjs/inline-code @editorjs/underline

lib/vibeflui/editorjs-tools.ts

import Header from "@editorjs/header"
import EditorjsList from "@editorjs/list"
import Checklist from "@editorjs/checklist"
import Quote from "@editorjs/quote"
import Warning from "@editorjs/warning"
import Delimiter from "@editorjs/delimiter"
import EditorjsTable from "@editorjs/table"
import CodeTool from "@editorjs/code"
import Embed from "@editorjs/embed"
import ImageTool from "@editorjs/image"
import LinkTool from "@editorjs/link"
import RawTool from "@editorjs/raw"
import Marker from "@editorjs/marker"
import InlineCode from "@editorjs/inline-code"
import Underline from "@editorjs/underline"
import { buildEditorJsTools } from "@vibeflui/editors"

export const editorJsTools = buildEditorJsTools(
  {
    header: Header,
    list: EditorjsList,
    checklist: Checklist,
    quote: Quote,
    warning: Warning,
    delimiter: Delimiter,
    table: EditorjsTable,
    code: CodeTool,
    embed: Embed,
    image: ImageTool,
    linkTool: LinkTool,
    raw: RawTool,
    marker: Marker,
    inlineCode: InlineCode,
    underline: Underline
  },
  {
    image: { endpointByFile: "/api/editorjs/upload-image" },
    link: { endpoint: "/api/editorjs/link-preview" }
  }
)

lib/vibeflui/registry.ts

import { editorJsPlugin } from "@vibeflui/editors"
import { editorJsTools } from "./editorjs-tools"

export const editorJs = editorJsPlugin({ tools: editorJsTools })

buildEditorJsTools only registers the tools whose class you actually passed in — skip an import to leave that tool out. It also applies sensible defaults per tool (inline toolbar enabled where it makes sense, Embed's common service list, Header's levels) so you don't have to know each tool's raw config shape.

Only pass an image/link endpoint once you have a server route for it (see below) — buildEditorJsTools skips registering the Image / Link Preview tools entirely when no endpoint is configured, instead of registering a tool that would fail on first use.

Narrowing tools per schema field

field.adapterProps (a plain FluiKitFieldConfig key, JSON-friendly) can restrict which of the app-registered tools one specific field enables, or override its image/link endpoint, without a second plugin registration:

form: {
  fields: [
    {
      name: "body",
      label: "Body",
      type: "json",
      adapter: "editorjs",
      adapterProps: {
        tools: ["header", "list", "quote", "linkTool"]
      }
    },
    {
      name: "avatarBio",
      label: "Bio",
      type: "json",
      adapter: "editorjs",
      adapterProps: {
        tools: ["image"],
        image: { endpointByFile: "/api/editorjs/upload-avatar" }
      }
    }
  ]
}

Image upload and link preview endpoints

The Image and Link Preview tools need a server route — VibeFlui is FE/UI-only and doesn't provide one, but the contract each tool expects is fixed by Editor.js itself:

  • Image (endpointByFile): receives a multipart/form-data POST with the file under the field name ("file" by default). Must respond with { "success": 1, "file": { "url": "https://..." } }.
  • Link preview (endpoint): receives a GET request with the target URL as a ?url= query param. Must respond with { "success": 1, "link": "https://...", "meta": { "title": "...", "description": "...", "image": { "url": "https://..." } } } (fetch the page server-side and parse its Open Graph tags to build meta).

Using Editor.js outside a form

EditorJsEditor is a plain controlled component — it doesn't need <FluiKit>, <FluiKitForm>, or a registry. Use it anywhere in a React tree, e.g. a standalone content editor page:

"use client"

import { useState } from "react"
import { EditorJsEditor, type EditorJsOutputData } from "@vibeflui/editors"
import { editorJsTools } from "@/lib/vibeflui/editorjs-tools"

export function ArticleBodyEditor() {
  const [value, setValue] = useState<EditorJsOutputData>();

  return <EditorJsEditor value={value} onChange={setValue} tools={editorJsTools} />
}

Pass tools={buildEditorJsTools({...})} the same way you would for the form field adapter (see above) — omit tools and you get Editor.js's built-in paragraph/bold/italic/inline-link only.

Stylesheets

Import the stylesheets your chosen editors need, once in your app:

import "@uiw/react-md-editor/markdown-editor.css" // markdown
// CodeMirror ships its own styles; TipTap is headless — style the `.ProseMirror` content yourself.
// Editor.js is headless too — style `.editorjs-holder` (the wrapper div) yourself.