Lit vs Rimmel - Comparing template-tagged UI libraries

Dario Mannu - May 9 - - Dev Community

Lit and Rimmel are some relatively similar libraries as they both make use of tagged templates.

There are some key differences which we're going to explore here

Architecture and Programming Paradigm

Lit is a simple, lightweight library for building web components using JavaScript or TypeScript. It uses a reactive update system where changes in data trigger updates to the DOM. It leverages JavaScript template literals for HTML templating, encapsulated in its html tagged templates.
Rimmel emphasizes a functional-reactive programming approach, integrating RxJS for managing data streams. This library focuses on using RxJS's Observables, Subjects, and BehaviorSubjects to create and manage UI state and behaviors, which can lead to more declarative and predictable UI code.

Templating and Data Binding

Lit offers a straightforward templating syntax (html and css tagged templates) and employs directives for advanced functionality like repeating templates, guarding updates, and managing attributes. It has built-in reactive data binding.
Rimmel utilises the rml tagged template for its HTML structure intertwined with dynamic data streams. Rimmel’s approach is highly declarative, using Observables directly within the template to manage data changes, which automatically handles subscriptions and unsubscriptions to avoid memory leaks.

Component Model

Each Lit component is a class that extends HTMLElement, allowing the use of lifecycle callbacks and state management features provided by the web component standards.
Rimmel Components are designed as plain functions, which might appeal to developers looking for a more functional style of programming. This approach can make components easier to test and maintain.

State Management

Lit manages state internally within components or through external libraries. The state handling is less declarative compared to Rimmel but fits well within the object-oriented paradigm.
Rimmel leverages RxJS for state management, making it inherently reactive. This method provides more robust control over state changes through streams, which can be more intuitive for developers familiar with reactive programming.

Community and Ecosystem

Lit is backed by Google and has a larger community and ecosystem. This popularity brings more third-party tools, libraries, and plugins that integrate well with it.
Rimmel, as a newer and more specialized library, might have a smaller community and fewer resources available. However, its tight integration with RxJS could attract developers looking for advanced functional-reactive programming solutions.

Performance

Lit is highly optimised for performance, with efficient update checks and minimal overhead for reactive updates.
Rimmel's performance depends almost entirely on the reactive streams being used, as it only binds them to DOM events and sinks. Proper use can lead to extremely efficient updates.

Use Case Suitability

Both Lit and Rimmel are suitable for a wide range of projects. The former fits more where traditionally-structured OOP approaches are preferred whilst the latter shows it true power when creating complex interactive user interfaces.

Action

Let's compare both with a code example: a little component with two buttons that prints "Hello" when both are clicked.

import { LitElement, html, css } from 'lit';

class HelloButtons extends LitElement {
  static properties = {
    firstClicked: { type: Boolean },
    secondClicked: { type: Boolean },
    showMessage: { type: Boolean }
  };

  constructor() {
    super();
    this.firstClicked = false;
    this.secondClicked = false;
    this.showMessage = false;
  }

  firstButtonClicked() {
    this.firstClicked = true;
    this.checkBothClicked();
  }

  secondButtonClicked() {
    this.secondClicked = true;
    this.checkBothClicked();
  }

  checkBothClicked() {
    if (this.firstClicked && this.secondClicked) {
      this.showMessage = true;
    }
  }

  render() {
    return html`
      <button @click="${this.firstButtonClicked}">Button 1</button>
      <button @click="${this.secondButtonClicked}">Button 2</button>
      <div>${this.showMessage ? 'Hello' : ''}</div>
    `;
  }
}

customElements.define('hello-buttons', HelloButtons);
const element = document.createElement('hello-buttons');
document.body.appendChild(element)
Enter fullscreen mode Exit fullscreen mode

Follwing is the Rimmel example, managing state exclusively as RxJS streams. You may notice how shorter did the code become as a result:

import { BehaviorSubject, zip, take } from 'rxjs';
import { rml } from 'rimmel';

function HelloButtons() {
  const first = new Subject();
  const second = new Subject();

  const msg = zip([first, second]).pipe(
    map(() => 'Hello'),
    take(1)
  );

  return rml`
    <button onclick="${first}">Button 1</button>
    <button onclick="${second}">Button 2</button>
    <div>${msg}</div>
  `;
}

document.body.innerHTML = HelloButtons();
Enter fullscreen mode Exit fullscreen mode

Which programming paradigm do you prefer? Imperative or Functional? Pushing or Pulling data?

. . . . . . . . . . . . . . . . . .