• CMSnpm version

    • Overview
    • ContentBuilder
    • InviteUserForm
    • LoginForm
    • RenewPasswordForm
  • UInpm version

    • Overview
    • Accordion
    • AlertBubble
    • AnnouncementBar
    • Avatar
    • AvatarFileInput
    • Badge
    • Button
    • ButtonGroup
    • ButtonList
    • Calendar
    • Checkbox
    • CheckboxButton
    • CheckboxInput
    • CheckboxList
    • Chip
    • ColorRadio
    • ColorRadioGroup
    • Combobox
    • DatePicker
    • DatePickerInput
    • DateRangePicker
    • DateRangePickerInput
    • DatetimePicker
    • DatetimePickerInput
    • Dialog
    • Dropdown
    • Dropzone
    • ErrorMessage
    • FileInput
    • FlashMessages
    • FormComponent
    • Icon
    • IconButton
    • ImageGallery
    • InfoBox
    • Input
    • Label
    • Layout
    • Lightbox
    • ListItem
    • Loader
    • Lozenge
    • Menu
    • Message
    • Modal
    • ✅ ModalDialog
    • ✅ ModalHeader
    • MultiCombobox
    • MultiSelect
    • Pagination
    • Paper
    • Popover
    • Radio
    • RadioGroup
    • RasterImage
    • Select
    • ✅ Tabs
    • TextInput
    • TextLink
    • Textarea
    • TimePicker
    • TimePickerInput
    • Toggle
    • Tooltip
    • Typography
  • Formnpm version

    • Overview
    • AvatarFileInput
    • CheckboxButton
    • CheckboxInput
    • CheckboxList
    • ColorRadioGroup
    • Combobox
    • DatePickerInput
    • DateRangePickerInput
    • DatetimePickerInput
    • Dropzone
    • FileInput
    • Form
    • FormRenderer
    • GpsInput
    • MoneyInput
    • MultiCombobox
    • MultiSelect
    • NumberInput
    • PasswordInput
    • RadioGroup
    • Select
    • TextInput
    • Textarea
    • TimePickerInput
    • Toggle
  • DataGridnpm version

    • Overview
    • DataGrid
    • DataGridCustomExample
    • ExportButton
    • FilterList
    • Filters
    • FiltersButton
    • FulltextInput
    • HiddenColumns
    • HiddenColumnsButton
    • Pagination
    • RowCounts
    • RowsPerPageSelect
    • SelectedRowsToolbar
    • TableV2
    • ToolbarControl
    • ToolbarCustoms
    • ToolbarTabs
  • Wysiwygnpm version

    • Overview
  • Resizernpm version

    • Overview
  • Routernpm version

    • Overview
  • Corenpm version

    • Overview
  • Core-Reactnpm version

    • Overview
  • Stylesnpm version

    • Overview
  • Localizenpm version

    • Overview
  • Analyticsnpm version

    • Overview
  • Datepickernpm version

    • Overview
  • Icons-generatornpm version

    • Overview
  • Smart-addressnpm version

    • Overview
  • E2Enpm version

    • Overview
  • E2E-Playwrightnpm version

    • Overview

@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

  • Plate documentation — the underlying editor framework.
  • Slate documentation — the underlying document model.
  • Package homepage: gitlab.com/uxf-npm/wysiwyg
Open in new tab
Open in new tab