hybrids.js — Declarative and Functional Web Components

Hybrids is a UI library for creating Web Components with a strong declarative & functional approach based on plain objects & pure functions

Indrek Lasn
Nov 3 · 4 min read
Photo by Clément H on Unsplash

Web Components is a suite of different technologies allowing you to create reusable custom elements — with their functionality encapsulated away from the rest of your code — and utilize them in your web apps.

If you’re new to web components, check out my previous post, “Web Components API In A Nutshell.”

The Hybrid Package includes the following:

  • The simplest definition — just plain objects and pure functions — no class and this syntax.
  • No global lifecycle — independent properties with their own simplified lifecycle methods.
  • Composition over inheritance — easy re-use, merge, or split property definitions.
  • Superfast recalculation — built-in smart cache and change detection mechanisms.
  • Templates without external tooling —a template engine based on tagged template literals.
  • Developer tools included — hot module replacement support for a fast and pleasant development.
hybrids Github Page

Getting Started

Install the npm package with NPM or Yarn.


npm i hybrids


yarn add hybrids

Then, import the required features and define a custom element. Here’s a simple example for a counter.

Run the demo on StackBlitz.

Finally, use your custom element in HTML:

<simple-counter count="10"></simple-counter>

The hybrids library provides a simple and declarative way for creating custom elements. Its name is taken from the idea that it’s a mix of functional and object-oriented architecture with a unique approach for defining custom elements, just like this:

Even though the above code might look straightforward, there are applied unique concepts that make it possible. This example relies on the three property-related ideas used together: descriptors, factories, and translation.

Additionally, the library uses change detection and cache mechanism to simplify the lifecycle of the component.

Simplified component lifecycles

The lifecycle of the component is reversed comparing to common solutions. What usually would be an effect of state computation is here a cause for that computation. Every single property (including render) is independent. If it requires other properties from the component, it calls them. Only then are the values of those properties calculated. We can illustrate this with the following diagram:

Lifecycle with cache and change detection

The render property (an update function, which manipulates DOM) requires the current state taken from other properties — not the opposite. Setting the state within some lifecycle callback (even asynchronously fetched data) isn’t needed. The change detection mechanism allows triggering update function only when one of the component properties changes.

As a result, we can easily create a component structure as a list of properties (inputs) and pure render function (output) that reflects the current state of the component to the DOM.

In this concept, side effects are outside of the component scope. They’re outcomes of the user input or other DOM events attached to the internal structure of the component. Render pattern used in the library allows us to implement them as ordinary functions that take custom element instance and change property values (inputs). Those changes will eventually render the property to update, but only when it’s needed.

ES modules

If you target modern browsers and don’t want to use external tooling (like webpack or parcel), you can use ES modules:

<script type="module">// We can use "/src" here - browsers, which support modules also support ES2015import { html, define } from 'https://unpkg.com/hybrids@[PUT_VERSION_HERE:x.x.x]/src';...</script>

Please take into account the fact that it doesn’t provide code minification and loads all required files in separate requests.

Hot Module Replacement

HMR works out of the box, but your bundler setup may require an indication that your entry point supports it. For webpack and parcel, add the following code to your entry point:

// Enable HMR for developmentif (process.env.NODE_ENV !== 'production') module.hot.accept();

If your entry point imports files that don’t support HMR, you can place the above snippet in a module where you define a custom element. (where define method is used).


The hybrids documentation is available at hybrids.js.org or in the docs path of the repository:

Live Examples

Here are a couple of examples of projects built with the hybrids web components.

Browser Support

Hybrids library is widely supported by most common browsers.

Browser support for Hybrids


Thanks for reading, I hope you discovered something new and useful. Stay curious, and happy coding!

Better Programming

Advice for programmers.

Indrek Lasn

Written by

Engineer, founder. Let’s change the world together for the better. Follow me on Twitter @ https://twitter.com/lasnindrek

Better Programming

Advice for programmers.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade