Making
a Javascript Framework

A story of over five years of coding told in five minutes

Dominik Lubański

Goals

  • Dependency-free
  • Runs in the browser™
  • Based on platform APIs
  • Declarative and functional

Component Model

  • based on plain objects and pure functions, still using the Web Components API under the hood
  • cache and change detection mechanisms built in
    the property definition
import { html, define } from "hybrids";

function increaseCount(host) {
  host.count += 1;
}

export default define({
  tag: "simple-counter",
  count: 0,
  render: ({ count }) => html`
    <button onclick="${increaseCount}">
      Count: ${count}
    </button>
  `,
});

May 14 2018

State Management

  • declarative model definitions with built-in support for:
    • async external storages
    • relations
    • offline caching
    • many more...
  • separates data logic from the presentation
import { store, define } from "hybrids";

const User = {
  id: true,
  firstName: "",
  lastName: "",
  [store.connect] : {
    get: id => 
      fetch(`/users/${id}`)
        .then(res => res.json()),
  },
};

define({
  tag: "my-user",
  user: store(User),
  render: ({ user }) => ...,
});

August 2020

Routing

  • depends on a tree-like structure of views, which have their own configuration
  • represented by the stack of views
  • supports dialogs, protected views, works without explicit URLs
import { define, html, router } from "hybrids";

const Home = define({
  [router.connect]: { stack: [...] },
  tag: "my-app-home-view",
  content: ({ ... }) => html`
    <div>...</div>
  `,
});

define({
  tag: "my-app",
  stack: router(Home),
  render: ({ stack }) => html`
    <main>${stack}</main>
  `,
});

December 2021

Localization

  • automatic translation of the component’s content
  • dynamic messages with plural forms, HTML content, or usage of messages outside of the template context
  • CLI tool to extract messages from the source code
import { define, html, localize } from "hybrids";

export default define({
  tag: "my-element",
  name: "",
  render: ({ name }) => html`
    <div>Hello ${name}!</div>
  `,
});

localize("pl", {
  "Hello ${0}!": {
    message: "Witaj ${0}!",
  },
});

April 2022

Layout Engine

  • focuses on “invisible” CSS rules, like display types, alignments, positioning, sizing, etc.
  • Supports media queries, compound selectors, css variables, predefined grid sizing, and many more
import { define, html } from "hybrids";

export default define({
  tag: "my-element",
  content: () => html`
    <template 
      layout="row gap"
      layout@768px="column gap:2"
    >
      <div layout="grow">...</div>
    </template>
  `
});

August 2022

What's next

  • Runtime & Server Side Rendering
  • Templates precompilation
  • Lazy-loaded Components (eg. when visible)
  • ....

Numbers

  • Whole package weights only 18.1kB (min,gzip)
  • 484 tests with 100% coverage
  • Used in production by over 2 millions of end-users
    (Ghostery, Tuleap, Viewcy, Tencent, FunkLang, ...)
  • 17k+ downloads monthly on npm registery

Demo

Making the UI Javascript Framework

By Dominik Lubański

Making the UI Javascript Framework

  • 371