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

@carlosvidalperu/mtbuilder

v1.0.0

Published

Lightweight drag-and-drop page builder web component

Readme

MT Builder

A lightweight, zero-dependency page builder using Web Components. Drag-and-drop interface for building responsive web pages. Works with any backend.

Live Demo | Examples

Features

  • Zero dependencies — No React, no Vue, no jQuery. Under 35KB gzipped.
  • Web Components — Standard Custom Elements with Shadow DOM. Works anywhere.
  • Two usage modes — Standalone <page-builder> or full <page-manager> with page listing.
  • Event-driven APIbuilder:ready, builder:save, builder:content-changed CustomEvents.
  • i18n built-in — English, Spanish and French. Set via lang attribute.
  • XSS sanitized — Built-in HTML sanitizer for user content.
  • Undo/Redo — Full history stack with keyboard support.
  • Responsive preview — Desktop, tablet and mobile preview modes.
  • 10 element types — Heading, text, image, button, table, list, video, divider, spacer, HTML.

Quick Start

Installation

git clone https://github.com/carlosvidal/mtbuilder.git
cd mtbuilder
npm install
npm run dev

Standalone Builder (recommended for integration)

Use <page-builder> when your application manages pages externally (PHP, Rails, Django, etc.):

<page-builder page-id="42" lang="es"></page-builder>

<script type="module">
  import "./dist/page-builder.js";

  const builder = document.querySelector("page-builder");

  // Load data when the builder is ready
  builder.addEventListener("builder:ready", () => {
    builder.setPageData(pageDataFromServer);
  });

  // Handle save
  builder.addEventListener("builder:save", async (e) => {
    await fetch(`/api/pages/${e.detail.pageId}`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(e.detail.data),
    });
  });

  // Optional: listen for content changes (autosave)
  builder.addEventListener("builder:content-changed", (e) => {
    console.log("Content changed:", e.detail.data);
  });
</script>

Page Manager (built-in page management)

Use <page-manager> for a complete solution with page listing and localStorage:

<page-manager lang="es"></page-manager>
<script type="module" src="./dist/page-builder.js"></script>

With backend API:

<page-manager
  lang="en"
  api-endpoint="https://api.example.com/pages"
  api-key="your-key"
  mode="hybrid"
></page-manager>

API Reference

<page-builder> Attributes

| Attribute | Description | Default | |-----------|-------------|---------| | page-id | Page identifier | "" | | lang | Language (en, es, fr) | browser default | | mode | "external" (events only) or "local" (localStorage) | "external" |

<page-builder> Methods

| Method | Description | |--------|-------------| | setPageData(data) | Load { rows, globalSettings } into the editor | | getPageData() | Returns current { rows, globalSettings } | | save() | Dispatches builder:save event with current data |

<page-builder> Events

All events use CustomEvent with bubbles: true, composed: true.

| Event | Detail | When | |-------|--------|------| | builder:ready | { pageId } | Editor is ready to receive data | | builder:save | { pageId, data } | User clicked Save or save() was called | | builder:content-changed | { pageId, data } | Content was modified in the editor |

<page-manager> Attributes

| Attribute | Description | Default | |-----------|-------------|---------| | lang | Language (en, es, fr) | browser default | | api-endpoint | Backend API URL | — (uses localStorage) | | api-key | API authentication key | — | | mode | "local", "api", "hybrid" | "local" |

Data Format

{
  "rows": [
    {
      "id": "row-1",
      "type": "row-2",
      "columns": [
        {
          "id": "col-1",
          "elements": [
            {
              "id": "el-1",
              "type": "heading",
              "content": "Hello World",
              "tagName": "h1",
              "styles": {}
            }
          ]
        }
      ],
      "styles": {}
    }
  ],
  "globalSettings": {
    "maxWidth": "1200px",
    "padding": "20px",
    "backgroundColor": "#ffffff",
    "fontFamily": "system-ui, -apple-system, sans-serif"
  }
}

API Endpoints (for backend integration)

GET    /api/pages      — List all pages
POST   /api/pages      — Create new page
GET    /api/pages/{id} — Get specific page
PUT    /api/pages/{id} — Update page
DELETE /api/pages/{id} — Delete page

Framework Integration

Since MT Builder uses standard Web Components, it works with any framework.

React

import { useRef, useEffect } from "react";
import "./dist/page-builder.js";

function PageEditor({ pageId, pageData, onSave }) {
  const ref = useRef(null);

  useEffect(() => {
    const builder = ref.current;
    const handleReady = () => builder.setPageData(pageData);
    const handleSave = (e) => onSave(e.detail.data);

    builder.addEventListener("builder:ready", handleReady);
    builder.addEventListener("builder:save", handleSave);

    return () => {
      builder.removeEventListener("builder:ready", handleReady);
      builder.removeEventListener("builder:save", handleSave);
    };
  }, [pageData, onSave]);

  return <page-builder ref={ref} page-id={pageId} lang="es" />;
}

Vue 3

<template>
  <page-builder
    ref="builder"
    :page-id="pageId"
    lang="es"
    @builder:ready="onReady"
    @builder:save="onSave"
  />
</template>

<script setup>
import { ref } from "vue";
import "./dist/page-builder.js";

const props = defineProps(["pageId", "pageData"]);
const emit = defineEmits(["save"]);
const builder = ref(null);

function onReady() {
  builder.value.setPageData(props.pageData);
}

function onSave(e) {
  emit("save", e.detail.data);
}
</script>

Configure Vue to recognize the custom elements in vite.config.js:

export default defineConfig({
  plugins: [
    vue({
      template: {
        compilerOptions: {
          isCustomElement: (tag) =>
            ["page-builder", "page-manager"].includes(tag),
        },
      },
    }),
  ],
});

Adding Languages

  1. Create src/locales/{code}.json following the existing structure
  2. Add the locale code to I18n.supportedLocales in src/js/utils/i18n.js

Development

npm run dev        # Start Vite dev server
npm run build      # Production build to dist/
npm test           # Run tests (47 tests)
npm run lint       # Run ESLint

Project Structure

mtbuilder/
├── src/
│   ├── js/
│   │   ├── components/
│   │   │   ├── editors/          # Element-specific editors
│   │   │   ├── builder-canvas.js # Main canvas with drag & drop
│   │   │   ├── builder-sidebar.js
│   │   │   ├── canvas-view-switcher.js
│   │   │   ├── page-builder.js   # Standalone builder component
│   │   │   └── page-manager.js   # Full page management
│   │   ├── utils/
│   │   │   ├── store.js          # State management
│   │   │   ├── event-bus.js      # Internal pub/sub
│   │   │   ├── history.js        # Undo/redo stack
│   │   │   ├── sanitize.js       # XSS protection
│   │   │   ├── i18n.js           # Internationalization
│   │   │   └── export-utils.js   # HTML/JSON export
│   │   └── index.js              # Entry point
│   └── locales/                  # en.json, es.json, fr.json
├── examples/
│   ├── page-manager.html         # Page manager demo
│   └── php-integration.html      # Standalone builder demo
├── tests/                        # Vitest tests
├── dist/                         # Built output
└── index.html                    # Project landing page

License

MIT License