DEV Community

Cover image for Lit vs Rimmel - Comparing tagged-template UI libraries
Dario Mannu
Dario Mannu

Posted on

Lit vs Rimmel - Comparing tagged-template UI libraries

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: 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: 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: Utilizes 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

Lit: Each 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 in Rimmel 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: Backed by Google and has a larger community and ecosystem. This popularity brings more third-party tools, libraries, and plugins that integrate well with Lit.
Rimmel: As a newer and more specialized library, Rimmel 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: Highly optimized for performance, with efficient update checks and minimal overhead for reactive updates.
Rimmel: Performance depends almost entirely on the reactive streams being used, as Rimmel only binds them to DOM events and sinks. Proper use can lead to extremely efficient updates.

Use Case Suitability

Lit: Suitable for a wide range of projects, especially where traditional object-oriented approaches are preferred, and there is a need for a robust community and ecosystem.
Rimmel: Ideal for projects where a functional-reactive programming model is beneficial, such as complex user interfaces with numerous asynchronous data sources or where developers are already comfortable with RxJS.
Each library offers distinct advantages depending on the project requirements and developer preferences. Rimmel, with its reactive programming model, might be more suited for applications requiring dynamic, real-time interactions, whereas Lit provides a more traditional, easy-to-adopt approach for building web components.

Both in 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, using Rx streams to hande state:

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

Top comments (0)