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

@naoki85/visual-html-builder

v0.0.3

Published

A modern, TypeScript-based visual HTML builder with drag-and-drop functionality

Downloads

25

Readme

Visual HTML Builder

License: MIT GitHub Pages

A modern visual HTML builder for browsers. A lightweight TypeScript library that provides intuitive drag-and-drop HTML editing, complete style isolation, and real-time preview functionality.

🌐 Demo

Live Demo - Try the Visual HTML Builder in action

🚀 Quick Start

Installation

# Install via npm
npm install @naoki85/visual-html-builder

# Or via yarn
yarn add @naoki85/visual-html-builder

# Via CDN (ESM)
<script type="module">
  import VisualHtmlBuilder from "https://unpkg.com/@naoki85/visual-html-builder@latest/dist/index.js";
</script>

Development/Contribution Setup

# Clone the project
git clone https://github.com/naoki85/visual-html-builder.git
cd visual-html-builder

# Install dependencies
npm install

Basic Usage

import VisualHtmlBuilder from '@naoki85/visual-html-builder';

// Prepare HTML container
// <div id="editor-container"></div>

// Basic initialization
const editor = new VisualHtmlBuilder('editor-container', {
  theme: 'default',
  enabledElements: ['title', 'text', 'image', 'list'] // Default element types
});
editor.render();

CDN Usage (ESM)

<body>
  <div id="editor-container"></div>

  <script type="module">
    import VisualHtmlBuilder from "https://unpkg.com/@naoki85/visual-html-builder@latest/dist/index.js";
    
    const editor = new VisualHtmlBuilder('editor-container', {
      enabledElements: ['title', 'text', 'image', 'list']
    });
    editor.render();
  </script>
</body>

Advanced Configuration

// Full customization example
const editor = new VisualHtmlBuilder('editor-container', {
  theme: 'default',
  enabledElements: ['title', 'text', 'image', 'list'],
  iframePreviewOptions: {
    enableDragDrop: true,
    enableElementSelection: true,
    customStyles: `
      body { 
        font-family: 'Helvetica Neue', sans-serif;
        background: #f5f5f5; 
      }
    `
  },
  htmlTemplate: {
    doctype: '<!DOCTYPE html>',
    htmlAttributes: { lang: 'en' },
    head: {
      title: 'Generated Page',
      meta: [
        { charset: 'UTF-8' },
        { name: 'viewport', content: 'width=device-width, initial-scale=1.0' },
        { name: 'description', content: 'Generated by Visual HTML Builder' }
      ],
      links: [
        { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600&display=swap' }
      ]
    },
    bodyAttributes: { class: 'generated-content' }
  }
});

HTML Retrieval and API

// Body HTML only (existing API - maintains backward compatibility)
const bodyHTML = editor.getHTML();

// Complete HTML document (new API)
const fullHTML = editor.getFullHTML();

// Template management
const currentTemplate = editor.getHTMLTemplate();
editor.updateHTMLTemplate({
  head: {
    title: 'Updated Title'
  }
});

// Element management
const elements = editor.getElements();
editor.setElements([
  { id: 1, type: 'title', props: { text: 'Welcome!', level: 1 }}
]);

// Adding custom elements
editor.registerElementType('custom-button', {
  name: 'Button',
  icon: '🔲',
  defaultProps: { text: 'Click Me', color: '#007bff' },
  render: (props) => `<button style="background-color: ${props.color};">${props.text}</button>`,
  renderEditor: (props) => `
    <div class="property-group">
      <label>Button Text:</label>
      <input type="text" value="${props.text}" 
             class="property-input" data-property="text">
    </div>
    <div class="property-group">
      <label>Color:</label>
      <input type="color" value="${props.color}" 
             class="property-input" data-property="color">
    </div>
  `,
  validate: (props) => props.text.trim() ? null : 'Button text is required'
});

📦 Development

# Install dependencies
npm install

# Start development server
npm run dev

# Build
npm run build

# Linting
npm run lint

# Code formatting
npm run format

🧪 Testing

This project includes a comprehensive test suite using Vitest.

Running Tests

# Basic test run (watch mode)
npm run test

# One-shot execution
npm run test:run

# Open test UI
npm run test:ui

# Watch mode (monitor file changes)
npm run test:watch

# Generate coverage report
npm run test:coverage

Testing Tech Stack

  • Vitest 3.2.4: Fast test runner (Jest API compatible)
  • jsdom: DOM environment simulation
  • c8: Coverage report generation
  • TypeScript: Complete type safety

🏗️ Architecture

Core Class

  • VisualHtmlBuilder (src/VisualHtmlBuilder.ts): Main class (478 lines)
    • Handles DOM management, event processing, element selection, and preview updates
    • Combines various helpers to provide functionality

Helper Module Structure

src/helpers/
├── HTMLTemplateHelper.ts     # 279 lines - Complete HTML document generation
├── IframePreviewHelper.ts    # 443 lines - Style isolation preview functionality
├── ElementTypesHelper.ts     # 196 lines - HTML element type definition & management
├── StylesHelper.ts           # 580 lines - CSS management & injection
├── NotificationHelper.ts     # 266 lines - Notification system
├── DragDropHelper.ts         # 297 lines - Drag & drop functionality
└── UtilityHelpers.ts         # 29 lines - General utilities

🔧 Tech Stack

Development & Build

  • TypeScript 5.8.3: Strict type checking, ES2020 target
  • Vite 7.0.4: Fast build tool, ES Modules output
  • ESLint + Prettier: Code quality management, unified formatting

Testing

  • Vitest 3.2.4: Fast test runner, Jest API compatible
  • jsdom: DOM environment simulation
  • c8: Coverage report generation

Runtime Environment

  • Node.js: >=20.0.0
  • npm: >=8.0.0

📋 Use Cases

🎯 Intended Usage Scenarios

  • HTML Editor in Web Applications: CMS, blog editors, email creation features
  • Landing Page Builders: Code-free page creation tools
  • Educational Tools: Visual editors for HTML learning
  • Prototyping: Mockup creation for designers and planners

🛠️ Technical Features

  • Framework Agnostic: Can be combined with any framework like React, Vue, Angular
  • Modular Design: Select and use only the features you need
  • Type Safety: Complete type support with TypeScript
  • Lightweight: Minimal dependencies with 44kB lightweight implementation
  • Default Element Types: Includes 4 built-in element types: title, text, image, list

🧩 Custom Element Development

ElementType Interface

interface ElementType {
  name: string;                    // Display name
  icon: string;                    // Icon (emoji or text)
  defaultProps: Record<string, any>; // Default properties
  render: (props: Record<string, any>) => string;        // HTML generation
  renderEditor: (props: Record<string, any>, onChange?: (key: string, value: any) => void) => string; // Property editor
  validate: (props: Record<string, any>) => string | null; // Validation
}

Basic Custom Element Example

// Simple button element
editor.registerElementType('custom-button', {
  name: 'Button',
  icon: '🔲',
  defaultProps: { 
    text: 'Click Me', 
    color: '#007bff', 
    size: 'medium' 
  },
  render: (props) => 
    `<button style="background-color: ${props.color}; padding: ${props.size === 'large' ? '12px 24px' : '8px 16px'};">${props.text}</button>`,
  renderEditor: (props) => `
    <div class="property-group">
      <label>Button Text:</label>
      <input type="text" value="${props.text}" 
             class="property-input" data-property="text">
    </div>
    <div class="property-group">
      <label>Color:</label>
      <input type="color" value="${props.color}" 
             class="property-input" data-property="color">
    </div>
    <div class="property-group">
      <label>Size:</label>
      <select class="property-input" data-property="size">
        <option value="small" ${props.size === 'small' ? 'selected' : ''}>Small</option>
        <option value="medium" ${props.size === 'medium' ? 'selected' : ''}>Medium</option>
        <option value="large" ${props.size === 'large' ? 'selected' : ''}>Large</option>
      </select>
    </div>
  `,
  validate: (props) => props.text.trim() ? null : 'Button text is required'
});

Overriding Default Element Types

The built-in element types (title, text, image, list) can be completely overridden by registering custom elements with the same names. This allows you to customize the behavior and appearance of default elements while maintaining the same API.

// Override the default 'title' element
editor.registerElementType('title', {
  name: 'Custom Title',
  icon: '📝',
  defaultProps: { 
    text: 'Custom Title', 
    level: 1,
    color: '#007bff',
    alignment: 'left'
  },
  render: (props) => `
    <h${props.level} style="color: ${props.color}; text-align: ${props.alignment};">
      ${props.text}
    </h${props.level}>
  `,
  renderEditor: (props) => `
    <div class="property-group">
      <label>Title Text:</label>
      <input type="text" value="${props.text}" 
             class="property-input" data-property="text">
    </div>
    <div class="property-group">
      <label>Heading Level:</label>
      <select class="property-input" data-property="level" data-value-type="int">
        <option value="1" ${props.level === 1 ? 'selected' : ''}>H1</option>
        <option value="2" ${props.level === 2 ? 'selected' : ''}>H2</option>
        <option value="3" ${props.level === 3 ? 'selected' : ''}>H3</option>
        <option value="4" ${props.level === 4 ? 'selected' : ''}>H4</option>
        <option value="5" ${props.level === 5 ? 'selected' : ''}>H5</option>
        <option value="6" ${props.level === 6 ? 'selected' : ''}>H6</option>
      </select>
    </div>
    <div class="property-group">
      <label>Text Color:</label>
      <input type="color" value="${props.color}" 
             class="property-input" data-property="color">
    </div>
    <div class="property-group">
      <label>Alignment:</label>
      <select class="property-input" data-property="alignment">
        <option value="left" ${props.alignment === 'left' ? 'selected' : ''}>Left</option>
        <option value="center" ${props.alignment === 'center' ? 'selected' : ''}>Center</option>
        <option value="right" ${props.alignment === 'right' ? 'selected' : ''}>Right</option>
      </select>
    </div>
  `,
  validate: (props) => props.text.trim() ? null : 'Title text is required'
});

Required Event Handling Attributes

When creating custom elements with renderEditor, you must use specific class names and data attributes for proper event handling:

1. Required CSS Class

  • class="property-input": Essential for automatic event binding
  • This class is used by the library to detect input elements and attach change event listeners

2. Required Data Attributes

  • data-property="propertyName": Specifies which property this input controls
  • The property name must match a key in your element's defaultProps

3. Optional Type Conversion Attributes

  • data-value-type="int": Converts string values to integers
  • data-value-type="boolean": Converts checkbox states to boolean values
  • Without this attribute, values remain as strings

4. List Element Classes(for list-type elements)

  • list-editor - Container for list editing interface
  • list-item-editor - Wrapper for individual list item editing
  • list-item-input - Input field for list items (requires data-index attribute)
  • remove-list-item - Button to remove list items (requires data-index attribute)
  • add-list-item - Button to add new list items

5. List Element Data Atributes(for list-type elements)

  • data-index - Required for list item inputs and remove buttons

6. Other prepared CSS Classes

  • property-group - Recommended wrapper for styling property sections

Event Handling Examples

// ✅ Correct: All required attributes present
renderEditor: (props) => `
  <div class="property-group">
    <label>Text Input:</label>
    <input type="text" value="${props.text}" 
           class="property-input" data-property="text">
  </div>
  
  <div class="property-group">
    <label>Number Input:</label>
    <select class="property-input" data-property="level" data-value-type="int">
      <option value="1">Level 1</option>
      <option value="2">Level 2</option>
    </select>
  </div>
  
  <div class="property-group">
    <label>
      <input type="checkbox" ${props.enabled ? 'checked' : ''} 
             class="property-input" data-property="enabled" data-value-type="boolean">
      Enable Feature
    </label>
  </div>
`

// ✅ Correct: List element with all required classes
renderEditor: (props) => `
  <div class="property-group">
    <label>Items:</label>
    <div class="list-editor">
      ${props.items.map((item, index) => `
        <div class="list-item-editor">
          <input type="text" value="${item}" 
                 class="list-item-input" data-index="${index}">
          <button type="button" class="remove-list-item" data-index="${index}">×</button>
        </div>
      `).join('')}
      <button type="button" class="add-list-item">+ Add Item</button>
    </div>
  </div>
`

// ❌ Incorrect: Missing required attributes
renderEditor: (props) => `
  <input type="text" value="${props.text}">  <!-- Missing class and data-property -->
  <select>  <!-- Missing class and data-property -->
    <option value="1">Level 1</option>
  </select>
`

Usage Notes

  1. Security: Prevent XSS with UtilityHelpers.escapeHtml()
  2. Element Activation: Need to add custom element name to enabledElements array
  3. Event Handling: Always use class="property-input" and data-property attributes
  4. Type Safety: Use data-value-type for automatic type conversion
  5. Validation: Input validation with appropriate error messages
  6. Accessibility: Consider HTML structure and aria attributes

📄 License

MIT License - See LICENSE file for details


📧 Contact