Skip to content

Get started

Install

DNA is published to the NPM registry

bash
npm install @chialab/dna
bash
yarn add @chialab/dna
bash
pnpm add @chialab/dna

Configure Typescript

This enables support for DNA decorators as well as correct transpilation of class property fields.

json
{
    "compilerOptions": {
        "moduleResolution": "bundler",
        "useDefineForClassFields": false,
        "experimentalDecorators": true
    }
}

Configure JSX

Typescript

json
{
    "compilerOptions": {
        "jsx": "react-jsx",
        "jsxImportSource": "@chialab/dna"
    }
}

Babel

json
{
    "plugins": [
        [
            "@babel/plugin-transform-react-jsx",
            {
                "runtime": "automatic",
                "importSource": "@chialab/dna"
            }
        ]
    ]
}

Define a component

DNA components are classes which extends the base HTMLElement.

Defining a component means to link a HTML tag with the element's constructor, as described by the Custom Elements specification. In this example we are going to use the customElement decorator method to register the component in the DNA registry:

tsx
import { Component, customElement, property } from '@chialab/dna';

@customElement('hello-world')
class HelloWorld extends Component {
    @property() name: string = '';

    render() {
        return <h1>Hello {this.name || 'world'}!</h1>;
    }
}

declare module '@chialab/dna' {
    namespace JSX {
        interface CustomElements {
            'hello-world': HelloWorld;
        }
    }
}
ts
import { Component, define, html } from '@chialab/dna';

const HelloWorld = define(
    'hello-world',
    class HelloWorld extends Component {
        static get properties() {
            return {
                name: {
                    type: String,
                    defaultValue: '',
                },
            };
        }

        render() {
            return html`<h1>Hello ${this.name || 'world'}!</h1>`;
        }
    }
);

INFO

TypeScript enables type checking and hints for component properties when you register the tag name with the JSX.CustomElements interface in DNA.

Extending native elements

Custom Element specification allows to define an element using the is attribute instead of the tag.
This is very useful when you want to extend a HTML tag, preserving its semanthic meaning. For example:

tsx
import { customElement, HTML, property } from '@chialab/dna';

@customElement('alert-dialog', {
    extends: 'dialog',
})
class AlertDialog extends HTML.Dialog {
    @property() title: string = '';

    render() {
        return <h1>{this.title}</h1>;
    }
}

declare module '@chialab/dna' {
    namespace JSX {
        interface CustomElements {
            'alert-dialog': AlertDialog & {
                extends: 'dialog';
            };
        }
    }
}
ts
import { define, HTML, html } from '@chialab/dna';

const AlertDialog = define(
    'alert-dialog',
    class AlertDialog extends HTML.Dialog {
        static get properties() {
            return {
                title: {
                    type: String,
                    defaultValue: '',
                },
            };
        }

        render() {
            return html`<h1>${this.title}</h1>`;
        }
    },
    {
        extends: 'dialog',
    }
);

In the example above, a new instance of AlertDialog inherits all class methods and properties, but its tagName will be DIALOG.

INFO

Extending builtin elements also preserves accessibility and usability features: extending HTMLButtonElement will make the component reachable and clickable via keyboard navigation without setting role and tabindex.

Render a component

The render helper is used by DNA components to generate their templates, but it can be used to add a component or a template in a specific point of the DOM tree, for example to instantiate the root component of your application:

tsx
import { Component, customElement, render } from '@chialab/dna';

@customElement('x-card')
class Card extends Component {
    ...
}

render(<Card />, document.body);

During the render cycle, DNA execs an in-place DOM diffing to update already existing nodes and remove the unused ones, so you can safely re-render a template. At the end of the render cycle, DNA will remove any node outside the template, including elements and texts of the original HTML document.

This function accepts the template as first argument and an optional render root node as second one. You can also use bound tag name instead of constructor reference:

tsx
import { render } from '@chialab/dna';
import './Card';
import './Article';

render(
    <>
        <x-card />
        <article is="x-article" />
    </>,
    document.body
);
tsx
import { Component, customElement } from '@chialab/dna';

@customElement('x-card')
class Card extends Component {
    // ...
}
tsx
import { Component, customElement } from '@chialab/dna';

@customElement('x-article', {
    extends: 'article',
})
class Article extends Component {
    // ...
}

Released under the MIT License.