vanilla-wysiwyg-editor
v1.1.9
Published
A lightweight, modern, and highly customizable What You See Is What You Get (WYSIWYG) editor built with vanilla TypeScript. It's dependency-free and easily integrates into any JavaScript project, with optional React and Svelte wrappers for modern framewor
Readme
🍦 Vanilla WYSIWYG Editor
A lightweight, modern, and highly customizable What You See Is What You Get (WYSIWYG) editor built with vanilla TypeScript. It's dependency-free and easily integrates into any JavaScript project, with optional React and Svelte wrappers for modern frameworks.
🆕 Recent Fixes & Improvements
Build System Enhancements (v1.1.7)
- ✅ Convert markdown to html able assign heading id
Build System Enhancements (v1.1.6)
- ✅ Wrapper passing parameter load file
Build System Enhancements (v1.1.5)
- ✅ Fixed react integration
Build System Enhancements (v1.1.4)
Cross-Platform Build Compatibility:
- ✅ Replaced Windows-specific copy commands with cross-platform Node.js solutions
- ✅ Enhanced build process to work seamlessly on Windows, macOS, and Linux
- ✅ Improved npm publish compatibility for all operating systems
- ✅ Added automated TypeScript declaration copying during build process
Svelte TypeScript Declaration Fixes:
- ✅ Fixed missing TypeScript declarations for Svelte wrapper component
- ✅ Resolved "Object literal may only specify known properties" errors
- ✅ Properly implemented
contentproperty and all WysiwygEditorOptions in Svelte component - ✅ Created comprehensive TypeScript definitions with proper export structure
- ✅ Enhanced IDE support with accurate type checking for Svelte integration
TypeScript Configuration Fixes:
- ✅ Fixed
NodeJSnamespace resolution errors by properly configuring Node.js types - ✅ Resolved
replaceAllmethod compatibility by updating TypeScript target to ES2021 - ✅ Created dedicated TypeScript configuration for Svelte builds (
tsconfig.svelte.json) - ✅ Eliminated TypeScript declaration conflicts in wrapper builds
Rollup Build Improvements:
- ✅ Fixed Svelte component compilation by reordering plugin execution
- ✅ Resolved "Cannot find module" errors for
.sveltefile imports - ✅ Eliminated "mixing named and default exports" warnings in UMD builds
- ✅ Updated all build targets to ES2021 for modern JavaScript feature support
- ✅ Optimized plugin configurations for better compatibility across all wrappers
Module Resolution Enhancements:
- ✅ Proper handling of Svelte component imports in TypeScript
- ✅ Improved external dependency handling for framework wrappers
- ✅ Better source map generation across all build outputs
- ✅ Enhanced CSS processing and injection for wrapper components
Development Experience:
- ✅ Clean builds without TypeScript errors or warnings
- ✅ Consistent targeting across UMD, ESM, React, and Svelte builds
- ✅ Improved error messages and build feedback
- ✅ Better IDE support with proper type declarations
- ✅ Cross-platform development and deployment workflows
These improvements ensure reliable builds across all supported environments and eliminate common development issues when integrating the editor into TypeScript projects or modern build systems.
✨ Features
- Full Formatting Suite: Bold, italic, underline, alignment, headings, quotes, and colors.
- Advanced Inserts: Links, images, tables, code blocks, and horizontal rules.
- Powerful Table Tools: Contextual toolbar for adding/removing rows and columns.
- Smart Toolbar: Highlights active formatting based on the current cursor position.
- File Loading: Load HTML, Markdown, or plain text files during initialization.
- Enhanced Converter: Improved HTML ↔ Markdown conversion with better support for tables and formatting.
- Framework Wrappers: Optional React and Svelte components with proper TypeScript support.
- Highly Customizable:
- Enable or disable any toolbar button or group.
- Provide initial content or load from files when initializing the editor.
- Dark mode support with smooth transitions.
- View mode for read-only content display.
- Modern & Lightweight: Written in vanilla TypeScript with zero runtime dependencies for the core editor.
- Keyboard Shortcuts: Full support for common editing shortcuts.
- Content Export: Save as HTML or Markdown formats with improved conversion accuracy.
- Image Viewer: Built-in image viewer with navigation for galleries.
🚀 Installation
Install using npm:
npm install vanilla-wysiwyg-editor📦 Module Structure
The package is organized into separate modules to avoid unnecessary dependencies:
- Core Editor:
vanilla-wysiwyg-editor- The main vanilla TypeScript editor with zero dependencies - React Wrapper:
vanilla-wysiwyg-editor/react- React component wrapper (requires React as peer dependency) - Svelte Wrapper:
vanilla-wysiwyg-editor/svelte- Svelte component wrapper (requires Svelte as peer dependency)
This structure ensures you only import what you need and don't get framework-related errors if you're not using a particular framework.
📄 Usage
Vanilla JavaScript/TypeScript
ES Modules
import { WysiwygEditor } from 'vanilla-wysiwyg-editor';
import 'vanilla-wysiwyg-editor/dist/editor.css';
const editor = new WysiwygEditor('#editor', {
content: '<h1>Hello, World!</h1><p>This is the initial content.</p>',
isDarkMode: false
});UMD (Browser)
<!DOCTYPE html>
<html>
<head>
<title>Vanilla WYSIWYG Editor</title>
<link rel="stylesheet" href="node_modules/vanilla-wysiwyg-editor/dist/editor.css" />
</head>
<body>
<div id="editor"></div>
<script src="node_modules/vanilla-wysiwyg-editor/dist/wysiwyg-editor.umd.js"></script>
<script>
const editor = new WysiwygEditor.WysiwygEditor('#editor', {
content: '<h1>Hello, World!</h1><p>This is the initial content.</p>'
});
</script>
</body>
</html>React Integration
First, install React as a peer dependency if you haven't already:
npm install react react-domThen use the React wrapper:
// src/MyEditorComponent.tsx
import React, { useRef } from "react";
import { ReactWysiwygEditor } from 'vanilla-wysiwyg-editor/react';
export default function MyEditorComponent() {
const editorRef = useRef(null);
const handleContentChange = (content) => {
console.log('Content changed:', content);
};
const getContent = () => {
if (editorRef.current) {
console.log('Current content:', editorRef.current.getContent());
}
};
return (
<>
<button onClick={getContent}>Get Content</button>
<ReactWysiwygEditor
ref={editorRef}
content="<h1>Hello from React!</h1>"
isDarkMode={false}
viewMode={false}
onContentChange={handleContentChange}
toolbar={{
exclude: ['meta', 'headings']
}}
/>
</>
);
}Svelte Integration
First, install Svelte as a peer dependency if you haven't already:
npm install svelteThen use the Svelte wrapper:
<!-- src/MyEditorComponent.svelte -->
<script lang="ts">
import { SvelteWysiwygEditor } from 'vanilla-wysiwyg-editor/svelte';
let editorComponent: SvelteWysiwygEditor;
let content = '<h1>Hello from Svelte!</h1><p>Start editing...</p>';
function handleContentChange(newContent: string) {
content = newContent;
console.log('Content changed:', newContent);
}
function handleFocus() {
console.log('Editor focused');
}
function handleBlur() {
console.log('Editor blurred');
}
function getContent() {
if (editorComponent) {
console.log('Current content:', editorComponent.getContent());
}
}
</script>
<div>
<h2>My Svelte Editor</h2>
<button on:click={getContent}>Get Content</button>
<SvelteWysiwygEditor
bind:this={editorComponent}
{content}
isDarkMode={false}
viewMode={false}
onContentChange={handleContentChange}
onFocus={handleFocus}
onBlur={handleBlur}
toolbar={{
exclude: ['meta', 'headings']
}}
/>
</div>⚙️ Configuration Options
The following options can be passed to the WysiwygEditor constructor, ReactWysiwygEditor component, and SvelteWysiwygEditor component.
| Option | Type | Description |
| :------------- | :--------------- | :------------------------------------------------------------------------------------------------------------------- |
| content | string | The initial HTML content to load into the editor. Default: '<p><br></p>' |
| loadFile | File | A File object to load into the editor on initialization. Supports HTML (.html), Markdown (.md), and plain text (.txt) files. If both content and loadFile are provided, loadFile takes precedence. |
| isDarkMode | boolean | If true, the editor will initialize with the dark theme enabled. Default: false |
| viewMode | boolean | If true, the editor will initialize in a non-editable "view" mode. The toolbar is hidden, and the content cannot be modified. Default: false |
| toolbar | ToolbarOptions | Configuration for the editor's toolbar. See below for details. |
ToolbarOptions
| Option | Type | Description |
| :-------- | :--------- | :------------------------------------------------------------------------------------------------------------------------ |
| exclude | string[] | An array of toolbar group names or individual button names to exclude from the toolbar. |
| | | Available Groups: 'history', 'styles', 'spacing', 'color', 'alignment', 'indent', 'actions', 'meta', 'headings'. |
| | | Available Buttons: 'bold', 'italic', 'underline', 'strikethrough', 'fontName', 'fontSize', 'foreColor', 'backColor', 'superscript', 'subscript', 'setLineHeight', 'justifyLeft', 'justifyCenter', 'justifyRight', 'justifyFull', 'outdent', 'indent', 'createLink', 'insertImage', 'insertTable', 'insertCodeBlock', 'inlineCode', 'selectAll', 'unselectAll', 'showBlocks', 'assignHeadingId', 'save', 'load', 'find', 'source', 'toggleFullscreen', 'toggleTheme'. |
🔄 Enhanced File Loading & Conversion
The editor now includes enhanced file loading capabilities and improved HTML ↔ Markdown conversion:
File Loading on Initialization
You can now load files directly when creating the editor:
// Create a File object from user input or fetch
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
const editor = new WysiwygEditor('#editor', {
loadFile: file, // Automatically loads and converts the file
isDarkMode: false
});
});
// Or load from a URL
async function loadFromUrl() {
const response = await fetch('./my-document.md');
const blob = await response.blob();
const file = new File([blob], 'my-document.md', { type: 'text/markdown' });
const editor = new WysiwygEditor('#editor', {
loadFile: file
});
}Supported File Types
- HTML files (.html): Loaded as-is into the editor
- Markdown files (.md): Automatically converted to HTML with enhanced parsing
- Plain text files (.txt): Converted to HTML with paragraph formatting and line break preservation
Improved Converter Features
The HTML ↔ Markdown converter has been significantly enhanced:
Enhanced Markdown to HTML Conversion
- Better Code Block Handling: Preserves whitespace and syntax highlighting
- Improved Table Support: Proper header detection and formatting
- Better Paragraph Handling: Converts single newlines to
<br>tags for better formatting - HTML Entity Safety: Preserves special formatting tags like
<u>,<sup>,<sub>
Enhanced HTML to Markdown Conversion
- Table Export: Converts HTML tables to proper Markdown table format
- Image Support: Handles images with titles and alt text
- Mixed Content: Preserves HTML tags for formatting not supported in Markdown (underline, superscript, subscript)
Usage Examples
// Example: Loading a markdown file with enhanced conversion
async function loadMarkdownFile() {
const file = await fetch('./demo/assets/complete-markdown-guide.md')
.then(r => r.blob())
.then(blob => new File([blob], 'guide.md'));
const editor = new WysiwygEditor('#editor', {
content: '<h1>Initial content</h1>', // This will be overridden
loadFile: file, // This takes precedence
toolbar: {
exclude: ['meta'] // Hide metadata buttons if desired
}
});
}
// Example: Better markdown export with enhanced converter
function exportAsMarkdown() {
const content = editor.getContent();
const markdown = convertHtmlToMarkdown(content);
// The converter now handles:
// - Tables with proper headers
// - Code blocks with language preservation
// - Images with titles and alt text
// - Mixed HTML/Markdown content
const blob = new Blob([markdown], { type: 'text/markdown' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'document.md';
a.click();
}🔧 API Reference
WysiwygEditor Class
Constructor
new WysiwygEditor(selector: string, options?: WysiwygEditorOptions)selector: CSS selector for the container elementoptions: Configuration options (see Configuration Options above)
Public Methods
| Method | Description | Return Type |
|--------|-------------|-------------|
| getContent() | Gets the current HTML content of the editor | string |
| setContent(html: string) | Sets the content of the editor | void |
| getTextContent() | Gets the plain text content without HTML tags | string |
| getWordCount() | Gets the word count of the editor content | number |
| getCharacterCount() | Gets the character count of the editor content | number |
| isEmpty() | Checks if the editor content is empty | boolean |
| focus() | Focuses the editor content area | void |
| destroy() | Destroys the editor instance and cleans up event listeners | void |
React Component
ReactWysiwygEditor Props
Extends all WysiwygEditorOptions plus:
| Prop | Type | Description |
|------|------|-------------|
| className | string | CSS class name for the editor container |
| onContentChange | (content: string) => void | Callback fired when content changes |
ReactWysiwygEditor Ref Methods
Access these methods using a ref:
const editorRef = useRef<ReactWysiwygEditorRef>(null);
// Then use: editorRef.current?.getContent()| Method | Description | Return Type |
|--------|-------------|-------------|
| getEditor() | Gets the underlying WysiwygEditor instance | WysiwygEditor \| null |
| getContent() | Gets the current content | string |
| setContent(content: string) | Sets the content | void |
| focus() | Focuses the editor | void |
| destroy() | Destroys the editor | void |
Svelte Component
SvelteWysiwygEditor Props
Extends all WysiwygEditorOptions plus:
| Prop | Type | Description |
|------|------|-------------|
| class | string | CSS class name for the editor container |
| onContentChange | (content: string) => void | Callback fired when content changes |
| onFocus | () => void | Callback fired when editor gains focus |
| onBlur | () => void | Callback fired when editor loses focus |
SvelteWysiwygEditor Methods
Access these methods using component binding:
<script>
let editorComponent;
</script>
<SvelteWysiwygEditor bind:this={editorComponent} />
<!-- Then use: editorComponent.getContent() -->| Method | Description | Return Type |
|--------|-------------|-------------|
| getEditor() | Gets the underlying WysiwygEditor instance | WysiwygEditor \| null |
| getContent() | Gets the current content | string |
| setContent(content: string) | Sets the content | void |
| focus() | Focuses the editor | void |
| destroy() | Destroys the editor | void |
📋 TypeScript Support
The package includes full TypeScript definitions for all modules:
// Core types are available from the main module
import type { WysiwygEditorOptions, ToolbarOptions } from 'vanilla-wysiwyg-editor';
// React-specific types
import type { ReactWysiwygEditorProps, ReactWysiwygEditorRef } from 'vanilla-wysiwyg-editor/react';
// Svelte types are included in the component🚫 Avoiding Module Resolution Errors
The new module structure prevents the common "Failed to resolve module specifier" errors:
- Core Editor: Only imports what it needs, no framework dependencies
- React Wrapper: Separate module that only loads when explicitly imported
- Svelte Wrapper: Separate module that only loads when explicitly imported
⌨️ Keyboard Shortcuts
The editor supports the following keyboard shortcuts:
| Shortcut | Action |
|----------|--------|
| Ctrl/Cmd + B | Bold |
| Ctrl/Cmd + I | Italic |
| Ctrl/Cmd + U | Underline |
| Ctrl/Cmd + K | Insert Link |
| Ctrl/Cmd + S | Save Content |
| Ctrl/Cmd + F | Find and Replace |
| Ctrl/Cmd + A | Select All |
| Ctrl/Cmd + Z | Undo |
| Ctrl/Cmd + Y | Redo |
| Ctrl/Cmd + Shift + Z | Redo (alternative) |
| Ctrl/Cmd + \`` | Toggle Inline Code |
| Tab| Indent |
|Shift + Tab| Outdent |
|Enter` | New Line (smart behavior in headings) |
🎨 Styling and Themes
The editor comes with built-in light and dark themes. You can toggle between themes programmatically or let users switch using the theme toggle button.
Custom Styling
You can customize the editor's appearance by overriding CSS custom properties:
.vanilla-editor-container {
--text-primary: #333;
--text-secondary: #666;
--background-primary: #fff;
--background-secondary: #f8f9fa;
--border-primary: #e9ecef;
--border-active: #007bff;
--accent-color: #007bff;
}
.vanilla-editor-container.dark-theme {
--text-primary: #e9ecef;
--text-secondary: #adb5bd;
--background-primary: #212529;
--background-secondary: #343a40;
--border-primary: #495057;
--border-active: #0d6efd;
--accent-color: #0d6efd;
}🔧 Advanced Features
File Loading and Conversion
- Multi-format Support: Load HTML, Markdown, or plain text files
- Automatic Conversion: Files are automatically converted to the appropriate format
- Initialization Loading: Load files during editor creation for seamless setup
- Error Handling: Graceful fallbacks when file loading fails
Enhanced Content Conversion
- Bidirectional Conversion: Improved HTML ↔ Markdown conversion accuracy
- Table Support: Full table conversion in both directions with header detection
- Code Block Preservation: Maintains syntax highlighting and whitespace
- Mixed Content: Handles HTML elements within Markdown content
Image Handling
- Click to Select: Click on any image to show the contextual toolbar
- Image Viewer: Click images in view mode to open the full-screen viewer
- Gallery Navigation: Navigate between multiple images using arrow buttons
Table Management
- Contextual Toolbar: Appears when cursor is in a table cell
- Dynamic Rows/Columns: Add or remove rows and columns on the fly
- Smart Navigation: Use Tab to move between cells
Code Blocks
- Syntax Highlighting: Specify language for proper highlighting
- Copy Button: Automatic copy-to-clipboard functionality
- Inline Code: Use backticks or Ctrl+` for inline code
Content Export
- HTML Export: Save content as HTML file with heading metadata
- Markdown Export: Convert and save as Markdown format
- Heading IDs: Assign IDs to headings for anchor links
🏗️ Building from Source
# Clone the repository
git clone <repository-url>
cd vanilla-wysiwyg-editor
# Install dependencies
npm install
# Build the project
npm run buildThe build process generates:
dist/wysiwyg-editor.umd.js- UMD build for browsers (vanilla only)dist/wysiwyg-editor.esm.js- ES modules build (vanilla only)dist/react.esm.js- React wrapper moduledist/svelte.esm.js- Svelte wrapper moduledist/editor.css- Compiled stylesdist/*.d.ts- TypeScript declarations for all modules
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
📝 License
This project is licensed under the ISC License - see the LICENSE file for details.
🙏 Acknowledgments
- Built with modern web standards and TypeScript
- Inspired by the need for a lightweight, dependency-free rich text editor
- Designed for modern frameworks while maintaining vanilla JS compatibility
Made with ❤️ by the Vanilla WYSIWYG Editor team
