Skip to content
🇨🇭Switzerland takes a functional approach to Web Components by applying middleware to your components. Supports Redux, mobx, attribute mutations, CSS variables, React-esque setState/state, etc… out-of-the-box, along with Shadow DOM for style encapsulation and Custom Elements for interoperability.
JavaScript CSS Other
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
bin Changed Prettier 'printWidth' to 100 Apr 9, 2019
example Fixed server rendering and "type" of "module" May 30, 2019
media Updated logo Sep 13, 2018
src Fixed server rendering and "type" of "module" May 30, 2019
.eslintignore Added .eslintignore file for the CLI build Mar 14, 2019
.gitignore Fixed deps for the CLI tool Jan 26, 2019
.npmignore Added readlint dependency Dec 16, 2018
.npmrc Added npmrc file to prevent package-lock.json creation Aug 24, 2018
.prettierignore Added MD files to Prettier formatting Mar 28, 2019
.prettierrc.json Changed Prettier 'printWidth' to 100 Apr 9, 2019
.stylelintrc Added stylelint Aug 23, 2018
.taskfile.yml Fixed server rendering and "type" of "module" May 30, 2019
Dockerfile Re-added Docker Sep 12, 2018 Added LICENSE file Sep 14, 2018 README correction Mar 31, 2019
ava.config.js Updated deps Jun 14, 2019
package.json Updated deps Jun 14, 2019
terser.config.js Changed Prettier 'printWidth' to 100 Apr 9, 2019
yarn.lock Updated deps Jun 14, 2019


Switzerland takes a functional approach to Web Components by applying middleware to your components. Supports Redux, mobx, attribute mutations, CSS variables, React-esque setState/state, etc… out-of-the-box, along with Shadow DOM for style encapsulation and Custom Elements for interoperability.

Travis   npm   License MIT   Coveralls   code style: prettier

npm: npm install switzerland --save
dkr: docker pull wildhoney/switzerland



  1. Getting Started
  2. Elements
  3. Philosophy
  4. Middleware
  5. CLI

Getting Started

As Switzerland is functional its components simply take props and yield props – middleware can have side-effects such as writing to the DOM, and can also be asynchronous by yielding a Promise. Middleware is processed on each render from left-to-right which makes components very easy to reason about. In the example below we create a component called x-countries that enumerates a few of the countries on planet earth:

import { create, m } from 'switzerland';

create('x-countries', m.html(({ h }) =>
    h('ul', {}, [
        h('li', {}, 'United Kingdom'),
        h('li', {}, 'Russian Federation'),
        h('li', {}, 'Republic of Indonesia')

We now have a usable custom element called x-countries which can be used anywhere. We're able to use the element even before the element is declared, as Switzerland subscribes to the progressive enhancement paradigm whereby elements are upgraded asynchronously. In the meantime you could display a loader, placeholder or even nothing at all before the component renders.

<x-countries />

For the x-countries component we only have one middleware function – the html middleware which takes props and yields props but has a side-effect of writing to the DOM using superfine's implementation of virtual DOM. It's worth noting that Switzerland doesn't encourage JSX as it's non-standard and unlikely to ever be integrated into the JS spec, and thus you're forced to adopt its associated toolset in perpetuity. However there's nothing at all preventing you from introducting a build step to transform your JSX into hyperdom.

Let's take the next step and supply the list of countries via HTML attributes. For this example we'll use the Switzerland types which transform HTML string attributes into more appropriate representations, such as Number, BigInt, etc...

import { create, m, t } from 'switzerland';

    m.attrs({ values: t.Array(t.String) }),
    m.html(({ attrs, h }) =>
        h('ul', {}, => h('li', {}, country)))

Notice that we've now introduced the attrs middleware before the html middleware; we have a guarantee that attrs has completed its work before passing the baton to html. It's the responsibility of the attrs middleware to parse the HTML attributes into a standard JS object, and re-render the component whenever those attributes are mutated. Since the list of countries now comes from the values attribute, we need to add it when using the custom element:

<x-countries values="United Kingdom,Russian Federation,Republic of Indonesia" />

By taking a reference to the x-countries element and mutating the values attribute we can force a re-render of the component with an updated list of countries:

const node = document.querySelector('x-countries');
node.attributes.values = `${node.attributes.values},Hungary,Cuba`;

Switzerland components only take string values as their attributes as that's all the HTML spec allows. Using the types we can transform those string values into JS values, and with this approach we allow for greater interoperability. Components can be used as pure HTML, using vanilla JS, or inside React, Vue, Angular, etc... Passing complex state to components only reduces their reusability.

Where other JS libraries fall short, Switzerland considers all web assets to be within its remit. For example in React it is fairly common to use a third-party, non-standard, somewhat hacky JS-in-CSS solution that brings its own set of complexities and issues. With Switzerland it's easy to package up a regular CSS file alongside the component, and have the assets it references load relative to the JS document without any configuration. For that we simply render a style node in the html middleware – or the template middleware if we choose to use JS template literals:

import { create, init, m, t } from 'switzerland';

const path = init(import.meta.url);

    m.attrs({ values: t.Array(t.String) }),
    m.html(({ attrs, h }) =>
        h('section', {}, [
            h('ul', {}, => h('li', {}, country)))

Notice that we've also added the m.boundary middleware function which attaches a shadow boundary to our custom element. We do this so that our applied styles are encapsulated to the component itself, rather than bleeding into other elements on the page.

We use the h.sheet helper function that uses @import to import a CSS document into the DOM, which also specifies a static key based on the path to prevent the CSS from being constantly downloaded on re-render. In using the init function we have a function that allows us to resolve assets relative to the current JS file:

:host {
    padding: 15px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.25);
    background-image: url('./images/world.png');

By utilising shadow DOM you're able to keep your CSS documents as general as possible, since none of the styles defined within it will leak into other elements or components. As the CSS document is imported, all assets referenced inside the CSS document are resolved relative to it. Switzerland also has a special function before a component is resolved to ensure all imported CSS files have been loaded into the DOM.

Adding events to a component is achieved through the utils.dispatch function which is passed through the props. In our case we'll set an event up for when a user clicks on a country name. Switzerland uses the native CustomEvent to handle events, and thus guaranteeing our components stay interoperable and reusable:

import { create, init, m, t } from 'switzerland';

const path = init(import.meta.url);

    m.attrs({ values: t.Array(t.String) }),
    m.html(({ attrs, utils, h }) =>
        h('section', {}, [
            h('ul', {}, => (
                h('li', {
                    onclick: () => utils.dispatch('clicked-country', { country })
                }, country)

Interestingly it's possible to use any valid event name for the utils.dispatch as we simply need a corresponding addEventListener of the same name to catch it. Once we have our event all set up we can attach the listener by using the native addEventListener method on the custom element itself:

const node = document.querySelector('x-countries');
node.addEventListener('clicked-country', event => (
    console.log(`Country: ${}!`)


Helpfully provided in the library are a set of custom elements that you can use in your own projects.


One of the largest downsides to creating components in React, Vue, Ember, etc... is that we re-invent the wheel time-and-time again with every new framework that comes about. Although their components may rely on more generic modules, we are still writing components specific to a certain framework, and typically within a certain version range — if our setup lies outside of those constraints then we need to continue our search.

For example, if somebody writes a <mayan-calendar /> component that works nicely with Mayan dates, wouldn't it be nice if we could use that component wherever, irrespective of our chosen framework and version? If there was a ReactMayanCalendar that works with React 15.x then we'd be out of luck if our setup was Ember based — or React 16.x based.

Thankfully by utilising custom elements which are native to the browser, we can write interoperable components that can be used anywhere — on their own or in a framework. In addition we inherit other benefits, such as style encapsulation to prevent cross-contamination, and relative loading of CSS documents and associated images.

Plug & Play

Switzerland is capable of being integrated into any website or app without any formal installation or build process if you wish. Thanks to shadow DOM technology, all styles are also applied since Switzerland detects which host the JS originated from; if the origin and the JS host differ, then absolute paths to the domain are used when loading assets, such as CSS documents and images.

As a little teaser, navigate to and paste the following snippet of code into the console:

const node = document.createElement('script');
node.type = 'module';
node.src = '';

After a couple of milliseconds you should see the todo app embedded into Google with all of the styles applied. If you have any todos in your list then you will also see those due to the IndexedDb that the example utilises. It's worth noting that for this example to work correctly, the host — in the above case — needs the CORS headers configured correctly.


  • adapt – Uses ResizeObserver to re-render whenever the component's dimensions change;
  • attrs – Provides the parsing and observing of a node's attributes.
  • blend – Keep your functions general when using as middleware functions.
  • defer – Invokes function after x milliseconds if the current render hasn't completed.
  • delay – Awaits by x milliseconds before continuing to the next middleware item.
  • wait – Await the resolution of other components to make rendering atmoic.


Switzerland provides a simple CLI tool that allows you to quickly create a file and directory structure for your component. There are currently two flavours of hierarchies which you can specify by using the style option.

Install a version of Switzerland globally, and then use the CLI tool to create your component – in this case the x-countries component in the packages directory:

foo@bar:~$ swiss packages/x-countries

Options supported include:

  • --name use a different name for the component than is specified using the directory path.
  • --version use a specific version of the Switzerland library.
  • --style basic|complex use the complex style over the default basic.
  • --overwrite overwrite an existing component when it already exists.
  • --novalidate prevent the validation of custom element names.
  • --test-runner use another test runner instead of the default ava.
You can’t perform that action at this time.