📚 Examples & Tutorials

Learn through practical code examples

Live Playground: Try Editora in your browser with a working sample. Open Playground →
Regression Test: Run the automated DOM-shape check for formatting sequence regressions. Open Regression Test →
Theme Verification: Compare default, dark, and the new acme theme. Open Acme Theme Demo →
Config Labs: Validate runtime config updates for autosave/security/performance/accessibility. Open Security + Autosave Lab → Open Performance + A11y Lab →
Advanced Find/Replace: Light Code Editor now supports case-sensitive, whole-word, and regex search with configurable replace navigation. Open Light Code Editor Demo →

Bundle Options

Editora offers two bundle sizes to fit your needs:

  • Core Bundle: Essential plugins only (bold, italic, underline, history)
  • Full Bundle: All plugins including advanced ones (embedIframe, math, specialCharacters, table, image, link, heading, list, blockquote, code, fontSize, textAlignment, fontFamily, lineHeight, textColor, backgroundColor, capitalization, indent, direction, checklist, emojis, preview, fullscreen, print, pageBreak, footnote, codeSample, anchor, mergeTag, template, comments, spellCheck, a11yChecker, strikethrough, clearFormatting, image, documentManager)

React Plugin Presets (NPM)

When integrating with @editora/react, you can choose plugin presets:

  • @editora/plugins: full plugin catalog
  • @editora/plugins/lite: common/core plugins for lean bundles
  • @editora/plugins/enterprise: advanced/specialized workflow plugins

Enterprise preset includes: Mention, Track Changes, Version Diff, Conditional Content, Data Binding, Content Rules, Citations, Approval Workflow, PII Redaction, Smart Paste, Blocks Library, Document Schema, Translation Workflow, Slash Commands, Spell Check, A11y Checker, Comments, Merge Tag, Template, Media Manager, and Document Manager.

All plugin entry paths (full/lite/enterprise) are completely free and customizable.

Core Bundle Usage

<!DOCTYPE html>
<html>
<head>
  <title>Editora Core Bundle</title>
  <script type="module" src="https://cdn.jsdelivr.net/npm/@editora/core/dist/webcomponent-core.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@editora/core/dist/webcomponent-core.min.css">
</head>
<body>
  <editora-editor
    plugins="bold italic underline history"
    toolbar-items="bold italic underline | undo redo"
    height="300"
  >
    <p>Start editing with core plugins...</p>
  </editora-editor>
</body>
</html>

Full Bundle Usage

<!DOCTYPE html>
<html>
<head>
  <title>Editora Full Bundle</title>
  <script type="module" src="https://cdn.jsdelivr.net/npm/@editora/core/dist/webcomponent.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@editora/core/dist/webcomponent.min.css">
</head>
<body>
  <editora-editor
    plugins="bold italic underline table codeSample embedIframe math specialCharacters history"
    toolbar-items="bold italic underline | table | codeSample | embedIframe | math | specialCharacters | undo redo"
    height="400"
  >
    <p>Start editing with all plugins...</p>
  </editora-editor>
</body>
</html>

Basic HTML Example

<!DOCTYPE html>
<html>
<head>
  <title>Editora Web Component</title>
  <script type="module" src="https://cdn.jsdelivr.net/npm/@editora/core/dist/webcomponent.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@editora/core/dist/webcomponent.min.css">
  <!-- Optional theme packs (npm: @editora/themes) -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@editora/themes/themes/dark.css">
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@editora/themes/themes/acme.css">
</head>
<body>
  <editora-editor
    plugins="bold italic underline history"
    toolbar-items="bold italic underline | undo redo"
    height="300"
    theme="dark"
    autosave='{"enabled":true,"intervalMs":3000,"storageKey":"example-doc"}'
    security='{"sanitizeOnPaste":true,"sanitizeOnInput":true}'
    accessibility='{"enableARIA":true,"keyboardNavigation":true}'
    performance='{"debounceInputMs":120,"viewportOnlyScan":true}'
  >
    <p>Start editing your content here...</p>
  </editora-editor>

  <script>
    // Access the editor programmatically
    const editor = document.querySelector('editora-editor');
    
    // Get content
    const content = editor.getContent();
    
    // Set content
    editor.setContent('<p>New content</p>');
    
    // Get API for advanced operations
    const api = editor.getAPI();
    if (api) {
      console.log('Editor content:', api.getContent());
      console.log('Runtime config:', api.getConfig());
    }
  </script>
</body>
</html>

React Example


import { useState } from "react";
import { EditoraEditor } from '@editora/react';

import "@editora/plugins/styles.css";
import "@editora/themes/themes/default.css";
import "@editora/themes/themes/acme.css";

import {
  BoldPlugin,
  ItalicPlugin,
  HeadingPlugin,
  HistoryPlugin
} from '@editora/plugins';

function MyEditor() {
  const [content, setContent] = useState('<p>Start writing...</p>');

  return (
    <div data-theme="acme">
      <EditoraEditor
        value={content}
        onChange={setContent}
        plugins={[
          BoldPlugin(),
          ItalicPlugin(),
          HeadingPlugin(),
          HistoryPlugin()
        ]}
      />
    </div>
  );
}

For CRA and other bundlers: plugin UI (table toolbar, color pickers, dialogs) requires @editora/plugins/styles.css. Import default.css first, then optional theme overrides.

Merge Tag Functional Customization

Use runtime options to define custom categories, labels, dialog text, and token output.

import { MergeTagPlugin } from "@editora/plugins";

const mergeTag = MergeTagPlugin({
  categories: [
    {
      id: "CUSTOMER",
      name: "Customer",
      tags: [
        { key: "first_name", label: "First Name", value: "{{customer.first_name}}", preview: "John" },
        { key: "email", label: "Email", value: "{{customer.email}}", preview: "john@acme.com" }
      ]
    },
    {
      id: "ORDER",
      name: "Order",
      tags: [
        { key: "id", label: "Order ID", value: "{{order.id}}", preview: "#A-1024" }
      ]
    }
  ],
  defaultCategory: "CUSTOMER",
  dialog: {
    title: "Insert Variable",
    searchPlaceholder: "Search variables...",
    emptyStateText: "No variables found",
    cancelText: "Close",
    insertText: "Insert",
    showPreview: true
  },
  tokenTemplate: "{value}" // supports {key} {label} {category} {value}
});

Template Plugin: Add Custom Templates

Register templates before initializing the editor plugin list.

import { TemplatePlugin, addCustomTemplate } from "@editora/plugins";

addCustomTemplate({
  id: "invoice-basic",
  name: "Invoice (Basic)",
  category: "Billing",
  description: "Simple invoice template",
  html: `
    <h1>Invoice</h1>
    <p><strong>Customer:</strong> {{customer.name}}</p>
    <p><strong>Date:</strong> {{today}}</p>
    <p><strong>Total:</strong> {{invoice.total}}</p>
  `,
  tags: ["invoice", "billing"]
});

const plugins = [TemplatePlugin()];

Vue 3 Example

<template>
  <div class="editor-wrapper">
    <editora-editor
      plugins="bold italic underline history"
      toolbar-items="bold italic underline | undo redo"
      height="300"
    >
      <p>Hello from Vue 3!</p>
    </editora-editor>
  </div>
</template>

<script setup>
import { onMounted } from 'vue';

onMounted(() => {
  // Access the editor programmatically if needed
  const editor = document.querySelector('editora-editor');
  if (editor) {
    const api = editor.getAPI();
    // Use API for advanced operations
  }
});
</script>

Vue 3 + Plugins Example

<template>
  <div class="editor-wrapper">
    <editora-editor
      plugins="bold italic underline table codeSample history"
      toolbar-items="bold italic underline | table | codeSample | undo redo"
      height="400"
    >
      <p>Hello from Vue with plugins!</p>
    </editora-editor>
  </div>
</template>

<script setup>
import { onMounted } from 'vue';

onMounted(() => {
  const editor = document.querySelector('editora-editor');
  if (editor) {
    const api = editor.getAPI();
    // Use API for advanced operations
  }
});
</script>

Angular Example

// app.component.ts
import { Component, AfterViewInit } from '@angular/core';

@Component({
  selector: 'app-root',
  template: `
    <editora-editor
      plugins="bold italic underline history"
      toolbar-items="bold italic underline | undo redo"
      height="300"
    >
      <p>Hello from Angular!</p>
    </editora-editor>
  `
})
export class AppComponent implements AfterViewInit {
  ngAfterViewInit() {
    const editor = document.querySelector('editora-editor');
    if (editor) {
      const api = editor.getAPI();
      // Use API for advanced operations
    }
  }
}

Svelte Example

<script>
  import { onMount } from 'svelte';

  onMount(() => {
    const editor = document.querySelector('editora-editor');
    if (editor) {
      const api = editor.getAPI();
      // Use API for advanced operations
    }
  });
</script>

<editora-editor
  plugins="bold italic underline history"
  toolbar-items="bold italic underline | undo redo"
  height="300"
>
  <p>Hello from Svelte!</p>
</editora-editor>

With Plugins Example

<!DOCTYPE html>
<html>
<head>
  <script type="module" src="https://cdn.jsdelivr.net/npm/@editora/core/dist/webcomponent.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@editora/core/dist/webcomponent.min.css">
</head>
<body>
  <editora-editor
    plugins="bold italic underline image table codeSample history"
    toolbar-items="bold italic underline | insertImage insertVideo | table | codeSample | undo redo"
    height="400"
  >
    <p>Start editing...</p>
  </editora-editor>

  <button id="save">Save</button>

  <script>
    document.getElementById('save').addEventListener('click', () => {
      const editor = document.querySelector('editora-editor');
      const html = editor.innerHTML;
      // Send to server
      fetch('/api/save', {
        method: 'POST',
        body: JSON.stringify({ content: html })
      });
    });
  </script>
</body>
</html>

Runtime Config Example (setConfig)

const editor = document.querySelector('editora-editor');

await editor.setConfig({
  theme: 'acme',
  toolbar: {
    items: 'undo redo | bold italic underline',
    floating: true,
    sticky: true
  },
  autosave: {
    enabled: true,
    intervalMs: 4000,
    storageKey: 'runtime-doc',
    provider: 'localStorage'
  },
  security: {
    sanitizeOnPaste: true,
    sanitizeOnInput: true
  },
  accessibility: {
    enableARIA: true,
    keyboardNavigation: true,
    checker: true
  },
  performance: {
    debounceInputMs: 120,
    viewportOnlyScan: true
  }
});

Auto-Save Example

<!DOCTYPE html>
<html>
<head>
  <script type="module" src="https://cdn.jsdelivr.net/npm/@editora/core/dist/webcomponent.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@editora/core/dist/webcomponent.min.css">
</head>
<body>
  <editora-editor
    plugins="bold italic underline history"
    height="300"
    autosave='{"enabled":true,"intervalMs":30000,"storageKey":"draft-doc","provider":"localStorage"}'
  >
    <p>Start typing...</p>
  </editora-editor>

  <script>
    // Built-in autosave stores content using the configured storageKey.
    // You can still read latest content manually when needed:
    const editor = document.querySelector('editora-editor');
    editor.addEventListener('content-change', () => {
      console.log('Current content:', editor.getContent());
    });
  </script>
</body>
</html>

Custom Toolbar Example

<editora-editor
  plugins="bold italic underline heading list link image history"
  toolbar-items="bold italic underline | heading1 heading2 | bullist numlist | link insertImage | undo redo"
  height="300"
>
  <p>Custom toolbar with specific buttons</p>
</editora-editor>

Event Handling Example

<editora-editor
  plugins="bold italic underline history"
  height="300"
>
  <p>Try editing this content...</p>
</editora-editor>

<div id="preview"></div>
<div id="status">Ready</div>

<script>
  const editor = document.querySelector('editora-editor');
  const preview = document.getElementById('preview');
  const status = document.getElementById('status');

  // Listen for content changes
  editor.addEventListener('input', () => {
    preview.innerHTML = editor.innerHTML;
  });

  // Focus/blur events
  editor.addEventListener('focus', () => {
    status.textContent = 'Editing...';
  });

  editor.addEventListener('blur', () => {
    status.textContent = 'Ready';
  });
</script>

Data Binding Example

<form id="form">
  <input type="hidden" id="content" name="content">
  <editora-editor
    plugins="bold italic underline history"
    height="300"
  >
    <p>Start writing...</p>
  </editora-editor>
  <button type="submit">Save</button>
</form>

<script>
  const form = document.getElementById('form');
  const contentInput = document.getElementById('content');
  const editor = document.querySelector('editora-editor');

  // Set initial content
  editor.innerHTML = contentInput.value || '<p>Start writing...</p>';

  // Update input on change
  editor.addEventListener('input', () => {
    contentInput.value = editor.innerHTML;
  });

  // Submit form
  form.addEventListener('submit', (e) => {
    e.preventDefault();
    // Form is ready with content in contentInput
    console.log('Content to save:', contentInput.value);
    // form.submit();
  });
</script>

Formatting Example

<editora-editor
  plugins="bold italic underline heading link history"
  toolbar-items="bold italic underline | heading1 heading2 heading3 | link | clearFormatting"
  height="300"
>
  <p>Select text and use the toolbar buttons to format it.</p>
</editora-editor>

<!-- Custom buttons for programmatic formatting -->
<div style="margin-top: 10px;">
  <button onclick="makeBold()">Make Bold</button>
  <button onclick="insertHeading(1)">H1</button>
  <button onclick="insertLink()">Insert Link</button>
</div>

<script>
  const editor = document.querySelector('editora-editor');
  const api = editor.getAPI();

  function makeBold() {
    if (api) {
      // Use API to execute commands programmatically
      api.execCommand('bold');
    }
  }

  function insertHeading(level = 1) {
    if (api) {
      api.execCommand('heading', { level });
    }
  }

  function insertLink() {
    const url = prompt('Enter URL:');
    if (url && api) {
      api.execCommand('insertLink', { url });
    }
  }
</script>

Validation Example

<editora-editor
  plugins="bold italic underline history"
  height="300"
>
  <p>Write some content here...</p>
</editora-editor>

<button id="submit">Submit Content</button>

<script>
  const editor = document.querySelector('editora-editor');
  const submitBtn = document.getElementById('submit');

  function validateContent() {
    const content = editor.innerHTML;
    
    // Check if empty
    if (content === '<p></p>' || content.trim() === '') {
      alert('Content cannot be empty');
      return false;
    }
    
    // Check minimum length
    const text = new DOMParser().parseFromString(content, 'text/html').body.textContent;
    if (text.length < 10) {
      alert('Content must be at least 10 characters');
      return false;
    }
    
    return true;
  }

  submitBtn.addEventListener('click', () => {
    if (validateContent()) {
      // Save content
      saveContent(editor.innerHTML);
    }
  });

  function saveContent(content) {
    console.log('Saving content:', content);
    // Send to server...
  }
</script>

💡 Additional Example Files

Explore these additional working example files:

🔗 Core Examples

🎨 UI & Toolbar

📷 Media & Plugins

🔧 Advanced Features

🍞 Toast Notifications

âš›ī¸ Framework Examples

All example files use CDN URLs and can be opened directly in your browser. Check the GitHub examples directory for the complete collection.