Skip to main content Accessibility Feedback
Versions

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.

More ways to install Reef

ES Modules

Reef also supports modern browsers and module bundlers (like Rollup, Webpack, Snowpack, and so on) using the ES modules import syntax. Use the .es version.

import Reef from 'https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.es.min.js';

NPM

You can also use NPM (or your favorite package manager). First, install with NPM.

npm install reefjs --save

Then import the package.

import Reef from 'reefjs';

CommonJS

If you use NodeJS, you can import Reef using the require() method with the .cjs version.

let Reef = require('https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.cjs.min.js');

AMD

If you use RequireJS, SystemJS, and other AMD formats, you can import Reef with the .amd version.

requirejs(['https://cdn.jsdelivr.net/npm/reefjs@8/dist/reef.amd.min.js'], function (Reef) {
  //...
});

Direct Download

You can download the files directly from GitHub.

Compiled and production-ready code can be found in the 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();

Try the demo on CodePen →

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:

  1. Router for single-page apps.
  2. Snorkel for speedier multi-page 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>

More ways to install Reef Router

When used with a module system, you must explicitly associate Reef with the router with the Reef.use() method.

ES Modules

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);

NPM

import Reef from 'reefjs';
import ReefRouter from 'reefjs/router';

Reef.use(ReefRouter);

CommonJS

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);

AMD

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);
});

Direct Download

<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>

More ways to install Snorkel

When used with a module system, you must explicitly associate Reef with Snorkel with the Reef.use() method.

ES Modules

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);

NPM

import Reef from 'reefjs';
import ReefSnorkel from 'reefjs/snorkel';

Reef.use(ReefSnorkel);

CommonJS

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);

AMD

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);
});

Direct Download

<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

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 into
    • event.detail - the current component data
    • 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 into
    • event.detail - the component data at time of render
  • reef:attached is emitted when one or more components is attached to a component.
    • event.detail - an object with the component and an array of attached components
  • reef:detached is emitted when one or more components is detached from a component.
    • event.detail - an object with the component and an array of detached 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 the current and next URLs
    • event.preventDefault() - stop the route from being fetched
  • snorkel:after is emitted after a new route is fetched and loaded into the UI
    • event.detail - an object with the current and previous URLs
  • snorkel:not-found is emitted when a fetched route is not found
    • event.detail - an object with the current and notFound URLs
  • snorkel:components-added is emitted when one or more components are added for rendering on route changes.
    • event.detail - an object with the snorkel instance and an array of added components
  • snorkel:components-removed is emitted when one or more components are removed from rendering on route changes.
    • event.detail - an object with the snorkel instance and an array of removed components
  • snorkel:cache-updated is emitted when a URL is added to the internal cache.
    • event.detail - an object with the url and html 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 the current and next URLs
    • event.preventDefault() - stop the route from being fetched
  • router:after is emitted after a new route is fetched and loaded into the UI
    • event.detail - an object with the current and previous URLs
  • router:routes-added is emitted when one or more routes are added to the router.
    • event.detail - an object with the router instance and an array of added routes
  • router:routes-removed is emitted when one or more routes are removed from the router.
    • event.detail - an object with the router instance and an array of removed routes
  • router:components-added is emitted when one or more components are added for rendering on route changes.
    • event.detail - an object with the router instance and an array of added components
  • router:components-removed is emitted when one or more components are removed from rendering on route changes.
    • event.detail - an object with the router instance and an array of removed components

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 by reef:render
  • beforeRouteUpdated was replaced by router:before
  • routeUpdated was replaced by router: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 into
    • event.detail - the current component data
    • 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 into
    • event.detail - the component data at time of render
  • reef:attached is emitted when one or more components is attached to a component.
    • event.detail - an object with the component and an array of attached components
  • reef:detached is emitted when one or more components is detached from a component.
    • event.detail - an object with the component and an array of detached 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 the current and next URLs
    • event.preventDefault() - stop the route from being fetched
  • snorkel:after is emitted after a new route is fetched and loaded into the UI
    • event.detail - an object with the current and previous URLs
  • snorkel:not-found is emitted when a fetched route is not found
    • event.detail - an object with the current and notFound URLs
  • snorkel:components-added is emitted when one or more components are added for rendering on route changes.
    • event.detail - an object with the snorkel instance and an array of added components
  • snorkel:components-removed is emitted when one or more components are removed from rendering on route changes.
    • event.detail - an object with the snorkel instance and an array of removed components
  • snorkel:cache-updated is emitted when a URL is added to the internal cache.
    • event.detail - an object with the url and html 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 the current and next URLs
    • event.preventDefault() - stop the route from being fetched
  • router:after is emitted after a new route is fetched and loaded into the UI
    • event.detail - an object with the current and previous URLs
  • router:routes-added is emitted when one or more routes are added to the router.
    • event.detail - an object with the router instance and an array of added routes
  • router:routes-removed is emitted when one or more routes are removed from the router.
    • event.detail - an object with the router instance and an array of removed routes
  • router:components-added is emitted when one or more components are added for rendering on route changes.
    • event.detail - an object with the router instance and an array of added components
  • router:components-removed is emitted when one or more components are removed from rendering on route changes.
    • event.detail - an object with the router instance and an array of removed components

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 by reef:render
  • beforeRouteUpdated was replaced by router:before
  • routeUpdated was replaced by router: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.