DNA
Progressive Web Components.
Get started
The recommended way to use DNA is to setup a project with Babel or TypeScript which has a lot of life saver features like modules, decorators and typechecking, but it also works untranspiled in the browser.
Use a CDN
You can use DNA via CDN thanks to the Unpkg:
import { Component, customElements } from 'https://unpkg.com/@chialab/dna?module';
Setup a bundler
The DNA environment is pretty common (if you are familiar with other libraries like React, Angular, LitElement etc.):
-
Make sure you have a recent Node.js version installed
-
Setup a bundler (Rollup is recommended, but Webpack and Parcel are equally good choices)
-
Setup Babel (Rollup, Webpack, Parcel) or TypeScript (Rollup, Webpack, Parcel):
-
if your choice is Babel, please make sure all this plugins are loaded in order to use all DNA features:
$ npm i -D \ @babel/plugin-proposal-decorators \ @babel/plugin-proposal-class-properties
.babelrc
{ "plugins": [ "@babel/plugin-proposal-decorators", "@babel/plugin-proposal-class-properties" ] }
If you want to use React JSX instead of template strings, you will also need to install the
@babel/plugin-transform-react-jsx
:Classic
{ "plugins": [ ["@babel/plugin-transform-react-jsx", { "pragma": "h" }] ] }
Automatic
{ "plugins": [ ["@babel/plugin-transform-react-jsx", { "runtime": "automatic", "importSource": "@chialab/dna" }] ] }
Since the DNA
html
method is provided by thehtm
module by Jason Miller, you can also use thebabel-plugin-htm
to preprocess templates.{ "plugins": [ ["babel-plugin-htm", { "pragma": "h", "import": { "module": "@chialab/dna" } }] ] }
-
-
Install DNA
$ npm i @chialab/dna
Define a component
DNA components are classes which extends the base HTMLElement with helpers for templating, styling, events delegation and life cycle.
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:
import { Component, customElement, property } from '@chialab/dna'; @customElement('hello-world') class HelloWorld extends Component { @property() name = ''; // define a template render() { return <h1>Hello {this.name || 'world'}!</h1>; } }
JavaScript
You can use the class decorator if you are using TypeScript or this Babel plugin, otherwise you have to fallback directly using customElements.define
:
import { Component, customElements, html } from '@chialab/dna'; class HelloWorld extends Component { static get properties() { return { name: { type: String, defaultValue: '', }, }; } // define a template render() { return html`<h1>Hello ${this.name || 'world'}!</h1>`; } } customElements.define('hello-world', HelloWorld);
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:
import { Component, customElement, property } from '@chialab/dna'; @customElement('blog-post', { extends: 'article' }) class BlogPost extends Component { @property() title = ''; render() { return <h1>{this.title}</h1>; } }
JavaScript
import { Component, customElements, html } from '@chialab/dna'; class BlogPost extends Component { static get properties() { return { title: { type: String, defaultValue: '', }, }; } render() { return html`<h1>${this.title}</h1>`; } } customElements.define('blog-post', BlogPost, { extends: 'article' });
In the example above, a new instance of BlogPost
inherits all class methods and properties, but its tagName
will be ARTICLE
.
Note Extending builtin elements also preserves accessibility and usability features: extending the
BUTTON
element will make the component reachable and clickable via keyboard navigation without settingrole
andtabindex
.
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:
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:
import { Component, customElement, render } from '@chialab/dna'; @customElement('x-card') class Card extends Component { ... } render(<x-card />, document.body);
It also work for extended native tags:
import { Component, customElement, render } from '@chialab/dna'; @customElement('x-article', { extends: 'article', }) class Article extends Component { ... } render(<article is="x-article" />, document.body);
You can use the render
method to inject more complex templates too:
import { render } from '@chialab/dna'; render(<div class="wrapper"> <h1>Title</h1> </div>, document.body);