background-symbol
release-notes-vector
DocsGetting Started

Eddyter documentation

A powerful, configurable rich text editor built on Lexical with AI capabilities and API key authentication.

Last updated:

Compatibility

Laravel ships Eddyter via Blade + Vite using @eddyter/core directly. No PHP package is required; the API key flows from config/services.php to a Blade data-attribute.

Pick your framework

Getting Started

Drop Eddyter into Laravel via Blade + Vite using the SDK directly. No PHP package required.

Framework Support

Check the compatibility banner above for supported versions. Mix and match Eddyter across React, the framework-agnostic SDK, Angular, Svelte, Vue, and Laravel.
1

Installation

Install the package via your preferred manager:

terminal
composer install && npm install @eddyter/core

Or with yarn / pnpm:

# add to package.json
# "@eddyter/core": "^1.7.0"
2

Style Integration

Import the required CSS so toolbars, tables, menus, and editor chrome render correctly:

resources/js/editor/basic.js
import '@eddyter/core/style.css';

Production note

Import the SDK stylesheet at the top of each Vite entry that mounts an editor.
3

Get your API key

Sign up and get your API key from the dashboard:

  1. Create an account at eddyter.com
  2. Navigate to License Keys in your dashboard
  3. Copy your API key

Free Trial

New accounts get 2 weeks of free premium access with all AI features enabled!
4

Mount the editor from a Vite entry

Implement the editor with your API key:

resources/js/editor/basic.js
import { init } from '@eddyter/core';
import '@eddyter/core/style.css';

// In the Blade view:
//   <main class="basic-page" data-api-key="{{ config('services.eddyter.api_key') }}">
//     <div id="editor" class="basic-page__editor"></div>
//   </main>

const apiKey = document.querySelector('.basic-page')?.dataset.apiKey ?? '';

const instance = init({
  container: '#editor',
  apiKey,
  mode: 'edit',
  darkMode: false,
  initialContent: '<p>Start typing…</p>',
  onChange: (html) => { /* persist html */ },
  onReady: () => console.log('[Eddyter] onReady'),
  onAuthSuccess: () => console.log('[Eddyter] onAuthSuccess'),
  onAuthError: (msg) => console.error('[Eddyter] onAuthError', msg),
});

Environment Variable

Store your API key in environment variables. Never commit API keys to version control.

Video on Integrating with AI

Authentication

Standard API key verification to enable premium features and AI capabilities.

How it works

  1. Read the key from a data-api-key attribute rendered from Blade, then pass it to init().
  2. The editor validates the key against our server
  3. Features are enabled based on your subscription plan
  4. onAuthSuccess fires when validation succeeds
  5. onAuthError fires if validation fails

Custom verification (optional)

You can provide your own verification function if you need to validate keys through your backend:

resources/js/editor/basic.js
import { init } from '@eddyter/core';

const apiKey = document.querySelector('.basic-page')?.dataset.apiKey ?? '';

init({
  container: '#editor',
  apiKey,
  customVerifyKey: async (key) => {
    const res = await fetch('/api/verify-editor-key', {
      method: 'POST',
      body: JSON.stringify({ key }),
    });
    const data = await res.json();
    return { success: data.valid, message: data.message };
  },
});

Features

A comprehensive toolset for modern content creation, from basic formatting to advanced AI generation.

Classic Formatting

Basic Formatting

Bold, italic, underline, strikethrough, subscript, superscript

Text Colors

Text color and background highlight with color picker

Font Controls

20+ font families with adjustable font sizes and line height

Text Alignment

Left, center, right, and justify alignment

Lists and Structure

Bulleted Lists

Custom bullet styles with proper nesting

Numbered Lists

Decimal, alpha, and roman numeral formats

Checklists

Interactive checkboxes with strikethrough

Headings

H1-H6 heading levels

Tables

Table Operations

Insert/delete rows and columns, merge cells

Cell Resizing

Drag to resize columns and rows

Header Styling

Distinct header row styling

Context Menu

Right-click menu for quick actions

Media Support

Images

Drag-drop upload with 8-point resize handles

Videos

YouTube/Vimeo embed with responsive players

File Attachments

Upload and attach downloadable files

Link Management

Insert links with floating editor and preview

AI PowerPremium

Smart Chat

In-editor AI assistant for research, drafting, and creative ideas.

Smart Autocomplete

Predictive text suggestions as you type.

Refinement

Instantly improve tone, fix grammar, or change content length.

Gen-AI Images

Create custom visuals from text prompts inside your document.

Configuration

Tailor every aspect of the editor to fit your application's specific needs.

Toolbar Configuration

Configure toolbar behavior with the toolbar option. In sticky mode you can set offset and zIndex. In static mode those values are ignored.

resources/js/editor/basic.js
init({
  container: '#editor',
  apiKey,
  toolbar: { mode: 'sticky', offset: 64, zIndex: 1200 },
});

Defaults: { mode: "sticky", offset: 20, zIndex: 1000 }.

In static mode, the editor automatically defaults to a maxHeight of 600px. You only need to pass the editor option if you wish to override this default.

resources/js/editor/basic.js
init({
  container: '#editor',
  apiKey,
  toolbar: { mode: 'static' },
  // editor: { maxHeight: 400 }, // Optional: overrides 600px default
});

Keyboard Shortcuts

Speed up your workflow with standard keyboard shortcuts.

Text Formatting

BoldCtrl/Cmd + B
ItalicCtrl/Cmd + I
UnderlineCtrl/Cmd + U

General

UndoCtrl/Cmd + Z
RedoCtrl/Cmd + Y
Select AllCtrl/Cmd + A
PasteCtrl/Cmd + V

Slash Commands

Type / at the start of a line to access the quick formatting menu.

API Reference

<EditorProvider>

Provides context and configuration for the editor. Must wrap the editor component to enable all features.

PropTypeDescription
childrenReactNodeThe wrapped content.
defaultFontFamiliesstring[]Override default font list.
currentUserCurrentUserUser info for comments.
apiKeystringAPI key for read-only mode.

<ConfigurableEditorWithAuth>

The core editor component with built-in subscription verification and AI service integration.

PropTypeReqDescription
apiKeystringYesYour Eddyter license key. Authenticates the editor against the server and unlocks tier-based features.
initialContentstringNoInitial HTML loaded into the editor on first render.
onChange(html: string) => voidNoCalled ~300 ms after content changes (debounced). Receives the current HTML.
onAuthSuccess() => voidNoFires once the API key is verified and the editor is ready.
onAuthError(error: string) => voidNoFires when API key verification fails. Receives the error message.
customVerifyKey(apiKey: string) => Promise<ApiResponse>NoProvide your own verifier (e.g. proxy through your backend) instead of the default endpoint.
mode"edit" | "preview"NoRender the full editor ("edit", default) or a read-only preview with link previews ("preview").
containerClassNamestringNoClass applied to the outer wrapper container.
contentClassNamestringNoClass applied specifically to the editor's writable content area.
onPreviewClick() => voidNoFires when the user clicks inside preview mode — typically used to switch to edit mode.
darkModeboolean | undefinedNotrue forces dark, false forces light, undefined (default) auto-detects from the host app's <html>/<body> dark class.
toolbar{ mode?: "sticky" | "static"; offset?: number; zIndex?: number }NoToolbar behaviour config. Defaults to { mode: "sticky", offset: 20, zIndex: 1000 }.
editor{ maxHeight?: string | number }NoContainer options. maxHeight only applies when toolbar.mode = "static" — caps the editable area's height.
defaultFontFamiliesstring[]NoOverride the font picker list. Defaults to the 24 fonts in defaultEditorConfig.defaultFontFamilies.
mentionUserListstring[]NoUsernames available to the @mentions autocomplete.
classNamestringNoClass on the outer wrapper element.

Code Examples

Basic Editor

components/BasicEditor.tsx
import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
import 'eddyter/style.css';

export default function BasicEditor() {
  return (
    <EditorProvider>
      <ConfigurableEditorWithAuth
        apiKey="your-api-key"
        onAuthSuccess={() => console.log('Ready!')}
      />
    </EditorProvider>
  );
}

State Management

components/EditorWithState.tsx
import { useState } from 'react';
import { ConfigurableEditorWithAuth, EditorProvider } from 'eddyter';
import 'eddyter/style.css';

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

  return (
    <EditorProvider>
      <ConfigurableEditorWithAuth
        apiKey="your-api-key"
        initialContent={content}
        onChange={setContent}
      />
    </EditorProvider>
  );
}

Layout Recipes

Editor Fit to Page

Create a Notion-style, edge-to-edge layout where the toolbar sticks to the top of the window and the word count floats in the bottom corner. The CSS hooks below are framework-agnostic — the same class names apply whether you mount via React, the SDK, or any wrapper.

Using containerClassName
<ConfigurableEditorWithAuth
  apiKey="your-api-key"
  toolbar={{ mode: "static" }}
  editor={{ maxHeight: "none" }}
  contentClassName="!bg-transparent !border-none"
  containerClassName="
    [&_.toolbar-content>.toolbar-features]:bg-transparent
    [&_.toolbar-content>.toolbar-comments-feature]:bg-transparent 
    [&_.toolbar-container]:border-b
    [&_.toolbar-container]:border-foreground/10
    [&_.toolbar-container]:bg-background
    [&_.toolbar-container]:sticky
    [&_.toolbar-container]:top-0
    [&_.toolbar-container]:z-50                          
    [&_.editor-area>div]:px-3                          
    [&_.editor-area>.wordCount]:fixed                          
    [&_.editor-area>.wordCount]:bottom-4
    [&_.editor-area>.wordCount]:right-16                      
  "
/>

Theming

FORCE THEME

If your application supports only one theme, use these CSS variables to avoid theme-related issues and ensure consistent rendering. These variables are framework-agnostic — they apply equally to React, SDK, Angular, Svelte, Vue, and Laravel integrations.

dark.css
.eddyter-scope {
        --cteditorf47ac10b-background: 240 6% 10%;
        --cteditorf47ac10b-foreground: 0 0% 100%;
        --cteditorf47ac10b-secondary: 240 7% 15%;
        --cteditorf47ac10b-main-secondary: 0 0% 16.1%;
        --cteditorf47ac10b-body: 240 10% 4%;
        --cteditorf47ac10b-card: 0 0% 14.9%;
        --cteditorf47ac10b-card-foreground: 0 0% 98%;
        --cteditorf47ac10b-popover: 0 0% 14.9%;
        --cteditorf47ac10b-popover-foreground: 0 0% 98%;
        --cteditorf47ac10b-primary: 0 0% 98%;
        --cteditorf47ac10b-primary-foreground: 0 0% 9%;
        --cteditorf47ac10b-secondary-foreground: 0 0% 98%;
        --cteditorf47ac10b-muted: 0 0% 14.9%;
        --cteditorf47ac10b-muted-foreground: 0 0% 63.9%;
        --cteditorf47ac10b-accent: 0 0% 14.9%;
        --cteditorf47ac10b-accent-foreground: 0 0% 98%;
        --cteditorf47ac10b-destructive: 0 62.8% 30.6%;
        --cteditorf47ac10b-destructive-foreground: 0 0% 98%;
        --cteditorf47ac10b-border: 0 0% 14.9%;
        --cteditorf47ac10b-input: 0 0% 14.9%;
        --cteditorf47ac10b-ring: 0 0% 83.1%;
        --cteditorf47ac10b-main-background: 0 0% 12.9%;
        --cteditorf47ac10b-grammar-tooltip-bg: 219 32% 12%;
        --cteditorf47ac10b-suggestion-bg:148 19% 18%;
        --cteditorf47ac10b-suggestion-border:144 100% 62%;
        --cteditorf47ac10b-suggestion-text:144 100% 62%;
        --cteditorf47ac10b-incorrect-word-bg:219 59% 7%;
        --cteditorf47ac10b-incorrect-word-border:4 100% 50%;
        --cteditorf47ac10b-incorrect-word-text:3 100% 80%;
        --cteditorf47ac10b-explanation-bg:230 35% 17%;
        --cteditorf47ac10b-explanation-border:221 83% 53%;
    }

Support & Resources

License

Eddyter is licensed under the MIT License. Security, privacy, and compliance are our core technical principles.

Build Better Together

Our documentation is constantly evolving. If you can't find what you're looking for, feel free to reach out.