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

@dlh.io/dlh-erd-viewer-deux

v0.1.12

Published

A React/Javascript component for viewing database ERD diagrams from DBML and TBLS formats

Downloads

52

Readme

DLH.io ERD Viewer Deux (ERDeux)

Popular ERD viewer designed by DLH.io originally to provide a better ERD experience to users of the DLH.io platform to visualize relationships in complicated data sources such as Salesforce.com, Dayforce, Paycor, and other operational systems.

Roadmap

Phase 1 - Read-Only

Future features and enhancements are taken by reqeust and PR.

Phase 2 - Full Capability

  • Connectivity to direct retrieve/update ERD from all Major Databases and Data Warehouse systems
  • Support additional schema formats
  • Metadata Updating
  • Sharing

Phase 3 - Extemded Capability

  • Design/Layout capability
  • Embedding on Another/Your Site
  • SaaS Hosted Version
    • Editing capability of ERD layouts
    • Team Collaboration

More about DLH.io

DLH.io is a leading ETL/ELT, data integration, API integration, and data engineering platform for any organization that works with data, and provide automated loading of popular operational data systems (e.g.: Dayforce, Salesforce, Workday, Hubspot, Toast, Aloha POS, etc.) typically to data warehouses (Databricks, BigQuery, Redshift, Snowflake, etc.) for reports, dashboards, and AI use cases. See more at DLH.io

@dlh.io/dlh-erd-viewer-deux

A React/javascript component for visualizing database Entity-Relationship Diagrams (ERD). Built by the tech team at DLH.io (datalakehouse.io), initially for read-only purposes to share target system data profiles for relationships in data for data/analytics engineers to better understand how to model their data products.

Installation

From a development perspective, a developer wishing to use this project will use our npmjs package deployment of the solution, using pnpm/npm...

npm install @dlh.io/dlh-erd-viewer-deux

Quick Start Project Code Reference

In your react/javascript/typescript project you will import the ERDViewer and styles (which can be overriden) in your existing project, and follow a similar patter to reference your DBML or TBLS (json) files which will be passed in to the component to be rendered in the ERDeux viewer.

import { ERDViewer } from '@dlh.io/dlh-erd-viewer-deux';
import '@dlh.io/dlh-erd-viewer-deux/styles.css';

const dbmlSchema = `
Table users {
  id integer [pk]
  username varchar(255) [not null]
  email varchar(255) [not null]
}

Table posts {
  id integer [pk]
  title varchar(255) [not null]
  author_id integer [ref: > users.id]
}
`;

function App() {
  return (
    <div style={{ width: '100%', height: '600px' }}>
      <ERDViewer
        format="dbml"
        content={dbmlSchema}
      />
    </div>
  );
}

Props / Properties

We've built a number of properties (prop) to allow a great deal of flexibility in how the component is used. Typically each prop is condition based in the code so that a default value is overriden.

Required Props

| Prop | Type | Description | |------|------|-------------| | format | 'dbml' \| 'tbls' | The schema format of the content | | content | string | The schema content to parse and display |

Optional Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | className | string | '' | Additional CSS class names for the container | | style | React.CSSProperties | - | Inline styles for the container | | positions | PositionMetadata | - | Pre-defined table positions for the layout | | devMode | boolean | false | Enable developer mode with position copying tools | | draggable | boolean | false | Allow users to drag and reposition tables | | showMinimap | boolean | true | Show or hide the minimap navigation | | showInfoPanel | boolean | true | Show or hide the information side panel | | displayMode | DisplayMode | 'all' | Column display mode: 'all', 'keys', or 'tableOnly' | | layoutAlgorithm | LayoutAlgorithm | 'default' | Layout algorithm for table positioning | | reverseAnimationFlow | boolean | false | Reverse the animation flow direction on connector lines | | tableOnlyConnectionMode | TableOnlyConnectionMode | 'center' | Connection mode when in table-only display | | toolbarAlignment | PanelAlignment | 'center' | Toolbar position: 'left', 'center', or 'right' | | infoPaneAlignment | PanelAlignment | 'right' | Info panel position: 'left' or 'right' | | printExportLogo | string | DLH logo | URL of logo to watermark on exports | | printExportLogoPosition | WatermarkPosition | 'top-left' | Position of watermark: 'top-left', 'top-right', 'bottom-left', 'bottom-right' | | theme | ERDViewerTheme | - | Theme overrides via CSS custom properties |

Callback Props

| Prop | Type | Description | |------|------|-------------| | onExport | (format: 'png' \| 'pdf') => void | Called when diagram is exported | | onPositionsChange | (positions: PositionMetadata) => void | Called when table positions change (in devMode or draggable) | | onTableSelect | (table: Table \| null) => void | Called when a table is selected | | onColumnSelect | (table: Table \| null, columnName: string \| null) => void | Called when a column is selected | | onDisplayModeChange | (mode: DisplayMode) => void | Called when display mode changes | | onLayoutAlgorithmChange | (algorithm: LayoutAlgorithm) => void | Called when layout algorithm changes |

Types

DisplayMode

Controls which columns are shown in table cards:

type DisplayMode = 'all' | 'keys' | 'tableOnly';
  • 'all' - Show all columns
  • 'keys' - Show only primary and foreign key columns
  • 'tableOnly' - Show only table names (no columns)

LayoutAlgorithm

Controls how tables are automatically positioned:

type LayoutAlgorithm = 'default' | 'hierarchical' | 'force' | 'grid' | 'spiral' | 'proximity';
  • 'default' - Dagre-based top-to-bottom layout
  • 'hierarchical' - Left-to-right hierarchical layout
  • 'force' - Force-directed layout
  • 'grid' - Grid pattern layout
  • 'spiral' - Spiral pattern layout
  • 'proximity' - Groups related tables together

PanelAlignment

type PanelAlignment = 'left' | 'center' | 'right';

WatermarkPosition

type WatermarkPosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';

PositionMetadata

Pre-defined positions for tables:

interface PositionMetadata {
  schemaFormat?: 'dbml' | 'tbls';
  capturedAt?: string;
  version?: string;
  positions: TablePosition[];
}

interface TablePosition {
  tableName: string;
  x: number;
  y: number;
}

CSS Customization

The component uses CSS custom properties (variables) for theming. You can override these in your application's CSS:

:root {
  /* Core colors */
  --erd-primary: #3b82f6;
  --erd-secondary: #64748b;
  --erd-background: #ffffff;
  --erd-surface: #f8fafc;
  --erd-border: #e2e8f0;
  --erd-text: #1e293b;
  --erd-text-muted: #64748b;
  --erd-highlight: #3b82f6;

  /* Key indicators */
  --erd-pk: #eab308;           /* Primary key color */
  --erd-fk: #8b5cf6;           /* Foreign key color */

  /* Typography */
  --erd-font-family: ui-sans-serif, system-ui, sans-serif;
  --erd-font-mono: ui-monospace, monospace;

  /* Toolbar */
  --erd-toolbar-text: #1e293b;

  /* Connector lines */
  --erd-edge-color: #64748b;
  --erd-edge-highlight-color: #3b82f6;
  --erd-animation-color: #3b82f6;

  /* Table cards */
  --erd-table-header-bg: #3b82f6;
  --erd-table-header-text: #ffffff;
  --erd-table-column-bg: #ffffff;
  --erd-table-column-highlight-bg: #eff6ff;

  /* Info panel */
  --erd-info-panel-bg: #ffffff;
  --erd-info-panel-header-bg: #f8fafc;
}

Using the theme prop

You can also pass theme overrides directly to the component via the theme prop (recommended):

<ERDViewer
  format="dbml"
  content={schema}
  theme={{
    '--erd-primary': '#10b981',
    '--erd-highlight': '#10b981',
    '--erd-table-header-bg': '#10b981',
  }}
/>

Examples

Basic Usage

import { ERDViewer } from '@dlh.io/dlh-erd-viewer-deux';
import '@dlh.io/dlh-erd-viewer-deux/styles.css';

function BasicExample() {
  const schema = `
    Table users {
      id integer [pk]
      name varchar(255)
    }
  `;

  return (
    <div style={{ width: '100%', height: '500px' }}>
      <ERDViewer format="dbml" content={schema} />
    </div>
  );
}

With Custom Layout and Display Mode

function CustomLayoutExample() {
  return (
    <ERDViewer
      format="dbml"
      content={schema}
      layoutAlgorithm="hierarchical"
      displayMode="keys"
      toolbarAlignment="left"
    />
  );
}

With Selection Callbacks

function SelectionExample() {
  const handleTableSelect = (table) => {
    if (table) {
      console.log('Selected table:', table.name);
    }
  };

  const handleColumnSelect = (table, columnName) => {
    if (table && columnName) {
      console.log(`Selected column: ${table.name}.${columnName}`);
    }
  };

  return (
    <ERDViewer
      format="dbml"
      content={schema}
      onTableSelect={handleTableSelect}
      onColumnSelect={handleColumnSelect}
    />
  );
}

With Custom Export Watermark

function ExportExample() {
  return (
    <ERDViewer
      format="dbml"
      content={schema}
      printExportLogo="https://example.com/my-logo.png"
      printExportLogoPosition="bottom-right"
      onExport={(format) => console.log(`Exported as ${format}`)}
    />
  );
}

Developer Mode

Developer mode was built so that we can render the ERD of the specific schema and then:

  1. Add metadata such as grouping
  2. Reposition the tables to a specific position that best emphasizes the schema/tables as we have an opinion on certain system relationships
  3. Capture the positions using the "Capture" button, which can then be added to the meta.json file for that schema so that the ERD will load with the updated positions and/or groupings as a default perspective.

Developer Mode for Position Capture

Enable devMode to capture table and group positions for later use:

function DevModeExample() {
  const handlePositionsChange = (positions) => {
    // Save positions to use as the `positions` prop later
    console.log(JSON.stringify(positions, null, 2));
  };

  return (
    <ERDViewer
      format="dbml"
      content={schema}
      devMode={true}
      draggable={true}
      onPositionsChange={handlePositionsChange}
    />
  );
}

Using Pre-defined Positions, Highlights, and Groups

const savedPositions = {
    "version": "1.0",
    "positions": [
        { "tableName": "customers", "x": 50, "y": 100 },
        { "tableName": "orders", "x": 350, "y": 100 },
        { "tableName": "order_items", "x": 650, "y": 100 },
        { "tableName": "products", "x": 650, "y": 350 },
        { "tableName": "categories", "x": 950, "y": 350 },
        { "tableName": "addresses", "x": 50, "y": 350 }
    ],
    "highlights": [
        {
            "tableName": "customers",
            "borderColor": "#3b82f6",
            "headerBackgroundColor": "#dbeafe"
        },
        {
            "tableName": "orders",
            "borderColor": "#f59e0b",
            "headerBackgroundColor": "#fef3c7"
        }
    ],
    "groups": [
        {
            "id": "core-commerce",
            "label": "Core Commerce",
            "tables": ["customers", "orders", "order_items"],
            "collapsible": false
        },
        {
            "id": "product-catalog",
            "label": "Product Catalog",
            "tables": ["products", "categories"],
            "collapsible": false
        }
    ]
};

function PositionedExample() {
  return (
    <ERDViewer
      format="dbml"
      content={schema}
      positions={savedPositions}
    />
  );
}

Dark Theme Example

function DarkThemeExample() {
  return (
    <ERDViewer
      format="dbml"
      content={schema}
      theme={{
        '--erd-background': '#1e293b',
        '--erd-surface': '#334155',
        '--erd-border': '#475569',
        '--erd-text': '#f1f5f9',
        '--erd-text-muted': '#94a3b8',
        '--erd-table-header-bg': '#3b82f6',
        '--erd-table-column-bg': '#1e293b',
        '--erd-toolbar-text': '#f1f5f9',
      }}
    />
  );
}

Next.js Usage

We enjoy the fruits of what our friends at https://vercel.com/ and the community have done with the https://nextjs.org/ project. We use this framework for our initial testing using the ERDeux Viewer component.

For Next.js applications, use dynamic import with SSR disabled:

import dynamic from 'next/dynamic';

const ERDViewer = dynamic(
  () => import('@dlh.io/dlh-erd-viewer-deux').then((mod) => mod.ERDViewer),
  { ssr: false }
);

// Import CSS in your layout or page
import '@dlh.io/dlh-erd-viewer-deux/styles.css';

export default function Page() {
  return (
    <div style={{ width: '100%', height: '100vh' }}>
      <ERDViewer format="dbml" content={schema} />
    </div>
  );
}

Supported Schema Formats

Currently the only schema formats are:

  • DBML
  • TBLS (json)

DBML

The component supports DBML (Database Markup Language) format:

Table users {
  id integer [pk, increment]
  username varchar(255) [not null, unique]
  email varchar(255) [not null]
  created_at timestamp [default: `now()`]
}

Table posts {
  id integer [pk, increment]
  title varchar(255) [not null]
  content text
  author_id integer [ref: > users.id]
}

TBLS

The component also supports TBLS JSON format for schema documentation.

License

GNU Lesser General Public License (LGPL)

Development Building and Contributing

Contribute

Fork and submit all PRs using Github. Post any issues with appropriate test case and visualizations on Github issues.

Building Locally

  1. Fork the project.
  2. Run pnpm run build to test locally
  3. Point an example application to using the package.json file link reference
  4. In a feature branch make changes
  5. Rebuild as necessary and retest
  6. Create appropriate example application tests and provide necessary screenshots if issues
  7. PR or Issue submit to Github

How we Publish to NPM

This is fairly well documented on the NPMjs site, but often we use a private repository, for finalizing testing/QA, and then we publish to the public repository, which is probably how you found us. We follow the same steps most architects do from our build machine, etc.:

  1. Confirm or ensure branch selection
  2. pnpm run build
  3. npm login
    • authenticate security, etc.
    • recent changes in 2025/6 require rotating tokens if using that approach
  4. npm publish
  5. Verify updated package with latest version # is deployed on npmjs