Rich Text WYSIWYG Editor for React & Vanilla JS – Editium

Rich Text WYSIWYG Editor for React & Vanilla JS – Editium
Rich Text WYSIWYG Editor for React & Vanilla JS – Editium
Editium is a lightweight WYSIWYG editor that supports both React and Vanilla JavaScript with a shared API. It provides a customizable toolbar and exports to HTML or JSON formats.

Features:

  • Rich text formatting: Bold, italic, underline, strikethrough, inline code, superscript, subscript, and more.
  • Eight heading levels: Supports heading-one through heading-eight in the React version, and heading-one through heading-six in the Vanilla version.
  • Block elements: Includes blockquote, code block, and paragraph formats.
  • Table editing: Supports dynamic row and column insertion directly in the editor.
  • Resizable images: Accepts a custom upload handler that returns a URL, then renders images as resizable elements in the content area.
  • Nested lists: Supports bulleted and numbered lists with indent and outdent controls.
  • Text and background colors: Ships built-in color pickers for both font color and highlight color.
  • Find and replace: Highlights search matches in the editor with navigation between occurrences.
  • Multi-format export: Returns content as HTML, JSON, or plain text via getHTML(), getJSON(), and getText().
  • Word and character count: Optional display rendered below the editor area.
  • Read-only mode: Renders content for display purposes with the toolbar hidden.
  • Fullscreen editing: Expands the editor to fill the viewport with a single toolbar button.
  • Keyboard shortcuts: Standard shortcuts for bold, italic, underline, undo, redo, link insertion, and list indentation.

How To Use It:

Installation

React:

npm install editium

Editium requires React and ReactDOM as peer dependencies:

npm install react react-dom

Vanilla JavaScript (CDN):

The single-bundle option includes JavaScript, CSS, and icons in one file. No additional setup is needed:

<script src="https://unpkg.com/editium/vanilla/editium.bundle.js"></script>

Vanilla JavaScript (npm):

npm install editium

Then import the CSS and the Vanilla module separately:

import 'editium/vanilla/editium.css';
import Editium from 'editium/vanilla/editium.js';

Basic Setup

React:

import { Editium } from 'editium';

function App() {
  return (
    <Editium
      placeholder="Write your article here..."
      toolbar="all"             // Show every available toolbar button
      showWordCount={true}      // Display word and character count below editor
    />
  );
}

Vanilla JavaScript:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Editium Demo</title>
  <!-- Load the self-contained bundle: JS + CSS + icons in one file -->
  <script src="https://unpkg.com/editium/vanilla/editium.bundle.js"></script>
</head>
<body>
  <div id="my-editor"></div>

  <script>
    // Attach the editor to a DOM element
    const editor = new Editium({
      container: document.getElementById('my-editor'),
      placeholder: 'Write your article here...',
      toolbar: 'all',           // Show every available toolbar button
      showWordCount: true       // Display word and character count
    });
  </script>
</body>
</html>

Capturing Content Changes

React (controlled component):

import React, { useState } from 'react';
import { Editium } from 'editium';

function ArticleEditor() {
  const [htmlContent, setHtmlContent] = useState('');
  const [jsonContent, setJsonContent] = useState(null);

  // onChange fires with both HTML string and JSON structure on every edit
  const handleChange = (html: string, json: any) => {
    setHtmlContent(html);
    setJsonContent(json);
  };

  const handleSave = async () => {
    // Post the HTML and JSON payload to your backend
    await fetch('/api/articles', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ html: htmlContent, json: jsonContent })
    });
  };

  return (
    <div>
      <Editium onChange={handleChange} toolbar="all" />
      <button onClick={handleSave}>Publish Article</button>
    </div>
  );
}

Vanilla JavaScript:

const editor = new Editium({
  container: document.getElementById('my-editor'),
  toolbar: 'all',
  onChange: (content) => {
    // content.html  — HTML string
    // content.json  — structured JSON array
    console.log('HTML output:', content.html);
  }
});

// Read content on demand via the API
document.getElementById('save-btn').addEventListener('click', () => {
  const payload = {
    html: editor.getHTML(),   // Full HTML string
    text: editor.getText(),   // Plain text string (no markup)
    json: editor.getJSON()    // Slate-compatible JSON array
  };

  fetch('/api/articles', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload)
  });
});

Loading Initial Content

React:

import { Editium } from 'editium';

function EditExistingPost() {
  // Pass a Slate.js-compatible JSON array as the initial value
  const savedContent = [
    {
      type: 'heading-one',
      children: [{ text: 'My Article Title' }]
    },
    {
      type: 'paragraph',
      children: [{ text: 'Edit this paragraph to get started.' }]
    }
  ];

  return (
    <Editium
      initialValue={savedContent}
      toolbar="all"
    />
  );
}

Vanilla JavaScript:

const editor = new Editium({
  container: document.getElementById('my-editor'),
  toolbar: 'all'
});

// setContent() accepts an HTML string
editor.setContent('<h1>My Article Title</h1><p>Edit this paragraph to get started.</p>');

Custom Toolbar

Pass an array of toolbar item keys to display only what you need:

// React
<Editium
  toolbar={[
    'bold', 'italic', 'underline', 'strikethrough',
    'separator',
    'heading-one', 'heading-two', 'heading-three',
    'separator',
    'bulleted-list', 'numbered-list', 'indent', 'outdent',
    'separator',
    'link', 'image',
    'separator',
    'undo', 'redo'
  ]}
/>
// Vanilla JS
const editor = new Editium({
  container: document.getElementById('my-editor'),
  toolbar: [
    'bold', 'italic', 'underline', 'strikethrough',
    'separator',
    'heading-one', 'heading-two', 'heading-three',
    'separator',
    'bulleted-list', 'numbered-list', 'indent', 'outdent',
    'separator',
    'link', 'image',
    'separator',
    'undo', 'redo'
  ]
});

Custom Image Upload Handler

Editium calls your upload function with the selected File object and expects a resolved URL string in return:

React:

import { Editium } from 'editium';

function EditorWithUpload() {
  const handleImageUpload = async (file: File): Promise<string> => {
    const form = new FormData();
    form.append('file', file);               // Append the file to a FormData payload

    const res = await fetch('/api/media', {
      method: 'POST',
      body: form
    });

    const data = await res.json();
    return data.url;                          // Return the CDN URL for the uploaded image
  };

  return (
    <Editium
      onImageUpload={handleImageUpload}
      toolbar="all"
    />
  );
}

Vanilla JavaScript:

const editor = new Editium({
  container: document.getElementById('my-editor'),
  toolbar: 'all',
  onImageUpload: async (file) => {
    const form = new FormData();
    form.append('file', file);

    const res = await fetch('/api/media', {
      method: 'POST',
      body: form
    });

    const data = await res.json();
    return data.url;                          // Return the CDN URL for the uploaded image
  }
});

Read-Only Mode

Use read-only mode to display stored rich text as a formatted view:

// React
<Editium
  initialValue={storedJsonContent}
  readOnly={true}
  toolbar={[]}        // Pass an empty array to hide the toolbar entirely
/>
// Vanilla JS
const viewer = new Editium({
  container: document.getElementById('article-body'),
  readOnly: true,
  toolbar: []         // Pass an empty array to hide the toolbar entirely
});

viewer.setContent(storedHtml);

Height Configuration

Heights accept either a string with a CSS unit or a plain number (converted to px):

// React
<Editium
  height={500}          // Sets default height to 500px
  minHeight={250}       // Editor won't shrink below 250px
  maxHeight={800}       // Editor scrolls beyond 800px
/>
// Vanilla JS
const editor = new Editium({
  container: document.getElementById('my-editor'),
  height: '500px',
  minHeight: '250px',
  maxHeight: '800px'
});

Custom Styling

Editium exposes CSS class hooks for all its structural elements:

/* Wrapper around the entire editor */
.editium-wrapper {
  border: 2px solid #4f46e5;
  border-radius: 6px;
  font-family: 'Inter', sans-serif;
}

/* Toolbar bar */
.editium-toolbar {
  background-color: #f8f8f8;
  border-bottom: 1px solid #e5e7eb;
}

/* Toolbar button hover and active states */
.editium-toolbar button:hover {
  background-color: #e0e7ff;
}

.editium-toolbar button.active {
  background-color: #4f46e5;
  color: white;
}

/* Editable content area */
.editium-editor {
  padding: 24px;
  font-size: 16px;
  line-height: 1.75;
}

Vanilla JS API Methods

// Returns the full HTML string of the current editor content
editor.getHTML();

// Returns a plain text version of the content, with all markup stripped
editor.getText();

// Returns the Slate-compatible JSON array representing the document structure
editor.getJSON();

// Replaces the editor content with the provided HTML string
editor.setContent('<p>Replacement content goes here.</p>');

// Clears all content from the editor
editor.clear();

// Moves focus to the editor area
editor.focus();

// Removes the editor from the DOM and cleans up event listeners
editor.destroy();

Configuration Reference

React Props

  • initialValue (string | CustomElement[]): Sets the editor’s starting content. Accepts an HTML string or a Slate-compatible JSON array. Defaults to a single empty paragraph.
  • onChange ((html: string, json: CustomElement[]) => void): Fires on every content change. Receives the HTML string and the JSON array.
  • toolbar (ToolbarItem[] | ‘all’): Accepts 'all' to show every button, or an array of item key strings to show a custom set.
  • placeholder (string): Text shown in the editor area when content is empty. Defaults to 'Start typing...'.
  • className (string): Adds a custom CSS class to the outer wrapper element.
  • style (React.CSSProperties): Applies inline styles to the outer wrapper element.
  • readOnly (boolean): Puts the editor in display-only mode. Defaults to false.
  • onImageUpload ((file: File) => Promise<string>): Receives the selected File object and must return a resolved URL string.
  • searchQuery (string): The active search term for match highlighting.
  • searchMatches (Array): The array of match locations returned by the find-replace feature.
  • currentMatchIndex (number): The index of the currently highlighted search match.
  • showWordCount (boolean): Displays the word and character count below the editor. Defaults to true.
  • height (string | number): Sets the default editor height. Defaults to '200px'.
  • minHeight (string | number): Sets the minimum height before content causes the editor to shrink. Defaults to '150px'.
  • maxHeight (string | number): Sets the maximum height before the editor area scrolls. Defaults to '250px'.

Vanilla JS Options

  • container (HTMLElement): The DOM element to mount the editor into. Required.
  • placeholder (string): Text shown when the editor is empty. Defaults to ''.
  • toolbar (string | array): Accepts 'all' or an array of item key strings. Defaults to 'all'.
  • showWordCount (boolean): Displays the word and character count. Defaults to false.
  • readOnly (boolean): Puts the editor in display-only mode. Defaults to false.
  • className (string): Adds a custom CSS class to the wrapper element.
  • height (string | number): Default editor height. Defaults to '200px'.
  • minHeight (string | number): Minimum height. Defaults to '150px'.
  • maxHeight (string | number): Maximum height before scrolling. Defaults to '250px'.
  • onChange (function): Fires on every content change. Receives an object with html, text, and json properties.
  • onImageUpload (function): Receives the File object and must return a resolved URL string.

Toolbar Item Keys

All available item keys: bold, italic, underline, strikethrough, code, superscript, subscript, heading-one, heading-two, heading-three, heading-four, heading-five, heading-six, heading-seven (React only), heading-eight (React only), paragraph, blockquote, code-block, bulleted-list, numbered-list, indent, outdent, left, center, right, justify, text-color, bg-color, link, image, table, horizontal-rule, undo, redo, find-replace, fullscreen, view-output, separator.

Keyboard Shortcuts

Shortcut Action
Ctrl/Cmd + B Bold
Ctrl/Cmd + I Italic
Ctrl/Cmd + U Underline
Ctrl/Cmd + Z Undo
Ctrl/Cmd + Y Redo
Ctrl/Cmd + K Insert link
F11 Toggle fullscreen
Ctrl/Cmd + F Open find and replace
Tab Indent list item
Shift + Tab Outdent list item

Alternatives

The post Rich Text WYSIWYG Editor for React & Vanilla JS – Editium appeared first on CSS Script.


Discover more from RSS Feeds Cloud

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Discover more from RSS Feeds Cloud

Subscribe now to keep reading and get access to the full archive.

Continue reading