Reef v8
A lightweight library for creating reactive, state-based components and UI. Reef is a simpler alternative to React, Vue, and other large frameworks.
Features
- Weighs just 3kb minified and gzipped, with zero dependencies.
- Simple templating with JavaScript strings or template literals.
- Load it with a
<script>
element or ES module import—no command line or transpiling required. - Uses DOM diffing to update only the things that have changed.
- Has Redux/Vuex-like data stores, setters and getters baked right in.
- Automatically encodes markup in your data to protect you from cross-site scripting (XSS) attacks.
- Work with native JavaScript methods and browser APIs instead of custom methods and pseudo-languages.
- Supported all the way back to IE9.
Ditch that bloated framework, and make web development fun and simple again!
Why use Reef?
Reef is an anti-framework.
It does a lot less than the big guys like React and Vue. It doesn’t have a Virtual DOM. It doesn’t require you to learn a custom templating syntax. It doesn’t provide a bunch of custom methods.
Reef does just one thing: render UI.
Couldn’t you just use some template strings and innerHTML
? Sure. But Reef only updates things that have changed instead of clobbering the DOM and removing focus from your form fields. It also automatically renders a new UI when your data updates, and helps protect you from XSS attacks.
If you’re craving a simpler, back-to-basics web development experience, Reef is for you.
(And if not, that’s cool too! Carry on.)
Getting Started
1. Include Reef on your site
Reef works without any build step.
The CDN is the fastest and simplest way to get started, but you can use importable modules or a direct download if you’d prefer.
<!-- Get the latest major version -->
<script src="https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.min.js"></script>
Reef uses semantic versioning. You can grab a major, minor, or patch version from the CDN with the @1.2.3
syntax. You can find all available versions under releases.
ES Modules Reef also supports modern browsers and module bundlers (like Rollup, Webpack, Snowpack, and so on) using the ES modules NPM You can also use NPM (or your favorite package manager). First, install with NPM. Then import the package. CommonJS If you use NodeJS, you can import Reef using the AMD If you use RequireJS, SystemJS, and other AMD formats, you can import Reef with the Direct Download You can download the files directly from GitHub. Compiled and production-ready code can be found in the More ways to install Reef
import
syntax. Use the .es
version.import Reef from 'https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.es.min.js';
npm install reefjs --save
import Reef from 'reefjs';
require()
method with the .cjs
version.let Reef = require('https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.cjs.min.js');
.amd
version.requirejs(['https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.amd.min.js'], function (Reef) {
//...
});
dist
directory. The src
directory contains development code.<script src="path/to/reef.min.js"></script>
2. Add an element to render your component/UI into
This is typically an empty div
with a targetable selector.
<div id="app"></div>
3. Create your component
Create a new Reef()
instance, passing in two arguments: your selector, and your options.
Provide a selector
The first argument is the selector for the element you want to render the UI into. Alternatively, you can pass in the element itself.
// This works
let app = new Reef('#app');
// This does too
let elem = document.querySelector('#app');
let app = new Reef(elem);
Provide a Template
The second argument is an object of options. It requires a template property, as either a string or a function that returns a string, to render into the DOM.
// Your template can be a string
let app = new Reef('#app', {
template: '<h1>Hello, world!</h1>'
});
// It can also be a function that returns a string
let app = new Reef('#app', {
template: function () {
return '<h1>Hello, world!</h1>';
}
});
[Optional] Add State/Data
As an optional property of the options argument, you can include state for your component with the data
property.
The data object is automatically encoded and passed into your template function, so that you can use it to customize your template.
// Some data
let app = new Reef('#app', {
data: {
greeting: 'Hello',
name: 'world'
},
template: function (props) {
return `<h1>${props.greeting}, ${props.name}!</h1>`;
}
});
Template literals give you a simple, JSX-like templating experience.
4. Render your component
Render your component by calling the render()
method on it.
app.render();
State Management
Reef uses data reactivity to update your UI.
Data reactivity means that the UI “reacts” to changes in your data. Update your data, and the UI automatically renders any required updates based on the new state.
// Create a component and render it
let app = new Reef('#app', {
data: {
greeting: 'Hello',
name: 'world'
},
template: function (props) {
return `<h1>${props.greeting}, ${props.name}!</h1>`;
}
});
app.render();
// This causes component to update with "Hi, universe"
app.data.greeting = 'Hi';
app.data.name = 'Universe';
You can also update the entire data object.
// This will also update the UI
app.data = {
greeting: 'Hi',
name: 'Universe'
};
Try data reactivity on CodePen →
For better performance, multiple property updates may be batched into a single, asynchronous render. You can detect when a render has been completed using the reef:render
event hook.
Non-Reactive Data
Sometimes, you want to update data without updating the UI.
You can get an immutable copy of your data by passing it into the Reef.clone()
method. This creates a non-reactive copy of your data that won’t affect the state of your component or cause a UI update.
// Create an immutable copy of the app.data
let data = Reef.clone(app.data);
// Update the copy
// This does NOT update the app.data or render a new UI
data.name = 'Universe';
When you’re ready to update your component data, you can set the component’s data
property to your cloned copy.
// Reactively update the component data
app.data = data;
Try non-reactive data on CodePen →
Note: You can use the Reef.clone()
method to create an immutable copy of any array or object, not just your component data.
Advanced Components
As your project gets bigger, the way you manage components and data may need to grow with it. Reef has some options to help make things a little easier.
HTML Templates
Default and state-based HTML attributes
You can use component data to conditionally include or change the value of HTML attributes in your template.
Use the reef-checked
, reef-selected
, and reef-value
attributes to dynamically control the checked
, selected
, and value
attributes, respectively. Use a falsy value when the item should not be checked or selected.
In the example below, the checkbox is checked
when agreeToTOS
is true
.
let app = new Reef('#app', {
data: {
agreeToTOS: true
},
template: function (props) {
return `
<label>
<input type="checkbox" reef-checked="${agreeToTOS}">
</label>`;
}
});
You might also want to use a default value when an element initially renders, but defer to any changes the user makes after that.
You can do that with the reef-default-checked
, reef-default-selected
, and reef-default-value
attributes.
In this example, Hermione
has the [selected]
attribute on it when first rendered, but will defer to whatever changes the user makes when diffing and updating the UI.
let app = new Reef('#app', {
template: function () {
return `
<label for="wizards">Who is the best wizard?</label>
<select>
<option>Harry</option>
<option reef-default-selected>Hermione</option>
<option>Neville</option>
</select>`;
}
});
Try controlling form attributes on CodePen →
Preventing Cross-Site Scripting (XSS) Attacks
To reduce your risk of cross-site scripting (XSS) attacks, Reef automatically encodes any markup in your data before passing it into your template.
You can disable this feature by setting the allowHTML
option to true
.
Important! Do NOT do this with third-party or user-provided data. This exposes you to the risk of cross-site scripting (XSS) attacks.
let app = new Reef('#app', {
data: {
greeting: '<strong>Hello</strong>',
name: 'world'
},
template: function (props) {
return `<h1>${props.greeting}, ${props.name}!</h1>`;
},
allowHTML: true // Do NOT use with third-party/user-supplied data
});
Try allowing HTML in your data on CodePen →
Getting the element the template is being rendered into
An optional second argument is passed into the template()
function: the element the template is being rendered into.
This is particularly handy if you have data attributes on your element that affect what’s rendered into the template.
<div id="app" data-greeting="Hello"></div>
let app = new Reef('#app', {
data: {
name: 'world'
},
template: function (props, elem) {
return `<h1>${elem.getAttribute('data-greeting')}, ${props.name}!</h1>`;
}
});
Try getting the HTML element that the template was rendered into on CodePen →
Nested Components
If you’re managing a bigger app, you may have components nested inside other components.
Reef provides you with a way to attach nested components to their parent components. When the parent component is updated, it will automatically update the UI of its nested components if needed.
Associate a nested component with its parent using the attachTo
key in your options
object. You can provide a component or array of components for a value.
You only need to render the parent component. It’s nested components will render automatically.
// Parent component
let app = new Reef('#app', {
data: {
greeting: 'Hello, world!'
},
template: function (props) {
return `
<h1>${props.greeting}</h1>
<div id="todos"></div>`;
}
});
// Nested component
let todos = new Reef('#todos', {
data: {
todos: ['Swim', 'Climb', 'Jump', 'Play']
},
template: function (props) {
return `
<ul>
${props.todos.map(function (todo) {
return `<li>${todo}</li>`;
}).join('')}
</ul>`;
},
attachTo: app
});
app.render();
Try nested components on CodePen →
Attaching and Detaching Nested Components
You can attach or detach nested components at any time using the attach()
and detach()
methods on the parent component.
Provide an individual component or array of components as an argument.
// Attach components
app.attach(todos);
app.attach([todos]);
// Detach components
app.detach(todos);
app.detach([todos]);
Try attaching nested components on CodePen →
Shared State with Data Stores
A Data Store is a special Reef object that holds reactive data you can share with multiple components.
Any time you update the data in your Data Store, any components that use the data will also be updated, and will render again if there are any UI changes.
Create a Data Store using the new Reef.Store()
constructor.
let store = new Reef.Store({
data: {
heading: 'My Todos',
todos: ['Swim', 'Climb', 'Jump', 'Play']
}
});
To use your Data Store with a component, pass it in with the store
property instead of providing a data
object.
let app = new Reef('#app', {
store: store,
template: function (props) {
return `
<h1>${props.heading}</h1>
<ul>
${props.todos.map(function (todo) {
return `<li>${todo}</li>`;
}).join('')}
</ul>`;
}
});
When using a Data Store, a component will have no data
of its own. All state/data updates must happen by updating the store
.
// Add a todo item
store.data.todos.push('Take a nap... zzzz');
Try creating a Data Store on CodePen →
Setters & Getters
Reef’s reactive data
makes updating your UI as simple updating an object property.
But as your app scales, you may find that keeping track of what’s updating state and causing changes to the UI becomes harder to track and maintain.
Setters and getters provide you with a way to control how data flows in and out of your component.
Setters
Setters are functions that update your component or store data.
Create setters by passing in an object of setter functions with the setters
property in your options
object. The first parameter on a setter function is the store or component data. You can add as many other parameters as you’d like.
let store = new Reef.Store({
data: {
heading: 'My Todos',
todos: ['Swim', 'Climb', 'Jump', 'Play']
},
setters: {
// Add a new todo item to the component
addTodo: function (props, todo) {
props.todos.push(todo);
}
}
});
Use setter functions by calling the do()
method on your component or store. Pass in the name of the setter, along with any required arguments (except for props
).
// Add a new todo item
store.do('addTodo', 'Take a nap... zzzz');
When a component or store has setter functions, they become the only way to update app or store data.
This protects your component or store data from unwanted changes. The data
property always returns an immutable copy.
// This will NOT update the store.data or the UI
store.data.todos.push('Take a nap... zzzz');
Try working with setter functions on CodePen →
Getters
Getters are functions that parse data from your component or store and return a value.
They’re useful if you need to manipulate and retrieve the same data across multiple views of components. Rather than having to import helper functions, you can attach them directly to the component or store.
Create getters by passing in an object of getter functions with the getters
property in your options
object. The first parameter on a getter function is the store or component data. You can add as many other parameters as you’d like.
Support for parameters besides props
requires version 8.2.0
or higher.
let store = new Reef.Store({
data: {
heading: 'My Todos',
todos: ['Swim', 'Climb', 'Jump', 'Play']
},
getters: {
total: function (props) {
return props.todos.length;
}
}
});
Use getter functions by calling the get()
method on your component or store. Pass in the name of getter, along with any required arguments (except for props
).
// Get the number of todo items
store.get('total');
Try working with getter functions on CodePen →
Asynchronous Data
You can use asynchronous data (such as content from an API) in your templates.
Set an initial default value, make your API call, and update the data
property once you get data back. This will automatically trigger a render.
// Create an app
let app = new Reef('#app', {
data: {
articles: []
},
template: function (props) {
// If there are no articles
if (!props.articles.length) {
return `<p>There are no articles.</p>`;
}
// Otherwise, show the articles
return `
<ul>
${props.articles.map(function (article) {
return `<li>
<strong><a href="#">${article.title}.</a></strong>
${article.body}
</li>`;
}).join('')}
</ul>`;
}
});
// Fetch API data
// Then, update the app data
fetch('https://jsonplaceholder.typicode.com/posts').then(function (response) {
return response.json();
}).then(function (data) {
app.data.articles = data;
});
Try create a template from asynchronous data on CodePen →
You might also choose to hard-code a loading message in your markup.
<div id="app">Loading...</div>
Debugging
By default, Reef fails silently. You can put Reef into debug mode to expose helpful error message in the Console tab of your browser’s Developer Tools.
Turn debug mode on or off with the Reef.debug()
method. Pass in true
to turn it on, and false
to turn it off.
// Turns debug mode on
Reef.debug(true);
// Turns debug mode off
Reef.debug(false);
Routing
Reef includes two optional ways to handle URL/route management in your apps:
Both have been designed to integrate deeply with Reef, and share the same simple, lightweight approach.
Router
Reef Router is an optional router that you can use to handle URL/route management with your single-page apps (SPA’s).
- Automatically renders your Reef components whenever the route changes.
- Works with any link element. Unlike bigger frameworks, you don’t need custom routing components.
- Baked-in accessibility. Reef’s router automatically handles focus management and title updates.
- Supports real URL paths, with an optional hashbang pattern (
#!
) fallback. - Compatible with nested routing structures.
- Weighs just 2.7kb minified and gzipped.
Installation
Reef Router is just as easy to install as Reef itself. Reef must also be installed as a dependency.
The CDN is the fastest and simplest way to get started, but you can use importable modules or a direct download if you’d prefer.
<script src="https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/reefjs@8/dist/router.min.js"></script>
When used with a module system, you must explicitly associate Reef with the router with the ES Modules NPM CommonJS AMD Direct DownloadMore ways to install Reef Router
Reef.use()
method.import Reef from 'https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.es.min.js';
import ReefRouter from 'https://cdn.jsdelivr.net/npm/reefjs@8/dist/router.es.min.js';
Reef.use(ReefRouter);
import Reef from 'reefjs';
import ReefRouter from 'reefjs/router';
Reef.use(ReefRouter);
let Reef = require('https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.cjs.min.js');
let ReefRouter = require('https://cdn.jsdelivr.net/npm/reefjs@8/dist/router.cjs.min.js');
Reef.use(ReefRouter);
requirejs([
'https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.amd.min.js',
'https://cdn.jsdelivr.net/npm/reefjs@8/dist/router.amd.min.js'
],function (Reef, ReefRouter) {
Reef.use(ReefRouter);
});
<script src="path/to/reef.min.js"></script>
<script src="path/to/router.min.js"></script>
Getting Started
Step 1: Create your links
No custom components required. Any link element with an href
will work.
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
Step 2: Define your routes
Create a new ReefRouter()
instance to define your routes.
Every route requires a title
and url
. The title
can be a string, or a function that returns a string and accepts the route
object as an argument. You can add any additional properties that you want (for example, an id
for the route).
Optionally, you can use *
as a url
to catch any unmatched URLs.
let router = new ReefRouter({
routes: [
{
id: 'home',
title: 'Home',
url: '/'
},
{
id: 'about',
title: function (route) {
return 'About';
},
url: '/about'
},
{
id: 'contact',
title: 'Contact Us',
url: '/contact'
}
]
});
Step 3: Associate your router with one or more components
For any Reef component that should be updated when the route changes, add a router
property and use your router component as the value.
Details about the current route are automatically passed into the template
function as a second argument. If you don’t provide a catchall (url: '*'
) route, an unmatched route
will have a value of null
.
let app = new Reef('#app', {
router: router,
data: {
greeting: 'hello!'
},
template: function (props, route) {
return `
<h1>${route.title}</h1>
<p>${props.greeting}</p>`;
}
});
Note: when using a router, the element that the template was rendered into becomes the third argument on the template()
function.
Accessibility
Any time the route changes, any associated components automatically re-render.
The document.title
is also updated, and focus is shifted to the primary heading on the page (or an anchor element if scrolling to an anchored location).
Focus rings on headings
Headings and anchor locations will appear with a focus ring around them, which you may find visually unappealing.
Elements that don’t normally receive focus are given a tabindex
of -1
to make them focusable with JS. You can remove the focus ring by styling [tabindex="-1"]
.
[tabindex="-1"] {
outline: 0;
}
Note: you should NOT remove focus styles from elements that are natively focusable.
Server Configuration
Single page apps works great when you visit the homepage first. But when someone visits one of your routes directly or hits reload, they get a 404 page.
To fix that, you need to configure your server to point all pages to your index.html
file. Paul Sherman has a great deep-dive on how that all works titled Single-Page Applications and the Server.
Here’s an .htaccess
file example for apache servers.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.html [L]
</IfModule>
And here’s what it might look like in express (taken from the Reach Router docs).
const path = require('path');
const express = require('express');
const app = new express()
// requests for static files in the "public" directory
// like JavaScript, CSS, images will be served
app.use(express.static('public'));
// Every other request will send the index.html file that
// contains your application
app.use('*', function(req, resp) {
resp.sendFile('/public/index.html');
});
app.listen('8000');
Advanced Routing
Getting parameters from routes
You can include variable parameters in your URLs, either in the path itself or as query or search parameters.
Reef Router will add them to the route
object that gets passed into your template()
. Path parameters are included under the params
property, and query/search parameters are included under the search
property.
<ul>
<li><a href="/">Home</a></li>
<li><a href="/account/tom?photo=true">My Account</a></li>
</ul>
let router = new ReefRouter({
routes: [
{
id: 'home',
title: 'Home',
url: '/'
},
{
id: 'user-account',
title: function (route) {
return `User Account: ${route.params.user}`;
},
url: '/account/:user'
}
]
});
// In this example:
// route.params.user will be "tom"
// route.search.photo will be "true"
let app = new Reef('#app', {
router: router,
data: {
greeting: 'hello!'
},
template: function (props, route) {
return `
<h1>${route.title}</h1>
<p>${props.greeting} ${route.params.user}</p>
${route.search.photo ? `<p>
<img alt="A photo of ${route.params.user}" src="/img/${route.params.user}.jpg">
</p>` : ''}`;
}
});
Nested routes
Reef Router supports nested routes out-of-the-box.
The order does not matter. Reef Router will check deeper routes for matches first.
let router = new ReefRouter({
routes: [
{
id: 'home',
title: 'Home',
url: '/'
},
{
id: 'account',
title: 'Account',
url: '/account/'
},
{
id: 'user-account',
title: 'User Account',
url: '/account/:user'
},
{
id: 'user-password',
title: 'Change Password',
url: '/account/:user/password'
}
]
});
Redirects
As your app grows, routes may change. You can setup redirects from one route to another.
When creating the route, create the url
property as normal, and add a redirect
property with the route that the URL should point to.
let router = new ReefRouter({
routes: [
{
id: 'contact',
title: 'Contact',
url: '/contact/'
},
{
url: '/contact-us/',
redirect: '/contact/'
}
]
});
The redirect
property can also be a function that returns a string.
The function automatically receives the existing route
object, with URL and search parameters, as an argument.
let router = new ReefRouter({
routes: [
{
id: 'user-account',
title: 'User Account',
url: '/account/:user'
},
{
url: '/my-account/:user',
redirect: function (route) {
return `/account/${route.params.user}/`;
}
},
]
});
Options & Settings
In addition to your routes, Reef Router accepts a few options you can use to customize how the router behaves.
let router = new ReefRouter({
root: '', // The root URL for your app, if using a subdirectory
// The pattern to use for the document.title
// Receives the current route and route.title as arguments
title: function (route, title) {
if (route.url === '/') return 'My Awesome App';
return `${title} | My Awesome App`;
},
useHash: false // If true, uses a hashbang (#!) pattern instead of true URL paths
});
The title
property can be a string or a function that returns a string. As a function, it receives the current route
and the route.title
as arguments.
The useHash
property is automatically set to true
for local file:
pages.
Examples
The app lives at my-site.com/my-app/
.
let router = new ReefRouter({
root: '/my-app'
});
The document.title
will always be the router.title
.
let router = new ReefRouter({
title: function (route, title) {
return title;
}
});
The document.title
will always have | My Awesome App
after it, except on the homepage, where it’s just My Awesome App
.
let router = new ReefRouter({
title: function (route, title) {
if (route.url === '/') return 'My Awesome App';
return `${title} | My Awesome App`;
},
});
Always use the hashbang pattern.
let router = new ReefRouter({
useHash: true
});
Kudos! Reef Router’s URL path matching and parameter extraction is adapted from Navigo by Krasimir Tsonev. Click handling is adapted from pages.js by Vision Media.
Snorkel
Snorkel merges the speed of SPAs with the simplicity of multi-page apps.
How it works. You create separate HTML files for your app, and add links to them just like you normally would. Snorkel detects clicks on those links, fetches the HTML with ajax, and updates the UI without causing a page reload.
- Automatically renders your Reef components whenever the view changes.
- No need to specify routes. Just build your multi-page app like you normally would.
- Baked-in accessibility. Scroll position is reset on each view change, and Snorkel’s UI updates are automatically announced by screen readers.
- Weighs just 2.3kb minified and gzipped.
Installation
Snorkel is just as easy to install as Reef itself. Reef must also be installed as a dependency.
The CDN is the fastest and simplest way to get started, but you can use importable modules or a direct download if you’d prefer.
Snorkel requires version 8.2.0
or higher of Reef.
<script src="https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/reefjs@8/dist/snorkel.min.js"></script>
When used with a module system, you must explicitly associate Reef with Snorkel with the ES Modules NPM CommonJS AMD Direct DownloadMore ways to install Snorkel
Reef.use()
method.import Reef from 'https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.es.min.js';
import ReefSnorkel from 'https://cdn.jsdelivr.net/npm/reefjs@8/dist/snorkel.es.min.js';
Reef.use(ReefSnorkel);
import Reef from 'reefjs';
import ReefSnorkel from 'reefjs/snorkel';
Reef.use(ReefSnorkel);
var Reef = require('https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.cjs.min.js');
var ReefSnorkel = require('https://cdn.jsdelivr.net/npm/reefjs@8/dist/snorkel.cjs.min.js');
Reef.use(ReefSnorkel);
requirejs([
'https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.amd.min.js',
'https://cdn.jsdelivr.net/npm/reefjs@8/dist/snorkel.amd.min.js'
],function (Reef, ReefSnorkel) {
Reef.use(ReefSnorkel);
});
<script src="path/to/reef.min.js"></script>
<script src="path/to/snorkel.min.js"></script>
Getting Started
Once Snorkel is installed, activate it by creating a new ReefSnorkel()
instance.
let snorkel = new ReefSnorkel();
For any Reef component that should be updated when the route changes, add a router
property and use the Snorkel instance as the value.
var app = new Reef('#app', {
router: snorkel,
data: {
greeting: 'hello!'
},
template: function (props) {
return `
<h1>Good morning</h1>
<p>${props.greeting}</p>`;
}
});
What happens on a URL update?
Not much, actually.
- The entire
document.body
is replaced. - The
<head>
element is diffed, and changes are selectively applied. - The URL is updated using the
history.pushState()
method. - Any associated Reef components are rendered.
Advanced Features
Ignoring Links
By default, Snorkel works on all links that point to pages on the same domain as your app automatically.
You can tell Snorkel to ignore certain links by adding the [snorkel-ignore]
attribute to them or one of their parent elements.
<!-- This link will be ignored -->
<a snorkel-ignore href="/about">About Us</a>
<!-- All of these links will be, too -->
<nav snorkel-ignore>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
You can activate Snorkel on a specific link while ignoring others by adding the [snorkel]
attribute.
<!-- All of these links will be ignored, except for /about -->
<nav snorkel-ignore>
<ul>
<li><a href="/">Home</a></li>
<li><a snorkel href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
You can also disable Snorkel globally by passing in an object of options
, and setting autoLinks
to false
. Then, only links with the [snorkel]
attribute on them will use Snorkel.
let snorkel = new ReefSnorkel({
autoLinks: false
});
Replacing a view instead of creating a new one
By default, Snorkel creates a new entry in a browser’s navigation history. Users can go back to the previous page with the browser’s back button.
If you add the [snorkel="replace"]
attribute to a link (or parent element of a link), the new page will replace the existing entry in the navigation history instead of creating a new one.
<!-- This link will replace the current URL in browser history -->
<a snorkel="replace" href="/about">About Us</a>
Caching views in history
To improve performance when navigating back through browser history, Snorkel caches the previous 50 pages for an hour.
These are not used when a link is clicked, only when using a browser’s forward/back buttons. On link clicks, fresh content is always fetched.
You can change how many pages are cached, and for how long, using the cacheSize
and cacheTime
properties on the options
object. You can also disable caching entirely by seeing the cache
property to false
.
// Change the default cache size and duration
let snorkel = new ReefSnorkel({
cacheSize: 25, // Only cache the last 25 pages
cacheTime: 60 * 30 // 30 minutes in seconds
});
// Disable caching entirely
let snorkel = new ReefSnorkel({
cache: false
});
Options & Settings
In addition to your routes, Reef Router accepts a few options you can use to customize how the router behaves.
let snorkel = new ReefSnorkel({
loading: 'reef-loading', // A class that's added to the HTML element while new content is loading
root: '/', // The root domain for the app
});
API Reference
Reef, Snorkel, and Reef Router expose a set of options, methods, and custom events that you can hook into.
Global Methods
Reef.debug()
Turn debug mode on or off. Pass in true
to turn debug mode on, and false
to turn it off.
// Turn debug mode on
Reef.debug(true);
// Turn debug mode off
Reef.debug(false);
Reef.clone()
Create an immutable copy of an array, object, Map()
, or Set()
.
Reef.clone({});
Reef.trueTypeOf()
Get the true type of an object.
Reef.trueTypeOf([]); // "array"
Reef.trueTypeOf({}); // "object"
Reef.trueTypeOf(''); // "string"
Reef.trueTypeOf(1); // "number"
Reef.err()
Log a warning in the console conditionally only if debug mode is on.
Reef.debug('You did something, silly!');
Reef.emit()
Emit a custom event. Pass in the element to emit the event on, the event name, and optionally, event details as arguments.
// Emits the "awesome" event on the document
Reef.emit(document, 'awesome');
// Emit the "awesome" event on the #app element, with some details
let app = document.querySelector('#app');
Reef.emit(app, 'awesome', {
whoIs: 'You are'
});
You can listen for custom events with the Element.addEventListener()
method.
Instance Methods
Reef.prototype.render()
Render a Reef component in the UI.
let app = new Reef('#app', {
template: 'Hello world!'
});
app.render();
Reef.prototype.attach()
Attach one or more components to a Reef component. Pass in the component or an array of components as an argument.
let app = new Reef('#app', {
template: '<ul id="todos"></ul>'
});
let todos = new Reef('#todos', {
template: '<li>Build something with Reef</li>'
});
// Attach one item
app.attach(todos);
// Attach an array of items
app.attach([todos]);
Reef.prototype.detach()
Detach an attached component. Pass in the component or an array of components as an argument.
// Detach one component
app.detach(todos);
// Detach an array of components
app.detach([todos]);
Reef.prototype.do()
Run a setter function. Pass in the name of the setter, and a comma-separate list of any arguments.
let app = new Reef('#app', {
data: {
count: 0
},
template: function (props) {
return count;
},
setters: {
increase: function (props) {
props.count++;
}
}
});
// Run the increase setter
app.do('increase');
Reef.prototype.get()
Run a getter function. Pass in the name of the getter, and a comma-separate list of any arguments.
let app = new Reef('#app', {
data: {
count: 0
},
template: function (props) {
return count;
},
getters: {
count: function (props) {
return props.count;
}
}
});
// Run the count getter
app.get('count');
ReefSnorkel.prototype.run()
Start or restart a Snorkel instance. This is run automatically when instantiating a new Snorkel instance.
// A snorkel instance
let snorkel1 = new ReefSnorkel();
// Another instance
// This stops snorkel1 from running
let snorkel2 = new ReefSnorkel({
autoLinks: false
});
// This restarts snorkel1, and stops snorkel2
snorkel1.run();
ReefSnorkel.prototype.stop()
Stops a snorkel instance from running.
let snorkel = new ReefSnorkel();
snorkel.stop();
ReefSnorkel.prototype.visit()
Visit a URL. Pass the URL in as an argument.
let snorkel = new ReefSnorkel();
snorkel.visit('/about');
ReefSnorkel.prototype.clearCache()
Clear the cache for a snorkel instance.
let snorkel = new ReefSnorkel();
snorkel.clearCache();
ReefSnorkel.prototype.addComponent()
Add one or more Reef components to be automatically rendered whenever the route changes. Pass in the component or an array of components as an argument.
// A Reef component
let app = new Reef('#app', {
template: 'Hello world!'
});
// Snorkel
let snorkel = new ReefSnorkel();
// Add one component
snorkel.addComponent(app);
// Add an array of components
snorkel.addComponent([app]);
ReefSnorkel.prototype.removeComponent()
Remove one or more Reef components from being automatically rendered whenever the route changes. Pass in the component or an array of components as an argument.
// A Reef component
let app = new Reef('#app', {
template: 'Hello world!'
});
// Snorkel
let snorkel = new ReefSnorkel();
// Remove one component
snorkel.removeComponent(app);
// Remove an array of components
snorkel.removeComponent([app]);
ReefRouter.prototype.addRoutes()
Add one or more routes to a Reef Router instance. Pass in a route or array of routes as an argument.
let router = new ReefRouter();
// Add one route
router.addRoutes({
url: '/about',
title: 'About'
});
// Add an array of routes
router.addRoutes([
{
url: '/about',
title: 'About'
},
{
url: '/contact',
title: 'Contact Us'
}
]);
ReefRouter.prototype.removeRoutes()
Remove one or more routes from a Reef Router instance. Pass in a route or array of routes as an argument.
let router = new ReefRouter({
routes: [
{
url: '/about',
title: 'About'
},
{
url: '/contact',
title: 'Contact Us'
}
]
});
// Remove one route
router.removeRoutes({
url: '/about',
title: 'About'
});
// Remove an array of routes
router.removeRoutes([
{
url: '/about',
title: 'About'
},
{
url: '/contact',
title: 'Contact Us'
}
]);
ReefRouter.prototype.addComponent()
Add one or more Reef components to be automatically rendered whenever the route changes. Pass in the component or an array of components as an argument.
// A Reef component
let app = new Reef('#app', {
template: 'Hello world!'
});
// Reef Router
let router = new ReefRouter();
// Add one component
router.addComponent(app);
// Add an array of components
router.addComponent([app]);
ReefRouter.prototype.removeComponent()
Remove one or more Reef components from being automatically rendered whenever the route changes. Pass in the component or an array of components as an argument.
// A Reef component
let app = new Reef('#app', {
template: 'Hello world!'
});
// Reef Router
let router = new ReefRouter();
// Add one component
router.removeComponent(app);
// Add an array of components
router.removeComponent([app]);
ReefRouter.prototype.visit()
Visit a URL. Pass the URL in as an argument.
let router = new ReefRouter();
router.visit('/about');
ReefRouter.prototype.navigate()
(deprecated)
This method has been replaced with ReefRouter.prototype.visit()
. It still works, but will be removed in the next major version release.
Instance Properties
Reef.prototype.data
Get a reactive copy of the app data.
let data = app.data;
Reef.prototype.elem
The element the component is associated with. Returns a string or Node.
let elem = app.elem;
Reef.prototype.allowHTML
Whether or not HTML is allowed in the Reef component data. Returns a boolean.
let allowed = app.allowHTML;
ReefRouter.prototype.routes
An immutable array of the routes for a router instance.
let routes = router.routes;
ReefRouter.prototype.current
The current route for a router.
let current = router.current;
Events
Reef, Snorkel, and Reef Router emit custom events throughout the life cycle of a component or instance.
All Reef events follow a library:event
pattern. Unless otherwise specified, all events are emitted on the document
element. Event details can be accessed on the event.details
property.
// Listen for when Reef components are rendered into the DOM
document.addEventListener('reef:render', function (event) {
console.log(event.target); // The element it was rendered into
console.log(event.detail); // The data used for the render
});
Reef Events
reef:ready
is emitted when Reef is loaded into the DOM and ready to use.reef:initialized
is emitted when a new Reef component is initialized.event.detail
- the instance
reef:before-render
is emitted on an element before a new UI is rendered into it.event.target
- the element the component is being rendered intoevent.detail
- the current componentdata
event.preventDefault()
- stop the component from rendering
reef:render
is emitted on an element after a new UI is rendered into it.event.target
- the element the component was rendered intoevent.detail
- the componentdata
at time of render
reef:attached
is emitted when one or more components is attached to a component.event.detail
- an object with thecomponent
and an array ofattached
components
reef:detached
is emitted when one or more components is detached from a component.event.detail
- an object with thecomponent
and an array ofdetached
components
Snorkel Events
snorkel:ready
is emitted when Snorkel is loaded into the DOM and ready to use.snorkel:initialized
is emitted when a new Snorkel instance is initialized.event.detail
- the instance
snorkel:stopped
is emitted when a Snorkel instance is stopped.event.detail
- the instance
snorkel:before
is emitted before a new route is fetched.event.detail
- an object with thecurrent
andnext
URLsevent.preventDefault()
- stop the route from being fetched
snorkel:after
is emitted after a new route is fetched and loaded into the UIevent.detail
- an object with thecurrent
andprevious
URLs
snorkel:not-found
is emitted when a fetched route is not foundevent.detail
- an object with thecurrent
andnotFound
URLs
snorkel:components-added
is emitted when one or more components are added for rendering on route changes.event.detail
- an object with thesnorkel
instance and an array of addedcomponents
snorkel:components-removed
is emitted when one or more components are removed from rendering on route changes.event.detail
- an object with thesnorkel
instance and an array of removedcomponents
snorkel:cache-updated
is emitted when a URL is added to the internal cache.event.detail
- an object with theurl
andhtml
string
snorkel:cache-cleared
is emitted when the internal cache is cleared.event.detail
- the Snorkel instance
Router Events
router:ready
is emitted when Reef Router is loaded into the DOM and ready to use.router:initialized
is emitted when a new Router instance is initialized.event.detail
- the instance
router:before
is emitted before a new route is fetched.event.detail
- an object with thecurrent
andnext
URLsevent.preventDefault()
- stop the route from being fetched
router:after
is emitted after a new route is fetched and loaded into the UIevent.detail
- an object with thecurrent
andprevious
URLs
router:routes-added
is emitted when one or more routes are added to the router.event.detail
- an object with therouter
instance and an array of addedroutes
router:routes-removed
is emitted when one or more routes are removed from the router.event.detail
- an object with therouter
instance and an array of removedroutes
router:components-added
is emitted when one or more components are added for rendering on route changes.event.detail
- an object with therouter
instance and an array of addedcomponents
router:components-removed
is emitted when one or more components are removed from rendering on route changes.event.detail
- an object with therouter
instance and an array of removedcomponents
Deprecated Events
These are events that are still emitted, but will be removed in the next major version release. You should migrate to one of the events listed above whenever possible.
render
was replaced byreef:render
beforeRouteUpdated
was replaced byrouter:before
routeUpdated
was replaced byrouter:after
Options
All of the options for Reef, Snorkel, and Reef Router. This section is still a work-in-progress.
Reef Options
// This can be a string or a element
let elem = '#app';
new Reef(elem, {
// The component data
data: {},
// A component or array of components to attach to
attachTo: [],
// A data store to use
// If used, the data option is ignored
store: null,
// A router to use for this component
router: null,
// An object of setter methods
setters: {},
// An object of getter methods
getters: {}
});
Snorkel Options
new ReefSnorke({
// If true, automatically run Snorkel on all links
autoLinks: true,
// The selector for links to follow
// Only needed if autoLinks is false or if you've used an ignore selector
follow: '[snorkel]',
// The selector for links to ignore
ignore: '[snorkel-ignore]',
// The selector for links that should replace the current state
// (instead of adding a new entry to browser history)
replace: '[snorkel="replace"]',
// The class that's added to the HTML element while a new route is loading
loading: 'reef-loading',
// The root for your app
// If your app is on a sub-page, you may want to change this
root: '/',
// If true, cache views for use with the forward/back button
cache: true,
// How many items to store in cache
cacheSize: 50,
// How long to keep cached items for, in seconds
cacheTime: 3600,
});
Reef Router Options
new ReefRouter({
// An array of routes
routes: [],
// The root path for your app
root: '',
// The format to use for the document.title
// Can be a string, or a function that returns a string
// Accepts the route object and page title as arguments
title: function (route, title) {
return title;
},
// If true, use a hashbang pattern instead of real URLs
useHash: false
});
API Reference
Reef, Snorkel, and Reef Router expose a set of options, methods, and custom events that you can hook into.
Global Methods
Reef.debug()
Turn debug mode on or off. Pass in true
to turn debug mode on, and false
to turn it off.
// Turn debug mode on
Reef.debug(true);
// Turn debug mode off
Reef.debug(false);
Reef.clone()
Create an immutable copy of an array, object, Map()
, or Set()
.
Reef.clone({});
Reef.err()
Log a warning in the console conditionally only if debug mode is on.
Reef.err('You did something, silly!');
Reef.emit()
Emit a custom event. Pass in the element to emit the event on, the event name, and optionally, event details as arguments.
// Emits the "awesome" event on the document
Reef.emit(document, 'awesome');
// Emit the "awesome" event on the #app element, with some details
let app = document.querySelector('#app');
Reef.emit(app, 'awesome', {
whoIs: 'You are'
});
You can listen for custom events with the Element.addEventListener()
method.
Component Methods
Reef.prototype.render()
Render a Reef component in the UI.
let app = new Reef('#app', {
template: 'Hello world!'
});
app.render();
Reef.prototype.attach()
Attach one or more components to a Reef component. Pass in the component or an array of components as an argument.
let app = new Reef('#app', {
template: '<ul id="todos"></ul>'
});
let todos = new Reef('#todos', {
template: '<li>Build something with Reef</li>'
});
// Attach one item
app.attach(todos);
// Attach an array of items
app.attach([todos]);
Reef.prototype.detach()
Detach an attached component. Pass in the component or an array of components as an argument.
// Detach one component
app.detach(todos);
// Detach an array of components
app.detach([todos]);
Reef.prototype.do()
Run a setter function. Pass in the name of the setter, and a comma-separate list of any arguments.
let app = new Reef('#app', {
data: {
count: 0
},
template: function (props) {
return count;
},
setters: {
increase: function (props) {
props.count++;
}
}
});
// Run the increase setter
app.do('increase');
Reef.prototype.get()
Run a getter function. Pass in the name of the getter, and a comma-separate list of any arguments.
let app = new Reef('#app', {
data: {
count: 0
},
template: function (props) {
return count;
},
getters: {
count: function (props) {
return props.count;
}
}
});
// Run the count getter
app.get('count');
ReefSnorkel.prototype.run()
Start or restart a Snorkel instance. This is run automatically when instantiating a new Snorkel instance.
// A snorkel instance
let snorkel1 = new ReefSnorkel();
// Another instance
// This stops snorkel1 from running
let snorkel2 = new ReefSnorkel({
autoLinks: false
});
// This restarts snorkel1, and stops snorkel2
snorkel1.run();
ReefSnorkel.prototype.stop()
Stops a snorkel instance from running.
let snorkel = new ReefSnorkel();
snorkel.stop();
ReefSnorkel.prototype.visit()
Visit a URL. Pass the URL in as an argument.
let snorkel = new ReefSnorkel();
snorkel.visit('/about');
ReefSnorkel.prototype.clearCache()
Clear the cache for a snorkel instance.
let snorkel = new ReefSnorkel();
snorkel.clearCache();
ReefSnorkel.prototype.addComponent()
Add one or more Reef components to be automatically rendered whenever the route changes. Pass in the component or an array of components as an argument.
// A Reef component
let app = new Reef('#app', {
template: 'Hello world!'
});
// Snorkel
let snorkel = new ReefSnorkel();
// Add one component
snorkel.addComponent(app);
// Add an array of components
snorkel.addComponent([app]);
ReefSnorkel.prototype.removeComponent()
Remove one or more Reef components from being automatically rendered whenever the route changes. Pass in the component or an array of components as an argument.
// A Reef component
let app = new Reef('#app', {
template: 'Hello world!'
});
// Snorkel
let snorkel = new ReefSnorkel();
// Remove one component
snorkel.removeComponent(app);
// Remove an array of components
snorkel.removeComponent([app]);
ReefRouter.prototype.addRoutes()
Add one or more routes to a Reef Router instance. Pass in a route or array of routes as an argument.
let router = new ReefRouter();
// Add one route
router.addRoutes({
url: '/about',
title: 'About'
});
// Add an array of routes
router.addRoutes([
{
url: '/about',
title: 'About'
},
{
url: '/contact',
title: 'Contact Us'
}
]);
ReefRouter.prototype.removeRoutes()
Remove one or more routes from a Reef Router instance. Pass in a route or array of routes as an argument.
let router = new ReefRouter({
routes: [
{
url: '/about',
title: 'About'
},
{
url: '/contact',
title: 'Contact Us'
}
]
});
// Remove one route
router.removeRoutes({
url: '/about',
title: 'About'
});
// Remove an array of routes
router.removeRoutes([
{
url: '/about',
title: 'About'
},
{
url: '/contact',
title: 'Contact Us'
}
]);
ReefRouter.prototype.addComponent()
Add one or more Reef components to be automatically rendered whenever the route changes. Pass in the component or an array of components as an argument.
// A Reef component
let app = new Reef('#app', {
template: 'Hello world!'
});
// Reef Router
let router = new ReefRouter();
// Add one component
router.addComponent(app);
// Add an array of components
router.addComponent([app]);
ReefRouter.prototype.removeComponent()
Remove one or more Reef components from being automatically rendered whenever the route changes. Pass in the component or an array of components as an argument.
// A Reef component
let app = new Reef('#app', {
template: 'Hello world!'
});
// Reef Router
let router = new ReefRouter();
// Add one component
router.removeComponent(app);
// Add an array of components
router.removeComponent([app]);
ReefRouter.prototype.visit()
Visit a URL. Pass the URL in as an argument.
let router = new ReefRouter();
router.visit('/about');
ReefRouter.prototype.navigate()
(deprecated)
This method has been replaced with ReefRouter.prototype.visit()
. It still works, but will be removed in the next major version release.
Instance Properties
Reef.prototype.data
Get a reactive copy of the app data.
let data = app.data;
Reef.prototype.elem
The element the component is associated with. Returns a string or Node.
let elem = app.elem;
Reef.prototype.allowHTML
Whether or not HTML is allowed in the Reef component data. Returns a boolean.
let allowed = app.allowHTML;
ReefRouter.prototype.routes
An immutable array of the routes for a router instance.
let routes = router.routes;
ReefRouter.prototype.current
The current route for a router.
let current = router.current;
Events
Reef, Snorkel, and Reef Router emit custom events throughout the life cycle of a component or instance.
All Reef events follow a library:event
pattern. Unless otherwise specified, all events are emitted on the document
element. Event details can be accessed on the event.details
property.
// Listen for when Reef components are rendered into the DOM
document.addEventListener('reef:render', function (event) {
console.log(event.target); // The element it was rendered into
console.log(event.detail); // The data used for the render
});
Reef Events
reef:ready
is emitted when Reef is loaded into the DOM and ready to use.reef:initialized
is emitted when a new Reef component is initialized.event.detail
- the instance
reef:before-render
is emitted on an element before a new UI is rendered into it.event.target
- the element the component is being rendered intoevent.detail
- the current componentdata
event.preventDefault()
- stop the component from rendering
reef:render
is emitted on an element after a new UI is rendered into it.event.target
- the element the component was rendered intoevent.detail
- the componentdata
at time of render
reef:attached
is emitted when one or more components is attached to a component.event.detail
- an object with thecomponent
and an array ofattached
components
reef:detached
is emitted when one or more components is detached from a component.event.detail
- an object with thecomponent
and an array ofdetached
components
Snorkel Events
snorkel:ready
is emitted when Snorkel is loaded into the DOM and ready to use.snorkel:initialized
is emitted when a new Snorkel instance is initialized.event.detail
- the instance
snorkel:stopped
is emitted when a Snorkel instance is stopped.event.detail
- the instance
snorkel:before
is emitted before a new route is fetched.event.detail
- an object with thecurrent
andnext
URLsevent.preventDefault()
- stop the route from being fetched
snorkel:after
is emitted after a new route is fetched and loaded into the UIevent.detail
- an object with thecurrent
andprevious
URLs
snorkel:not-found
is emitted when a fetched route is not foundevent.detail
- an object with thecurrent
andnotFound
URLs
snorkel:components-added
is emitted when one or more components are added for rendering on route changes.event.detail
- an object with thesnorkel
instance and an array of addedcomponents
snorkel:components-removed
is emitted when one or more components are removed from rendering on route changes.event.detail
- an object with thesnorkel
instance and an array of removedcomponents
snorkel:cache-updated
is emitted when a URL is added to the internal cache.event.detail
- an object with theurl
andhtml
string
snorkel:cache-cleared
is emitted when the internal cache is cleared.event.detail
- the Snorkel instance
Router Events
router:ready
is emitted when Reef Router is loaded into the DOM and ready to use.router:initialized
is emitted when a new Router instance is initialized.event.detail
- the instance
router:before
is emitted before a new route is fetched.event.detail
- an object with thecurrent
andnext
URLsevent.preventDefault()
- stop the route from being fetched
router:after
is emitted after a new route is fetched and loaded into the UIevent.detail
- an object with thecurrent
andprevious
URLs
router:routes-added
is emitted when one or more routes are added to the router.event.detail
- an object with therouter
instance and an array of addedroutes
router:routes-removed
is emitted when one or more routes are removed from the router.event.detail
- an object with therouter
instance and an array of removedroutes
router:components-added
is emitted when one or more components are added for rendering on route changes.event.detail
- an object with therouter
instance and an array of addedcomponents
router:components-removed
is emitted when one or more components are removed from rendering on route changes.event.detail
- an object with therouter
instance and an array of removedcomponents
Deprecated Events
These are events that are still emitted, but will be removed in the next major version release. You should migrate to one of the events listed above whenever possible.
render
was replaced byreef:render
beforeRouteUpdated
was replaced byrouter:before
routeUpdated
was replaced byrouter:after
Options
All of the options for Reef, Snorkel, and Reef Router. This section is still a work-in-progress.
Reef Options
// This can be a string or a element
let elem = '#app';
new Reef(elem, {
// The component data
data: {},
// A component or array of components to attach to
attachTo: [],
// A data store to use
// If used, the data option is ignored
store: null,
// A router to use for this component
router: null,
// An object of setter methods
setters: {},
// An object of getter methods
getters: {}
});
Snorkel Options
new ReefSnorke({
// If true, automatically run Snorkel on all links
autoLinks: true,
// The selector for links to follow
// Only needed if autoLinks is false or if you've used an ignore selector
follow: '[snorkel]',
// The selector for links to ignore
ignore: '[snorkel-ignore]',
// The selector for links that should replace the current state
// (instead of adding a new entry to browser history)
replace: '[snorkel="replace"]',
// The class that's added to the HTML element while a new route is loading
loading: 'reef-loading',
// The root for your app
// If your app is on a sub-page, you may want to change this
root: '/',
// If true, cache views for use with the forward/back button
cache: true,
// How many items to store in cache
cacheSize: 50,
// How long to keep cached items for, in seconds
cacheTime: 3600,
});
Reef Router Options
new ReefRouter({
// An array of routes
routes: [],
// The root path for your app
root: '',
// The format to use for the document.title
// Can be a string, or a function that returns a string
// Accepts the route object and page title as arguments
title: function (route, title) {
return title;
},
// If true, use a hashbang pattern instead of real URLs
useHash: false
});
Browser Compatibility
Reef works in all modern browsers. That means:
- The latest versions of Edge, Chrome, Firefox, and Safari.
- Mobile Safari, Chrome, and Firefox on Safari.
- WebView, Chrome, and Firefox for Android.
If you need to support older browsers, you’ll need to transpile your code into ES5 with BabelJS.
License
The code is available under the MIT License.