A smooth, modern WYSIWYG text editor with a clean interface, elegant typography, and a calming editing experience.
- π¨ Beautiful Design: Clean, calming interface with elegant typography
- π Rich Text Editing: Full WYSIWYG editor with formatting toolbar
- β‘ Modern Stack: Built with React 18, TypeScript, and Vite
- π§ Fully Typed: Strict TypeScript configuration for better development experience
- π± Responsive: Works seamlessly across desktop, tablet, and mobile devices
- π Dark Mode: Built-in light and dark theme support
- βΏ Accessible: WCAG compliant with proper ARIA attributes
- βοΈ Configurable: Extensive customization options
- π Undo/Redo: Full history management with keyboard shortcuts
- β¨οΈ Keyboard Shortcuts: Full support for common formatting shortcuts
- π οΈ Extensible Toolbar: Customizable toolbar with rich formatting options
- π Plugin System: Powerful extension system with built-in plugins
- π― Framework Agnostic: Designed to work with multiple frameworks (React first, more coming)
npm install lilac-editor
# or
yarn add lilac-editor
# or
pnpm add lilac-editorimport React from 'react';
import { Editor } from 'lilac-editor';
function MyApp() {
const [content, setContent] = useState('');
return (
<Editor
initialContent="<h1>Welcome to Lilac!</h1><p>Start typing...</p>"
placeholder="Enter your text here..."
onChange={setContent}
theme="light"
autoFocus
toolbar={{
show: true,
tools: ['bold', 'italic', 'underline', 'separator', 'heading1', 'heading2']
}}
/>
);
}import React from 'react';
import { Editor, wordCountPlugin, emojiPlugin, tablePlugin } from 'lilac-editor';
function MyApp() {
const [content, setContent] = useState('');
return (
<Editor
initialContent="<h1>Welcome to Lilac!</h1><p>Start typing...</p>"
onChange={setContent}
plugins={[
wordCountPlugin, // Document statistics panel
emojiPlugin, // Emoji picker with Ctrl+Shift+E
tablePlugin, // Table inserter with Ctrl+Shift+T
]}
toolbar={{ show: true }}
/>
);
}import React, { useRef } from 'react';
import { Editor, type EditorRef } from 'lilac-editor';
function MyApp() {
const editorRef = useRef<EditorRef>(null);
const handleUndo = () => {
editorRef.current?.undo();
};
const handleRedo = () => {
editorRef.current?.redo();
};
const getContent = () => {
const content = editorRef.current?.getContent();
console.log('Current content:', content);
};
return (
<div>
<div>
<button onClick={handleUndo}>Undo</button>
<button onClick={handleRedo}>Redo</button>
<button onClick={getContent}>Get Content</button>
</div>
<Editor
ref={editorRef}
initialContent="Welcome to Lilac Editor!"
maxLength={5000}
toolbar={{ show: true }}
/>
</div>
);
}import React from 'react';
import { Editor } from 'lilac-editor';
function MyApp() {
return (
<Editor
initialContent="<h1>Welcome to Lilac!</h1><p>This editor has custom height settings...</p>"
placeholder="Start typing..."
minHeight={300}
maxHeight={500}
toolbar={{ show: true }}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
initialContent |
string |
'' |
Initial content of the editor (HTML for rich text) |
placeholder |
string |
'Start writing...' |
Placeholder text when editor is empty |
readOnly |
boolean |
false |
Whether the editor is read-only |
autoFocus |
boolean |
false |
Auto-focus editor on mount |
maxLength |
number |
undefined |
Maximum character limit (plain text mode only) |
minHeight |
number |
200 |
Minimum height of editor content area in pixels |
maxHeight |
number |
600 |
Maximum height of editor content area in pixels (enables scrolling) |
theme |
'light' | 'dark' | 'auto' |
'light' |
Editor theme |
className |
string |
undefined |
Additional CSS class |
style |
CSSProperties |
undefined |
Inline styles |
onChange |
(content: string) => void |
undefined |
Content change callback |
onSelectionChange |
(selection: SelectionRange | null) => void |
undefined |
Selection change callback |
onFocus |
() => void |
undefined |
Focus event callback |
onBlur |
() => void |
undefined |
Blur event callback |
toolbar |
ToolbarConfig |
undefined |
Toolbar configuration |
plugins |
EditorPlugin[] |
[] |
Array of plugins to install |
| Prop | Type | Default | Description |
|---|---|---|---|
show |
boolean |
false |
Whether to show the toolbar |
tools |
ToolbarTool[] |
Default tools | Array of toolbar tools to display |
position |
'top' | 'bottom' | 'floating' |
'top' |
Toolbar position (top only for now) |
| Tool | Description | Keyboard Shortcut |
|---|---|---|
'bold' |
Bold text formatting | Ctrl/Cmd + B |
'italic' |
Italic text formatting | Ctrl/Cmd + I |
'underline' |
Underline text formatting | Ctrl/Cmd + U |
'strikethrough' |
Strikethrough text formatting | - |
'heading1' |
Heading 1 format | - |
'heading2' |
Heading 2 format | - |
'heading3' |
Heading 3 format | - |
'paragraph' |
Paragraph format | - |
'bulletList' |
Bullet list | - |
'orderedList' |
Numbered list | - |
'blockquote' |
Block quote | - |
'codeBlock' |
Code block | - |
'link' |
Insert/edit link | Ctrl/Cmd + K |
'image' |
Insert image | - |
'separator' |
Visual separator | - |
<Editor
toolbar={{
show: true,
tools: [
'bold',
'italic',
'underline',
'separator',
'heading1',
'heading2',
'heading3',
'paragraph',
'separator',
'bulletList',
'orderedList',
'blockquote',
'separator',
'link',
'codeBlock'
]
}}
/>| Method | Type | Description |
|---|---|---|
getContent() |
() => string |
Get current editor content |
setContent(content) |
(content: string) => void |
Set editor content |
focus() |
() => void |
Focus the editor |
blur() |
() => void |
Blur the editor |
undo() |
() => void |
Undo last change |
redo() |
() => void |
Redo last undone change |
canUndo |
boolean |
Whether undo is available |
canRedo |
boolean |
Whether redo is available |
Lilac features a powerful plugin system that allows you to extend the editor with custom functionality. The system supports toolbar buttons, side panels, keyboard shortcuts, content transformers, and more.
Displays real-time document statistics in a side panel.
import { wordCountPlugin } from 'lilac-editor';
<Editor plugins={[wordCountPlugin]} />Features:
- Words, characters, paragraphs count
- Real-time updates
- Right sidebar panel
- Clean, organized display
Add emojis to your content with an easy-to-use picker.
import { emojiPlugin } from 'lilac-editor';
<Editor plugins={[emojiPlugin]} />Features:
- Categorized emoji selection (Smileys, Nature, Food, Travel, Objects)
- Toolbar button integration
- Keyboard shortcut:
Ctrl+Shift+E - Modal interface with search
Insert and customize HTML tables with an interactive dialog.
import { tablePlugin } from 'lilac-editor';
<Editor plugins={[tablePlugin]} />Features:
- Configurable rows and columns
- Header row option
- Border toggle
- Live preview
- Keyboard shortcut:
Ctrl+Shift+T
Create your own plugins by implementing the EditorPlugin interface:
import { EditorPlugin, EditorContext } from 'lilac-editor';
import { MyIcon } from 'lucide-react';
export const myCustomPlugin: EditorPlugin = {
id: 'my-custom-plugin',
name: 'My Custom Plugin',
version: '1.0.0',
description: 'A custom plugin that does amazing things',
// Add toolbar buttons
toolbarButtons: [
{
id: 'my-button',
icon: <MyIcon size={16} />,
label: 'My Tool',
tooltip: 'My custom tool (Ctrl+M)',
onClick: (context: EditorContext) => {
context.insertContent('<strong>Custom content!</strong>');
},
},
],
// Add keyboard shortcuts
keyboardShortcuts: [
{
key: 'm',
ctrlKey: true,
action: (context: EditorContext) => {
context.insertContent('<em>Shortcut triggered!</em>');
},
},
],
// Add custom styles
styles: `
.my-custom-styles {
color: #ff6b6b;
font-weight: bold;
}
`,
// Lifecycle hooks
onInstall: (context) => console.log('Plugin installed'),
onContentChange: (content, context) => {
// React to content changes
},
};| Feature | Description |
|---|---|
| Toolbar Buttons | Add custom formatting tools and actions |
| Side Panels | Create custom UI panels (left, right, bottom) |
| Keyboard Shortcuts | Define custom hotkey combinations |
| Content Transformers | Process and transform content automatically |
| Lifecycle Hooks | React to editor events (mount, unmount, content changes) |
| Custom Styles | Inject CSS for plugin-specific styling |
| Context Menu | Add right-click menu items |
Access the plugin manager for programmatic control:
import { pluginManager } from 'lilac-editor';
// Install a plugin
pluginManager.install(myCustomPlugin);
// Check if installed
if (pluginManager.isInstalled('my-plugin-id')) {
console.log('Plugin is active');
}
// Get all plugins
const allPlugins = pluginManager.getAllPlugins();
// Uninstall a plugin
pluginManager.uninstall('my-plugin-id');// Plugin system
import {
PluginManager,
pluginManager,
EditorPlugin,
EditorContext
} from 'lilac-editor';
// Built-in plugins
import {
wordCountPlugin,
emojiPlugin,
tablePlugin
} from 'lilac-editor';- Node.js 18+, recommended 20+ LTS
- npm, yarn, or pnpm
# Clone the repository
git clone https://github.com/maifeeulasad/lilac.git
cd lilac
# Install dependencies
npm install
# Start development server
npm run dev
# Build for production
npm run build
# Run tests
npm test
# Lint code
npm run lint
# Type check
npm run typechecksrc/
βββ components/ # React components
β βββ Editor/ # Main editor component
β βββ Toolbar/ # Toolbar component
βββ hooks/ # Custom React hooks
βββ plugins/ # Plugin system and built-in plugins
β βββ PluginManager.ts # Plugin manager
β βββ wordCount.tsx # Word count plugin
β βββ emojiPicker.tsx # Emoji picker plugin
β βββ tableInserter.tsx # Table inserter plugin
β βββ index.ts # Plugin exports
βββ types/ # TypeScript type definitions
β βββ editor.ts # Core editor types
β βββ plugin.ts # Plugin system types
β βββ index.ts # Type exports
βββ utils/ # Utility functions
βββ App.tsx # Demo application
βββ main.tsx # Entry point
βββ index.ts # Library exports
Lilac comes with built-in light and dark themes. You can also create custom themes by overriding CSS custom properties:
.lilac-editor {
--lilac-color-primary: #your-color;
--lilac-color-background: #your-bg;
--lilac-border-radius: 8px;
/* ... other variables */
}The editor uses CSS custom properties for easy theming. All styles are scoped to prevent conflicts with your application.
- π§ Rich text toolbar (Bold, Italic, Underline, etc.)
- π Emoji support (via Emoji Picker plugin)
- π Copy/Paste enhancements
- π Link insertion and management
- πΌοΈ Image upload and embedding
- π Markdown support
- π Find and replace
- π Table support
- π― Vue.js integration
-
π °οΈ Angular integration - π Plugin system
- π± Mobile optimizations
- π¨ More themes
- π§ͺ Comprehensive test suite
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for your changes
- Ensure all tests pass (
npm test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
- Inspired by modern text editors like Notion, Linear, and GitHub
- Built with love using React, TypeScript, and modern web technologies
- Thanks to all contributors and the open-source community
Made with πΈ by maifeeulasad
β Star us on GitHub if you find this project useful!
