npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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 content property 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 NodeJS namespace resolution errors by properly configuring Node.js types
  • ✅ Resolved replaceAll method 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 .svelte file 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-dom

Then 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 svelte

Then 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 element
  • options: 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 build

The 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 module
  • dist/svelte.esm.js - Svelte wrapper module
  • dist/editor.css - Compiled styles
  • dist/*.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