Maple.js is a React webcomponents based framework mixing ES6 with Custom Elements, HTML Imports and Shadow DOM. It has in-built support for SASS and JSX, including a Gulp task for vulcanizing your project.



Travis   Bower   npm   License MIT

Maple is a seamless module that allows you to organise your React project in terms of webcomponents — with HTML Imports, Shadow DOM, and Custom Elements — allowing you to implement any Flux architecture you choose, and then compile with Mapleify for production.


Getting Started

🍁 Watch "Getting Started with Maple": (Previous)

💎 Install all dependencies and start server using npm start.

Given the typical Flux architecture where components reside in their respective components directory, we continue that trend in Maple, where one component can register one or many custom elements – but each HTML document can only have one template element.

Within the directory my-app/components we create our component's index that will be imported — date-time.html — which will import its associated JavaScript and CSS documents:

    <script type="text/jsx" src="date-time.js"></script>
    <script type="text/javascript" src="../../../vendor/moment/moment.js"></script>
    <link rel="stylesheet" type="text/css" href="date-time.css" />

Note: When we import the date-time.js file we use the local path, which Maple.js understands as being a part of the module – whereas our third-party module — moment.js — resides outside of the component's directory and is therefore imported into the window scope.

Within our CSS file, we can be as loose as we like, because the date-time.js component will be imported under its own shadow boundary, preventing the styles from bleeding over into other components — even components that are children of our component.

We next need to add some standard ES6 React code to our date-time.js to make it return a date and time when rendered:

export default class MyDateTime extends React.Component {

    render() {
        let dateTime = moment().format(this.props.format || 'YYYY-MM-DD');
        return <time>{dateTime}</time>


Note: You could use the React.createElement('datetime', null, dateTime) approach as well – the System.import we use recognises when it's a JSX file and will transpile it automatically for you.

By looking at the above React component, we can immediately deduce that the eventual custom element will be called my-date-time. For those eagle-eyed individuals amongst us, you'll have noticed we use this.props.format to specify the date/time format – and this is something we'll pass into our component when adding the custom element to the DOM.

Next all we need to do is add a little CSS to our date-time.css document:

time {
    color: rebeccapurple;
    font-family: Arial, Tahoma, Helvetica, sans-serif;

And finally import the component into our main index.html document that includes the maple.js and react.js imports:

<link rel="import" type="text/html" href="my-app/components/time-date/index.html" />

Note: You may have noticed that the component's directory name is largely irrelevant – and it is, in most cases. However, there are certain circumstances where the component's directory matters – such as when registering a Worker — In this case Maple provides the component directory as this.props.path.

Once the HTML document has been imported, Maple will register our custom element and it will be then usable in our application – although don't forget that we should pass in the optional format attribute to override YYYY-MM-DD:

<my-date-time data-format="YYYY-MM-DD HH:mm"></my-date-time>

Note: In the above example we use data-format, whereas our React component expects format — you'll be glad to know that in these cases, Maple strips the data- segment from the attribute, which allows you to write perfectly valid HTML5 syntax.

Component Path

From within your React component, use the this.props.path.getRelativePath() to get the path of the current component – with this information, you can easily register Workers and other relatively stored documents:

let name   = 'MyWebWorker.js',
    path   = `${this.props.path.getRelativePath()}/${name}`,
    worker = new Worker(path);

Ignore Import

Importing a HTML file may not require Maple at all, and therefore if the imports were left to be processed by Maple this would be a waste of resources – as no components would be contained within the import. For these cases you can add the data-ignore attribute to the HTML import, and Maple will leave them unprocessed:

<link rel="import" type="text/html" href="example.html" data-ignore />

Multiple Elements

Each HTML document can have exactly one template element registering components. In cases where you want to register multiple components, you must split them into their individual HTML documents for developers to import separately. For instance, a DateTime component could yield date-time-gmt, date-time-bst, etc... Each element can have its own associated CSS documents as well. There are two approaches for this:

  1. Create two HTML documents: index-gmt.html and index-bst.html and require them to be imported separately;
  2. Create one HTML import with one template node and import both JS documents with a shared CSS document:
    <script type="text/javascript" src="datetime-gmt.js"></script>
    <script type="text/javascript" src="datetime-bst.js"></script>
    <link rel="stylesheet" type="text/css" href="shared.css" />

Choosing between the two approaches should be evident – if you want to apply custom CSS documents to each component individually — datetime-gmt.css to one, and datetime-bst.css to the other — then you should have two HTML documents. Otherwise if the two are directly related, and share the same CSS and JS documents, then they can be kept together in one HTML document.

JSX Compilation

In development environments it is often useful to compile JSX documents — Maple supports JSX compilation. All you have to do is import JSX the usual JSX way using the text/jsx type:

    <script type="text/jsx" src="my-jsx-document.js"></script>

Note: When using Mapleify to render your app – Mapleify merely changes the type of your script elements from text/jsx to text/javascript and changes the extensions from .jsx to .js (pre v1.2.0 when JSX files were included with JSX extensions) – it's left entirely up to the developer to write their Gulp/Grunt scripts to convert their JSX — and SASS — documents prior to Mapleify compilation.

Also Note: Since the release of v1.2.0 JSX files must have a JS extension – also JSX files will import just fine using a text/javascript type, too.

Nested Shadow Boundaries

As Maple uses Custom Elements to create the components, it's straightforward to have components within components – you only need to place your Custom Element node into your React component:

render() {
    return <li><date-time data-unix={}></date-time></li>

SASS Transpiling

🍁 Watch "SASS to CSS":

In a development environment Maple supports transpiling SASS documents to CSS documents – for production you should use your build tool to transpile SASS to CSS documents.

Maple uses Sass.js and can be installed separately:

bower install sass.js -D

Once you have included sass.js all documents that are included with the type text/scss will be automatically transpiled for you to CSS before being appended to the shadow boundary:

<link type="text/scss" href="default.scss" />

Resolved Components (FOUC)

🍁 Watch "Preventing FOUC":

Maple uses the same mechanism as Polymer when it comes to preventing FOUC. Place the unresolved attribute on each element, and then once they're upgraded by Maple, the unresolved attribute will be replaced with the resolved attribute:

<date-time unresolved></date-time>

With the following styles the date-time element will fade in gradually once upgraded:

date-time {
    opacity: 0;
    display: block;
    transition: opacity 3s;

date-time[resolved] {
    opacity: 1;

Mutation Observer

🍁 Watch "Mutation Observer":

Maple uses the MutationObserver to listen for changes to the document.head element – if new elements are added to the node, then Maple will eagerly attempt to resolve the HTML imports and load them dynamically.

For components to be processed by the mutation observer, link elements must:

  • Be a child of the document.head element;
  • Pass the utility.isHTMLImport method;

The utility.isHTMLImport method checks for the following to determine whether the newly added element is a valid link import:

  • Is an instance of HTMLLinkElement;
  • rel attribute resolves to string import;
  • Has the href attribute defined;
  • type attribute resolves to string text/html;

Once the element has passed the aforementioned check, Maple will load in the component and it will be ready to use. As an example, let's dynamically load our DateTime component from the first tutorial:

var linkElement = document.createElement('link');
linkElement.setAttribute('href', 'app/components/todo-form/index.html');
linkElement.setAttribute('type', 'text/html');
linkElement.setAttribute('rel', 'import');

It's worth noting that the above code contains a fair amount of boilerplate code, which is why you'll likely want to have a wrapper function for this. After the linkElement has been appended to the document.head element, Maple will resolve the HTML import via the MutationObserver.

Extending Native Elements

⚠️ Not yet supported – merged and pending release.

🍁 Watch "Extending Elements":

By default all of your Maple components will be simple elements. For example, the class DateTime object will create an element called date-time – in cases where you'd like the element to be specialised — such as extending the HTMLButtonElement then you need to modify the object's name:

export default class DateTime_Button {}

In the above case the element will still be registered as date-time – but now the date-time element will extend HTMLButtonElement.prototype:

<button is="date-time">
    DateTime Button!

Mapleify (Vulcanization)

For development purposes the HTML Imports are an acceptable design implementation – however when pushing to production — as you do with Polymer — you'll want to minify and concatenate your resources. In Polymer you would use vulcanize – Maple utilises vulcanize to create Mapleify which compiles your HTML document.

You can install Mapleify globally with npm: npm install mapleify -g – it can then be used from your terminal:

mapleify -i index.html (default renders to mapleify.html – change with -o rendered.html)

Browser Support

Chrome Firefox Opera Safari Safari

Note: Example has also been tested in IE11 where it seems to be functioning well.

Maple also comes distributed with a dist/maple-polyfill.js file that includes all necessary polyfills for the widest possible support in modern browsers.

Example Todo

We have a typical todo example on Heroku which uses Maple along with the Alt.js Flux library. Everything should look familiar to a seasoned React.js developer – with the customary stores and actions – where the codebase differs is in the components directory, where each of the three components are written in ES6 and exported using export default.


It's crucial to know how Maple traverses the DOM to find your CSS/SASS and JS documents. Maple attempts to adhere to the HTML5 standard – and therefore if you notice something amiss, please open an issue!

  • External CSS: Must have rel="stylesheet" – all other attributes optional;
  • Inline CSS: Optional type="text/css" attribute;
  • HTML Imports: Must have rel="import" – all other attributes optional;
  • Inline Templates: Must have a ref attribute – automated by Mapleify;
  • External JS: Optional type="text/css" attribute – matches JSX with type="text/jsx";


In some cases it may be desirable to prepend a namespace to all custom elements – especially in the case where you're loaded a third-party import and are unable to touch their custom elements directly. In these instances Maple allows you to specify a namespace when importing the document:

<link rel="import" href="app/components/date-time/index.html" data-namespace="x" />

By specifying the data-namespace attribute, you effectively prepend x to all custom elements imported by that document. Therefore if date-time defined a date-time element, with the data-namespace attribute as x the element would now be x-date-time which helps to prevent naming conflicts.


Maple uses Polymer's wct testing tool – which relies on the Chai assertion library.

  • npm install
  • bower install
  • gulp test

Optionally you may also invoke the wct testing yourself by issuing the wct command in your terminal.

You might also like...
Simple and elegant component-based UI library
Simple and elegant component-based UI library

Simple and elegant component-based UI library Custom components • Concise syntax • Simple API • Tiny Size Riot brings custom components to all modern

An event-based global state management tool for vue, react, mini-program, ect.

hy-event-store An event-based global state management tool for vue, react, mini-program, etc. 一个基于事件的全局状态管理工具,可以在Vue、React、小程序等任何地方使用。 设计灵感 在项目中找到一个更加

A reactive filesystem interface based on Vue 3 reactivity system.

A reactive filesystem interface based on Vue 3 reactivity system.

A performant, scalable and pluggable approach to instrumenting your React application.
A performant, scalable and pluggable approach to instrumenting your React application.

react-i13n react-i13n provides a performant, scalable and pluggable approach to instrumenting your React application. Typically, you have to manually

Fire7 is a small library that implements real-time data binding between Firebase Cloud Firestore and your Framework7 app.

Fire7 is a small library that implements real-time data binding between Firebase Cloud Firestore and your Framework7 app.

Harness the power of reactive programming to supercharge your components
Harness the power of reactive programming to supercharge your components

Handle your component effects and side-effects in a clear and declarative fashion by using asynchronous data streams (reactive programming). Why? · In

A spring that solves your animation problems.

React-Motion import {Motion, spring} from 'react-motion'; // In your render... Motion defaultStyle={{x: 0}} style={{x: spring(10)}} {value = div

Magic Quadrant built with React & Typescript

Magic Quadrant Interactive Magic Quadrant built with React & Typescript Demo Demo Link Firebase: Usage Install D

A lightweight reactivity API for other UI libraries to be built on top of.
A lightweight reactivity API for other UI libraries to be built on top of.

This is a tiny (~850B minzipped) library for creating reactive observables via functions. You can use observables to store state, create computed properties (y = mx + b), and subscribe to updates as its value changes.

  • Thrice loaded sass.worker.js in TodoList Demo App

    Thrice loaded sass.worker.js in TodoList Demo App

    Hi, I just had a look on your demo-app and wondered why it is loading the sass.worker.js file 3 times with the initial load:

    screen shot 2015-05-29 at 16 08 51

    Is there any reason why you are doing this, or is this a bug? The file is with 2,6 Mb very big, so I may not want to load this with my smartphone over mobile network ;-)

    opened by dustin-H 1
  • Update for V1 of Web Components?

    Update for V1 of Web Components?

    V1 of Web Components just became available in Chrome 53 Beta

    Do you have any plans to update Maple.js?

    opened by idibidiart 0
Adam Timberlake
Frequent traveller and 10k runner. Occasional solipsist and part-time vexillologist. Passion for FP, web components, and fluffy creatures with 4 legs. 🐱
Adam Timberlake
A wrapper for placing elements along div borders.

Test in Browser Installation npm npm install react-border-wrapper yarn yarn add react-border-wrapper Usage Use the border wrapper in the same way you

Christopher Powroznik 155 Nov 4, 2022
Demo app for integration, a react-based framework for rapid building of internal tools.

Demo app for integration, a react-based framework for rapid building of internal tools.

Permify 7 Apr 28, 2022
A tiny library aims to parse HTML to Figma Layer Notation and do it well.

TSDX User Guide Congrats! You just saved yourself hours of work by bootstrapping this project with TSDX. Let’s get you oriented with what’s here and h

青岚 5 Dec 28, 2022
Automatically AJAXify plain HTML with the power of React. It's magic!

React-Magic and HTMLtoJSX React-Magic is an experimental library that uses the power of Facebook's React library to inject AJAX-loading goodness into

React Community 921 Dec 16, 2022
Lightweight react-like library. Support for asynchronous rendering and hooks.

Recept · Lightweight react-like library. Like the name, this project is mainly based on the architectural idea of react, which can feel react more int

RuiLin Dong 52 Sep 17, 2022
Mobile App Framework powered by React

TouchstoneJS Mobile App Framework powered by React. See the demo at Follow @touchstonejs on Twitter for updates. See the touchstone-s

TouchstoneJS 3.3k Jan 5, 2023
A React framework for building text editors.

Draft.js Draft.js is a JavaScript rich text editor framework, built for React and backed by an immutable model. Extensible and Customizable: We provid

Facebook 22.3k Dec 31, 2022
A completely customizable framework for building rich text editors. (Currently in beta.)

A completely customizable framework for building rich text editors. Why? · Principles · Demo · Examples · Documentation · Contributing! Slate lets you

Ian Storm Taylor 26.2k Jan 9, 2023
👻 Zero-configuration framework-agnostic static prerendering for SPAs

react-snap Pre-renders a web app into static HTML. Uses Headless Chrome to crawl all available links starting from the root. Heavily inspired by prep

null 4.8k Jan 7, 2023
The simple but very powerful and incredibly fast state management for React that is based on hooks

Hookstate The simple but very powerful and incredibly fast state management for React that is based on hooks. Why? • Docs / Samples • Demo application

Andrey 1.5k Jan 7, 2023