Lightweight WYSIWYG Editor for Modern Web Apps – RayEditor

Category: Javascript , Text | March 26, 2026
Authoryeole-rohan
Last UpdateMarch 26, 2026
LicenseMIT
Views269 views
Lightweight WYSIWYG Editor for Modern Web Apps – RayEditor

RayEditor is a lightweight WYSIWYG editor that transforms plain content areas into feature-rich text editors with zero dependencies.

It is useful if you need more than a plain textarea but less than a full-blown document editor like Google Docs. Think blog post creation, CMS content fields, simple documentation tools, or even formatted comment sections.

Features

  • Standard Text Formatting: Bold, Italic, Underline, Strikethrough.
  • Case Transformation: Uppercase, Lowercase, Toggle Case.
  • Text Styles: Subscript, Superscript, Text Color, Background Color, Alignment.
  • Structure: Headings (H1-H6), Ordered/Unordered Lists.
  • Code Editing: Inline code highlighting and multi-line code blocks.
  • Media: Image and File uploads with configurable endpoints and size limits. Image resizing is included.
  • Tables: Basic table creation and manipulation (add/delete rows/columns).
  • History: Undo/Redo functionality.
  • Links: Add and edit hyperlinks.

How to use it:

1. Download the package and load the RayEditor’s JavaScript & files in the document.

<link rel="stylesheet" href="ray-editor.css" />
<script src="ray-editor.js"></script>

2. Place an empty div (or another block element) in your HTML where you want the editor to appear. Give it an ID so you can target it with JavaScript.

<div id="myEditor"></div>

3. Call the RayEditor function to create a basic WYSIWYG editor:

const editor = new RayEditor('editor', {
  // options here
});

4. Configure the editor with the following options:

  • toolbar (ToolbarGroup[]): Defines the visual groups of buttons separated by dividers.
  • imageUpload (object): Configures image handling. Contains imageUploadUrl (string) and imageMaxSize (number).
  • fileUpload (object): Configures file handling. Contains fileUploadUrl (string) and fileMaxSize (number).
  • mentions (object): Configures user tagging. Contains enableMentions (boolean), mentionTag (string), mentionElement (string), and mentionUrl (string).
  • toolbarType (string): Sets the toolbar display style. Accepts ‘default’ or ‘inline’.
  • overflowMenu (boolean): Collapses excess toolbar buttons into an overflow menu.
  • readOnly (boolean): Disables text editing.
  • markdownShortcuts (boolean): Toggles markdown syntax conversion.
  • wordCount (boolean): Displays a word count bar at the bottom.
  • findReplace (boolean): Enables the search and replace panel.
  • slashCommands (boolean): Activates the / command palette.
  • historySize (number): Sets the maximum number of undo/redo steps.
  • theme (string): Sets the visual theme. Accepts ‘light’, ‘dark’, or ‘auto’.
  • initStyles (boolean): Auto-injects the default CSS link.
  • stylesheetUrl (string): Points to a custom CSS file.
  • hideWatermark (boolean): Removes the editor watermark.
  • plugins (RayPlugin[]): Registers external plugins.
  • onChange (function): Fires a callback when the HTML content updates.
const editor = new RayEditor('editor', {

  toolbar: ToolbarGroup[], 
  imageUpload: {
    imageUploadUrl: string,
    imageMaxSize: number,
  },
  fileUpload: {
    fileUploadUrl: string,
    fileMaxSize: number,
  },
  mentions: {
    enableMentions: boolean, 
    mentionTag: string,
    mentionElement: 'span' | 'a',
    mentionUrl: string,
  },
  toolbarType: 'default' | 'inline', 
  overflowMenu: boolean,
  readOnly: boolean,
  markdownShortcuts: boolean,
  wordCount: boolean,
  findReplace: boolean,
  slashCommands: boolean,
  historySize: number,
  theme: 'light' | 'dark' | 'auto',
  initStyles: boolean,
  stylesheetUrl: string,
  hideWatermark: boolean,
  plugins: RayPlugin[],
  onChange: (html: string) => void,
  
});

5. API methods.

// Retrieves the current HTML content from the editor
myEditor.getContent();
// Overwrites the editor with new HTML content
myEditor.setContent('<h1>Welcome to our custom editor</h1>');
// Legacy method to get content (v1 backward compatibility)
myEditor.getRayEditorContent();
// Legacy method to set content (v1 backward compatibility)
myEditor.setRayEditorContent('<p>Legacy support active</p>');
// Attaches an event listener to the editor
myEditor.on('focus', myFocusHandler);
// Removes an event listener from the editor
myEditor.off('focus', myFocusHandler);
// Manually triggers a specific event
myEditor.emit('customEvent', { data: 'testPayload' });
// Registers a new plugin
myEditor.use(myCustomPlugin);
// Injects a new button into the toolbar
myEditor.addButton({ name: 'highlightText', icon: 'H', action: () => {} });
// Removes an existing button from the toolbar
myEditor.removeButton('highlightText');
// Adds a new command to the slash palette
myEditor.registerSlashCommand({ name: 'Insert Signature', action: () => {} });
// Registers a custom command handler
myEditor.registerCommand('saveDocument', () => {});
// Executes a registered command programmatically
myEditor.execCommand('bold');
// Switches the editor theme dynamically
myEditor.setTheme('dark');
// Toggles the read-only state
myEditor.setReadOnly(true);
// Returns the current word and character count
myEditor.getWordCount();
// Completely removes the editor instance and cleans up the DOM
myEditor.destroy();

6. Events.

// Triggers whenever the HTML content updates
myEditor.on('content:change', function(event) {
  console.log('New content detected:', event.html);
});
// Triggers when the user changes their text selection or cursor position
myEditor.on('selection:change', function() {
  console.log('User moved the cursor');
});
// Triggers when the editor gains focus
myEditor.on('focus', function() {
  console.log('Editor is now active');
});
// Triggers when the editor loses focus
myEditor.on('blur', function() {
  console.log('Editor is now inactive');
});
// Triggers right before a command executes. Return false to cancel it.
myEditor.on('command:before', function(event) {
  console.log('Preparing to run:', event.command);
});
// Triggers immediately after a command finishes executing
myEditor.on('command:after', function(event) {
  console.log('Finished running:', event.command);
});
// Triggers when a new plugin finishes installation
myEditor.on('plugin:install', function(event) {
  console.log('Successfully installed plugin:', event.name);
});
// Triggers when a plugin is removed or destroyed
myEditor.on('plugin:destroy', function(event) {
  console.log('Successfully removed plugin:', event.name);
});
// Triggers when the visual theme changes
myEditor.on('theme:change', function(event) {
  console.log('Switched to new theme:', event.theme);
});

Changelog:

03/26/2025

  • v2.0.9: bugfixes

03/04/2025

  • feat: inline color picker, datetime popup, table toolbar fixes, multi-editor isolation

08/20/2025

  • code fixes and improements

08/04/2025

  • Implement Toggleable Source Code Button

05/28/2025

  • code fixes and improements

05/27/2025

  • Add a chck to ensure the codeblock insertion

05/23/2025

  • JS update

05/13/2025

  • css, pre and code fix

You Might Be Interested In:


Leave a Reply