GUIDE
Getting started
Install Dojo NG and render your first component, in your framework of choice.
-
Install
npm install @dojo-ng/button -
Import the component
import '@dojo-ng/button'; -
Render it
function App() { return <dj-button kind="contained">Get started</dj-button>; }
Working with React
Use React 19 or later. React 19 passes arrays and objects to a custom element as properties, so <dj-select options={plans}> works as written. Earlier versions turn every prop into a string attribute.
For a custom event, write on followed by its exact name:
<dj-chip closeable ondj-close={() => setLabel("(cleared)")}>{label}</dj-chip>
In TypeScript, React's JSX types do not know the dj-* tags until you declare them once:
// dj-elements.d.ts — anywhere in your TypeScript project
import "react";
declare module "react" {
namespace JSX {
interface IntrinsicElements {
[tag: `dj-${string}`]: any;
}
}
}
Import the packages before the first render. The React page explains why, and covers forms and React 18.
-
Install
npm install @dojo-ng/button -
Import the component
// main.js import '@dojo-ng/button'; -
Render it
<template> <dj-button kind="contained">Get started</dj-button> </template>
Working with Vue
Tell Vue's template compiler that dj-* tags are custom elements, so it stops looking for Vue components by those names:
// vite.config.js
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
export default defineConfig({
plugins: [
vue({
template: {
compilerOptions: { isCustomElement: (tag) => tag.startsWith("dj-") },
},
}),
],
});
Add .prop to make sure an array or object is set as a property, and bind custom events with @:
<dj-select label="Plan" :options.prop="plans"></dj-select>
<dj-chip closeable @dj-close="label = '(cleared)'">{{ label }}</dj-chip>
The Vue page covers v-model and forms.
-
Install
npm install @dojo-ng/button -
Import the component
// main.ts import '@dojo-ng/button'; -
Render it
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; @Component({ selector: 'app-root', template: `<dj-button kind="contained">Get started</dj-button>`, schemas: [CUSTOM_ELEMENTS_SCHEMA], }) export class AppComponent {}
Working with Angular
CUSTOM_ELEMENTS_SCHEMA goes on each standalone component, or NgModule, whose templates use a dj-* tag. Angular sets bracket bindings as DOM properties, so arrays and objects pass straight through, and custom events bind with parentheses:
<dj-select label="Plan" [options]="plans"></dj-select>
<dj-chip closeable (dj-close)="label = '(cleared)'">{{ label }}</dj-chip>
The Angular page covers ngModel and reactive forms.
-
Install
npm install @dojo-ng/button -
Import the component
<!-- App.svelte --> <script> import '@dojo-ng/button'; </script> -
Render it
<dj-button kind="contained">Get started</dj-button>
Working with Svelte
Svelte needs no configuration. It sets a prop as a property whenever the element defines one, so arrays and objects pass straight through. Custom events bind with on followed by the event name:
<dj-select label="Plan" options={plans}></dj-select>
<dj-chip closeable ondj-close={() => (label = '(cleared)')}>{label}</dj-chip>
The Svelte page covers state and forms.
-
Add an import map
<script type="importmap"> { "imports": { "@dojo-ng/button": "https://esm.sh/@dojo-ng/button" } } </script> -
Import the component
<script type="module">import "@dojo-ng/button";</script> -
Render it
<dj-button kind="contained">Get started</dj-button>
Working without a framework
No install and no build step. esm.sh serves each package as an ES module and resolves its dependencies, Lit among them. Add one import map entry per package you use.
Set array and object values as properties from script, and listen for events the usual way:
<dj-select id="plan" label="Plan"></dj-select>
<script type="module">
import "@dojo-ng/select";
const plan = document.getElementById("plan");
plan.options = [
{ value: "free", label: "Free" },
{ value: "pro", label: "Pro" },
];
plan.addEventListener("change", () => console.log(plan.value));
</script>
The Plain HTML page covers bundlers, version pinning, and events.
Load the theme
Every component carries fallback values, so it renders without a theme. @dojo-ng/theme adds the full --dj-* token set and a dark mode that follows the operating system, or is fixed with data-dj-theme="dark" on <html>. Link it, or add @dojo-ng/theme/theme.css to your bundler's global styles. The theming guide covers the rest.
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@dojo-ng/theme/theme.css">
Supporting Safari before 16.4
Form controls such as dj-text-input rely on ElementInternals, which Safari added in 16.4. To support Safari 15 through 16.3, load element-internals-polyfill before the first dj-* import. It does nothing in browsers that already have the API.
import "element-internals-polyfill";
import "@dojo-ng/text-input";