Dojo NG is being built in the open, one section at a time — follow along on Heptapod.

OPEN SOURCE · BSD-3-CLAUSE

Framework-agnostic components,
without the compromise.

dj-* web components you consume the same way from React, Vue, Angular, and Svelte, or from plain HTML with no framework at all.

Try the playground Get started

React Vue Angular Svelte Plain HTML

<dj-data-grid>: static facsimile, styled to match a React-hosted render

<dj-data-grid>: static facsimile, styled to match a Vue-hosted render

<dj-data-grid>: static facsimile, styled to match an Angular-hosted render

<dj-data-grid>: static facsimile, styled to match a Svelte-hosted render

<dj-data-grid>: static facsimile, styled to match a plain-HTML render

Example dj-* components, their category, and their release status
ComponentCategoryStatus
dj-data-gridDataStable
dj-rich-textEditingStable
dj-buttonFoundationStable
Open the full playground  →

Most component libraries lock you into one framework. Switch frameworks, or run more than one across your org, and you rebuild your design system from scratch. Dojo NG's components are custom elements: the same dj-* tags work natively in React, Vue, Angular, and Svelte, and in plain HTML with no framework at all, so the library outlives whatever framework you're on today.

How it works

One set of components

Every dj-* element is built once: markup, styling, and behavior all live in the component itself.

Every framework, or none

React, Vue, Angular, and Svelte consume the same elements through thin, typed adapters. Plain HTML needs no adapter.

No lock-in

Swap frameworks, run more than one, or drop the JS framework entirely. The components don't care.

Extend it with plugins

The core components stay small. Anything optional layers on as a separate package, through the same public extension points anyone else can write against.

Data grid plugins

Row grouping with aggregates, tree rows, and column selection ship as separate packages. The grid core never reads their config.

Browse the plugins  →

Rich-text plugins

Headings, tables, mentions, the slash menu, embeds, and a paste sanitizer, each its own package on the Lexical core.

Browse the plugins  →

Write your own

defineDataGridPlugin and defineRichTextPlugin are public API. Extend a component without forking it.

Read the guide  →

Built for real apps

Not a marketing-page component kit: designed for the data- and forms-heavy apps teams actually ship.

WCAG 2.2 AA

Accessible by default

WCAG 2.2 AA is a standing requirement, checked with automated axe audits across the set.

Form-associated

Every input plugs into native HTML forms via ElementInternals. No wrapper libraries needed.

Built for scale

Designed for large, data- and forms-heavy line-of-business applications, not landing pages.

Pick your starting point

Same components, five getting-started paths.

Illustrative — these snippets don't resolve until the packages publish. See Getting started.

npm install @dojo-ng/button
import '@dojo-ng/button';

function App() { return <dj-button kind="contained">Get started</dj-button>; }

npm install @dojo-ng/button
<script setup>
import '@dojo-ng/button';
</script>

<template> <dj-button kind="contained">Get started</dj-button> </template>

npm install @dojo-ng/button
import '@dojo-ng/button';

@Component({ selector: 'app-root', template: &lt;dj-button kind="contained"&gt;Get started&lt;/dj-button&gt;, schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class AppComponent {}

npm install @dojo-ng/button
<script>
import '@dojo-ng/button';
</script>

<dj-button kind="contained">Get started</dj-button>

<script type="importmap">
{ "imports": { "@dojo-ng/button": "https://esm.sh/@dojo-ng/button" } }
</script>
<script type="module">import "@dojo-ng/button";</script>
<dj-button kind="contained">Get started</dj-button>

Coming from another library?

Component-by-component mapping guides so a migration doesn't mean a rewrite.

Coming from MUI

Map MUI components to their dj-* equivalents, prop by prop.

Read the guide  →

Coming from Ant Design

A component and theming-token mapping from Ant's system to --dj-* tokens.

Read the guide  →

Coming from PrimeNG

Bring an Angular PrimeNG app across component by component.

Read the guide  →
★ Star on Heptapod

Re-skin the whole library with tokens

Every dj-* component reads its color, spacing, and type from --dj-* custom properties. Override the primitives, and the whole library follows.

--dj-color-primary-500

#3B82F6

--dj-color-primary-600

#2563EB

--dj-color-neutral-800

#1F2937

--dj-color-success-600

#16A34A

See the theming guide  →

Ready to try Dojo NG?