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

@uxf/wysiwyg

v11.122.5

Published

UXF Wysiwyg editor

Downloads

1,208

Readme

@uxf/wysiwyg

Rich-text / WYSIWYG editor for React, built on Plate / Slate. It ships a ready-made WysiwygEditor component, a toolbar, and a set of UXF-styled plugins (headings, marks, lists, links, images, videos, buttons, blockquote, highlight).

When to use

Reach for @uxf/wysiwyg when you need a structured rich-text editor whose value is a JSON document (a Slate/Plate node tree), not an HTML string. The editor is uncontrolled: it is seeded once from initialValue and reports every edit through onChange as a WysiwygContent array.

It is a client component only and depends on @uxf/ui for its visual primitives. For a higher-level CMS content-builder wrapper, see @uxf/cms; use this package directly when you want the standalone editor.

Installation

yarn add @uxf/wysiwyg

All Plate/Slate packages and the UXF packages are peer dependencies with pinned versions — install them at the versions declared in package.json:

  • UXF: @uxf/core, @uxf/core-react, @uxf/ui
  • Plate (20.7.2): @udecode/plate-basic-marks, @udecode/plate-block-quote, @udecode/plate-break, @udecode/plate-code-block, @udecode/plate-common, @udecode/plate-core, @udecode/plate-floating, @udecode/plate-link, @udecode/plate-list, @udecode/plate-media, @udecode/plate-node-id, @udecode/plate-paragraph, @udecode/plate-reset-node, @udecode/plate-select, @udecode/plate-trailing-block
  • Slate wrappers: @udecode/slate (19.8.0), @udecode/slate-react (19.7.1), @udecode/slate-utils (19.7.1)
  • Slate core: slate (0.90.0), slate-history (0.86.0), slate-hyperscript (0.77.0), slate-react (0.91.11)
  • react / react-dom (>=18.2.0)

Required CSS

Import the published stylesheet once (e.g. in your global CSS):

@import url("@uxf/wysiwyg/styles/styles.css");

This stylesheet uses Tailwind's theme(), @apply (including uxf-typo-* utilities from @uxf/ui) and CSS nesting, so it must be processed by your app's Tailwind/PostCSS pipeline alongside the @uxf/ui Tailwind config.

Required translations

The editor renders its toolbar/labels through useUxfTranslation (@uxf/core-react/translations). Register this package's translations, or you will see raw translation keys instead of text. Merge the default export of @uxf/wysiwyg/translations/translations (locales: cs, en, sk, de) into the translation function you pass to your app's provider:

import wysiwygTranslations from "@uxf/wysiwyg/translations/translations";
import uiTranslations from "@uxf/ui/translations/translations";
import { createDevT } from "@uxf/core-react/translations/create-dev-t";

export const tFunction = createDevT("cs", {
    ...uiTranslations,
    ...wysiwygTranslations,
});

Wire tFunction through the standard UXF setup (translationFn on UiContextProvider from @uxf/ui/context, or TranslationsProvider from @uxf/core-react/translations).

Quick start

Build the plugin set once, then render WysiwygEditor. createAllPluginsWithUi enables every plugin; its only required option is image (image insertion needs uploadImage + getImageUrl).

"use client";

import { createAllPluginsWithUi, WysiwygContent, WysiwygEditor } from "@uxf/wysiwyg";
import { useState } from "react";

const plugins = createAllPluginsWithUi({
    image: {
        uploadImage: async (file) => uploadToS3(file), // returns FileResponse
        getImageUrl: (file) => resolveUrl(file),
    },
});

const INITIAL_VALUE: WysiwygContent = [{ type: "paragraph", id: "p1", children: [{ text: "Hello" }] }];

export function MyEditor() {
    const [value, setValue] = useState<WysiwygContent>(INITIAL_VALUE);

    return <WysiwygEditor id="my-editor" initialValue={value} onChange={setValue} plugins={plugins} />;
}

To enable only some plugins, compose them yourself with createPluginsWithUi:

import { createBoldPluginWithUi, createHeadingsPluginWithUi, createPluginsWithUi } from "@uxf/wysiwyg";

const plugins = createPluginsWithUi([
    createHeadingsPluginWithUi({ disabledLevels: [1] }),
    createBoldPluginWithUi(),
]);

Content model

WysiwygContent is a WysiwygRootBlock[] — a Plate/Slate document, not HTML. Each block carries a type, an optional id, and children:

type WysiwygRootBlock =
    | UxfParagraphElement // type: "paragraph"
    | UxfHeadingElement // type: "h1" | "h2" | "h3" | "h4" | "h5" | "h6"
    | UxfBlockQuoteElement // type: "blockquote"
    | UxfUnorderedListElement // type: "ul"  (li > lic)
    | UxfOrderedListElement // type: "ol"  (li > lic)
    | UxfLinkElement // type: "link"  (inline)
    | UxfImageElement // type: "image" (void)
    | UxfVideoElement // type: "video" (void)
    | UxfButtonElement; // type: "button" (void)

Inline text (RichText) carries the mark flags bold, italic, underline, code, highlight. onChange always receives the full, current document.

API

Everything below is exported from the package root (@uxf/wysiwyg) unless a deep path is shown. There is no exports map, so deep imports resolve by filesystem.

WysiwygEditor

import { WysiwygEditor } from "@uxf/wysiwyg";

| Prop | Type | Required | Description | | --- | --- | --- | --- | | id | string | yes | Unique Plate instance id. Must be unique per editor on the page. | | initialValue | WysiwygContent \| undefined | yes | Document the editor is seeded with on mount. | | onChange | (value: WysiwygContent) => void | yes | Called on every edit with the full document. | | plugins | UxfPlatePlugin[] | yes | Build with createAllPluginsWithUi / createPluginsWithUi. | | className | string | no | Class on the editor root wrapper. | | editableProps | TEditableProps<WysiwygContent> | no | Passed to Plate's editable area (placeholder, readOnly, autoFocus, spellCheck, className, …). Defaults: autoFocus: false, readOnly: false, spellCheck: false, localized placeholder. | | customPluginsToolbarButtons | ReactNode | no | Extra toolbar buttons appended to the built-in ones. | | toolbarLeftElement | ReactNode | no | Node rendered at the toolbar's left edge. | | toolbarRightElement | ReactNode | no | Node rendered at the toolbar's right edge. |

Plugin builders

createPluginsWithUi / createAllPluginsWithUi return the UxfPlatePlugin[] you pass to WysiwygEditor. Both inject the base plugins automatically (paragraph, exit-break, reset-node, soft-break, node-id, select-on-backspace, trailing-block), so pass only the feature builders below — not raw Plate plugins.

| Export | Signature | Notes | | --- | --- | --- | | createPluginsWithUi | (plugins: Array<WysiwygPlugin \| WysiwygRecursivePlugin<string>>, options?: { overrideByKey?: MyOverrideByKey }) => UxfPlatePlugin[] | Compose a custom subset. | | createAllPluginsWithUi | (options: CreateAllPluginsOptions) => UxfPlatePlugin[] | Enable every plugin. | | createHeadingsPluginWithUi | (options?: HeadingsPluginOptions) => WysiwygRecursivePlugin | disabledLevels?: (1..6)[]. | | createBoldPluginWithUi | () => WysiwygPlugin | | | createItalicPluginWithUi | () => WysiwygPlugin | | | createUnderlinePluginWithUi | () => WysiwygPlugin | | | createCodePluginWithUi | () => WysiwygPlugin | | | createHighlightPluginWithUi | (color?: CSSProperties["color"]) => WysiwygPlugin | Defaults to twColors.yellow[300]. | | createBlockquotePluginWithUi | () => WysiwygPlugin | | | createListPluginWithUi | () => WysiwygRecursivePlugin | Renders ul / ol / li. | | createLinkPluginWithUi | () => WysiwygPlugin | | | createImagePluginWithUi | (options: UxfImagePluginOptions) => WysiwygPlugin | See options below. | | createVideoPluginWithUi | () => WysiwygPlugin | | | createButtonPluginWithUi | () => WysiwygPlugin | |

CreateAllPluginsOptions:

| Key | Type | Required | Description | | --- | --- | --- | --- | | image | UxfImagePluginOptions | yes | Image plugin options (pass {} to enable without upload handlers). | | headings | HeadingsPluginOptions | no | e.g. { disabledLevels: [1] }. | | highlightColor | CSSProperties["color"] | no | Highlight mark color. |

UxfImagePluginOptions (extends Plate's MediaPlugin):

| Key | Type | Description | | --- | --- | --- | | uploadImage | (file: File) => Promise<FileResponse> | Upload handler; returns a FileResponse (@uxf/core/types). | | getImageUrl | (file: FileResponse) => string | Resolves the display URL for an uploaded file. | | disableUploadOnPasteImageUrl | boolean | Disable auto-upload when an image URL is pasted. | | disableUploadOnPasteImage | boolean | Disable auto-upload when an image blob is pasted. |

Hooks

import { ... } from "@uxf/wysiwyg"; — thin, typed wrappers over Plate's editor hooks, bound to WysiwygContent / UxfEditor:

useUxfPlateEditorRef, useUxfEditorRef, useUxfEditorState, useUxfPlateEditorState, useUxfPlateSelectors, useUxfPlateActions, useUxfPlateStates, and a re-export of useSelected (from slate-react).

Utilities

Editor helpers for building custom plugins/toolbar buttons: getUxfEditor, getPluginOptions, getPluginType, someNode, toggleNodeType, focusEditor, isRangeInSingleText, isMarkActive, toggleMark, isPluginEnabled, isSomeOfPluginsEnabled, getSelectedNode, getActiveElement, removeElement, insertVoid, removeSelectedNode.

Serialize a document to plain text (deep import, not in the root barrel):

import { serializeToPlaintext } from "@uxf/wysiwyg/serializers/serialize-to-plaintext";

serializeToPlaintext(value); // keepIndentation defaults to true (joins blocks with "\n")

Types

The full node model and editor types are re-exported, including: WysiwygContent, WysiwygRootBlock, RichText, the element interfaces (UxfParagraphElement, UxfHeadingElement, UxfBlockQuoteElement, UxfLinkElement, UxfImageElement, UxfVideoElement, UxfButtonElement, UxfUnorderedListElement, UxfOrderedListElement, LiElement, LicElement), plus UxfEditor, UxfPlatePlugin, WysiwygPlugin, WysiwygRecursivePlugin, and the render prop/component types (RenderElementProps, ElementUiComponent, RenderLeafProps, LeafUiComponent, RenderAfterEditable, MyOverrideByKey, UiComponents).

Gotchas

  • Client component only. WysiwygEditor is marked "use client" and renders a Loader until it has mounted on the client; it cannot render on the server.
  • Uncontrolled value. initialValue seeds the document once on mount. Changing initialValue later does not reset the editor — remount it (e.g. via a changed React key) to load a different document. Read the live value from onChange.
  • Build plugins with the provided factories. Pass only the results of createPluginsWithUi / createAllPluginsWithUi to plugins; they inject the required base plugins. Do not hand-assemble raw Plate plugin arrays.
  • CSS is required and Tailwind-dependent. Without @uxf/wysiwyg/styles/styles.css the editor is unstyled; the file relies on your Tailwind/PostCSS setup and @uxf/ui's uxf-typo-* utilities.
  • Translations are required. Register @uxf/wysiwyg/translations/translations, otherwise labels/tooltips render as raw keys.
  • Pinned peers. Plate/Slate and @uxf/* peer versions are pinned exactly (see package.json); mismatched versions will not work.

Links