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

@dineug/erd-editor

v3.9.1

Published

Entity-Relationship Diagram Editor

Readme

@dineug/erd-editor

Entity-Relationship Diagram Editor as a custom element

erd-editor

<erd-editor> is a full database-schema editor in a single custom element. It has no framework dependency and renders into a closed shadow root, so it drops into any page — React, Vue, Svelte, or plain HTML — without leaking styles either way.

This is the same editor that powers erd-editor.io, the VS Code extension and the IntelliJ plugin.

Features

  • Visual schema design — tables, columns, memos, and four relationship cardinalities (zero-one, zero-N, one-only, one-N)
  • Import — a .sql dump, a GraphQL SDL schema from any tool that emits one, a .dbml file, or an .aml file
  • SQL DDL export — Databricks, MariaDB, MSSQL, MySQL, Oracle, PostgreSQL, Snowflake and SQLite
  • Code generation — TypeScript, GraphQL, C#, Java, JPA, Kotlin, Scala, Go, SQLAlchemy, TypeORM, Sequelize, Drizzle, DBML, AML
  • Export — .erd.json, .sql, .png
  • Force-directed visualization of table relationships
  • Quick search, undo / redo, remappable keyboard shortcuts, and a built-in theme builder
  • Collaboration hooks — the editor emits and applies actions; you supply the transport

Install

npm install @dineug/erd-editor

The package ships ES modules with its dependencies left as bare imports, so any bundler (Vite, webpack, Rspack, esbuild, …) resolves, dedupes and tree-shakes them like the rest of your app. Four features run in shared workers — schema garbage collection, the PNG export, the automatic table placement and the syntax highlighting — constructed as new SharedWorker(new URL('./workers/…', import.meta.url)), which those bundlers emit as worker files beside your chunks; a strict CSP needs worker-src 'self'. Without a bundler, use the UMD file described under Script tag instead.

Usage

import '@dineug/erd-editor';

const editor = document.createElement('erd-editor');
// the editor fills its container, and a custom element is inline by default
Object.assign(editor.style, { display: 'block', width: '100%', height: '100vh' });
document.body.appendChild(editor);

// load a document without adding an undo entry, then keep it in sync
editor.setInitialValue(localStorage.getItem('my-diagram') ?? '');
editor.addEventListener('change', () => {
  localStorage.setItem('my-diagram', editor.value);
});

setInitialValue('') starts an empty document.

Server-side rendering

Importing the package registers the custom element at module scope, so it needs a DOM and throws in Node. In Next.js, Nuxt, SvelteKit or Astro, reach it from a client-only path:

useEffect(() => {
  import('@dineug/erd-editor');
}, []);

Vite

In development Vite pre-bundles dependencies into its cache directory, and the worker files would then be looked up there rather than beside the package. Exclude the editor packages from that step; a production build needs nothing.

// vite.config.js
export default {
  optimizeDeps: {
    exclude: ['@dineug/erd-editor'],
  },
};

Script tag

dist/erd-editor.umd.js is a self-contained build for a plain <script> tag: every dependency and all four workers are inside it, and it registers <erd-editor> and exposes the two file callbacks as window.ErdEditor. It is what unpkg and jsdelivr serve.

<erd-editor style="display: block; width: 100%; height: 100vh"></erd-editor>
<script src="https://cdn.jsdelivr.net/npm/@dineug/erd-editor/dist/erd-editor.umd.js"></script>

The workers travel inside the file as data: URLs, so a strict CSP needs worker-src data: for this build. Pin a version in the URL for anything beyond a demo.

HTML

<erd-editor system-dark-mode enable-theme-builder></erd-editor>
<script type="module">
  import '@dineug/erd-editor';

  const editor = document.querySelector('erd-editor');
</script>
erd-editor {
  display: block;
  width: 100%;
  height: 100vh;
}

API

Attributes

| Attribute | Property | Description | | --- | --- | --- | | readonly | readonly | Blocks editing and suppresses the change event. Assigning value, setSchemaSQL(), setSchemaGraphQL(), setSchemaDBML(), setSchemaAML() and clear() are ignored while it is set — load with setInitialValue() instead. Viewport actions and the SQL/code output settings still apply. | | system-dark-mode | systemDarkMode | Follows the OS color scheme | | enable-theme-builder | enableThemeBuilder | Shows the built-in theme builder |

Properties

| Property | Description | | --- | --- | | value: string | The document as JSON — an .erd.json document (schema). Assigning it loads the document as an edit, so it lands in the undo history; use setInitialValue to load without one. |

Methods

| Method | Description | | --- | --- | | setInitialValue(value: string) | Load the initial document. Does not create a history entry, and clears the undo history, so nothing done before the load can be undone or redone onto it. | | getSchemaSQL(vendor?) | Export DDL. vendor is one of Databricks, MariaDB, MSSQL, MySQL, Oracle, PostgreSQL, Snowflake, SQLite; omit it to use the document's own setting. | | setSchemaSQL(value: string) | Parse a DDL string and replace the current document with it. Lands in the undo history; an empty string is ignored. | | setSchemaGraphQL(value: string) | Parse a GraphQL SDL string and replace the current document with it. Object types become tables, scalars map to the document's own dialect, and relationships are read from the fields that point at another type. Lands in the undo history; an empty string is ignored. | | setSchemaDBML(value: string) | Parse a DBML string and replace the current document with it. Tables, columns, indexes and every Ref spelling are read; a Project, TableGroup or sticky Note is skipped, and text it cannot read loads an empty document rather than being refused. Lands in the undo history; an empty string is ignored. | | setSchemaAML(value: string) | Parse an AML string and replace the current document with it. Entities, attributes, indexes and every relation arrow are read, in the v2 and the legacy v1 spelling; a check, a struct type and a view are skipped, and text it cannot read loads an empty document rather than being refused. Lands in the undo history; an empty string is ignored. | | setDiffValue(value: string) | Open the diff viewer against another document. | | setPresetTheme(options) | Set appearance, grayColor and accentColor. | | setTheme(theme) | Override individual theme tokens. | | setKeyBindingMap(map) | Remap shortcuts. edit, stop, search, undo, redo, zoomIn, zoomOut and zoomReset are reserved. | | getSharedStore(config?) | Returns { subscribe, dispatch, dispatchSync, connection, disconnect, destroy }. subscribe gives you this editor's actions to relay; dispatch applies a peer's. You supply the transport. config is { getNickname?, mouseTracker?, focusTracker? }; both trackers default to true and broadcast this editor's cursor and table focus to peers. | | focus() / blur() | Move focus in and out of the editor. | | clear() | Empty the document. | | destroy() | Tear the editor down and release its listeners, subscriptions and shared stores. |

Events

| Event | Description | | --- | --- | | change | The document changed. Debounced, and never fired while readonly. Read editor.value. | | changePresetTheme | The theme was changed from inside the editor. event.detail carries the new options. |

Syntax highlighting

The SQL and code-generation panels are highlighted by Shiki, in a shared worker of its own. There is nothing to install or register: the worker is built the first time a code panel renders, so a page that opens none never fetches the grammars.

| | | | --- | --- | | Languages | SQL, TypeScript, GraphQL, C#, Java, Kotlin, Scala, Go, Python | | Themes | github-dark, github-light, picked from the editor's light / dark appearance |

Those are exactly the languages the panels emit — the JPA generator emits Java, the SQLAlchemy generator emits Python, the TypeORM, Sequelize and Drizzle generators emit TypeScript, and the DBML and AML generators are highlighted as SQL, the closest grammar shiki ships.

Where SharedWorker is missing — Chrome on Android, Safari before 16.4 — the underlying error is logged and the panels render as plain text; nothing else is affected. The regex engine is plain JavaScript, so no host CSP needs wasm-unsafe-eval.

File dialogs

Import and export go through injectable callbacks, so a host without a browser file dialog — an IDE webview, for example — can supply its own. The two are not symmetric: export hands you the finished file, while import only asks for one, and you push the content back in yourself.

import { setExportFileCallback, setImportFileCallback } from '@dineug/erd-editor';

setExportFileCallback((blob, { fileName }) => host.writeFile(fileName, blob));

setImportFileCallback(async ({ type, op, accept }) => {
  const text = await host.pickFile(accept);

  if (op === 'diff') {
    editor.setDiffValue(text);
  } else if (type === 'json') {
    editor.value = text;
  } else if (type === 'sql') {
    editor.setSchemaSQL(text);
  } else if (type === 'graphql') {
    editor.setSchemaGraphQL(text);
  } else if (type === 'dbml') {
    editor.setSchemaDBML(text);
  } else if (type === 'aml') {
    editor.setSchemaAML(text);
  }
});

Dispatch on every type you handle and ignore the rest. Assigning value clears the document before it parses, so routing a payload there that is not an .erd.json document — through a catch-all else, or because a type added later fell through — empties the diagram instead of importing anything. accept carries the extensions for that type (.json, .sql, or .graphql,.gql,.graphqls), ready to hand to a host file dialog.

Left unset, the editor uses the browser's own download and file-picker behavior.

Headless replica

A second entry point runs the document store with no DOM, for hosts that need to apply an action stream and serialize the result off the main thread:

import { createReplicationStore } from '@dineug/erd-editor/engine.js';

// `toWidth` measures text for layout — a worker has no DOM, so you supply it
const store = createReplicationStore({ toWidth });

store.setInitialValue(savedJson);
store.on({ change: () => persist(store.value) });
store.dispatch(actions); // actions relayed from a live editor's shared store

Development

This package is the editor core of the erd-editor monorepo; work on it from a workspace checkout.

pnpm --filter @dineug/erd-editor dev            # builds workspace deps, then a dev server
pnpm --filter @dineug/erd-editor dev:storybook  # component playground
pnpm exec vp run --filter @dineug/erd-editor --fail-if-no-match test
pnpm --filter @dineug/erd-editor e2e            # Playwright

Documentation

Issues

Found a bug or want a feature? Open an issue.

License

MIT © SeungHwan-Lee