WEB-COMPONENTS LIT FRONTEND JAVASCRIPT CUSTOM-ELEMENTS SHADOW-DOM COMPONENT-DEVELOPMENT FRAMEWORK-AGNOSTIC PERFORMANCE UI-UX MODERN-WEB DX

Membangun Web Components Modern dengan Lit: Ringan, Cepat, dan Interoperabel

⏱️ 14 menit baca
👨‍💻

Membangun Web Components Modern dengan Lit: Ringan, Cepat, dan Interoperabel

1. Pendahuluan: Mengapa Web Components dan Mengapa Lit?

Di era pengembangan web modern, kita sering dihadapkan pada pilihan framework yang melimpah: React, Vue, Angular, Svelte, dan banyak lagi. Masing-masing memiliki ekosistem, sintaks, dan cara kerja yang unik. Ini adalah pedang bermata dua. Di satu sisi, kita mendapatkan alat yang powerful untuk membangun aplikasi kompleks. Di sisi lain, muncul masalah fragmentasi UI dan duplikasi kode.

Bayangkan Anda memiliki beberapa proyek dengan framework berbeda, tetapi semuanya membutuhkan komponen UI dasar yang sama, seperti tombol, modal, atau input field. Apakah Anda akan menulis ulang komponen tersebut untuk setiap framework? Tentu tidak efisien! ❌

Di sinilah Web Components datang sebagai penyelamat. Web Components adalah standar web natif yang memungkinkan kita membuat elemen HTML kustom yang sepenuhnya terenkapsulasi dan dapat digunakan kembali di mana saja, terlepas dari framework frontend yang Anda gunakan. Ini seperti membuat “elemen HTML baru” yang bisa dipasang di proyek React, Vue, atau bahkan Vanilla JavaScript. ✅

Namun, membangun Web Components “vanilla” (tanpa bantuan library) bisa sedikit verbose dan memerlukan banyak boilerplate code. Mengatur Shadow DOM, mengelola atribut, dan memastikan reaktivitas bisa jadi pekerjaan yang melelahkan.

📌 Masuklah Lit!

Lit adalah library JavaScript yang ringan dan cepat dari Google yang dirancang khusus untuk menyederhanakan pengembangan Web Components. Lit menyediakan fondasi yang kokoh dengan API yang intuitif, memungkinkan Anda fokus pada logika komponen daripada detail implementasi standar Web Components yang kompleks. Dengan Lit, Anda bisa menulis Web Components yang powerful, berperforma tinggi, dan benar-benar interoperabel dengan lebih sedikit kode.

💡 Mengapa Lit menjadi pilihan menarik?

Mari kita selami bagaimana Lit membantu kita membangun komponen UI yang cerdas dan reusable.

2. Memahami Dasar Lit: Custom Elements dan Shadow DOM

Sebelum masuk ke Lit, mari kita segarkan kembali dua konsep inti Web Components:

Lit memanfaatkan kedua standar ini secara elegan. Anda akan meng-extend class LitElement untuk membuat Custom Element Anda, dan Lit akan secara otomatis mengelola Shadow DOM untuk Anda.

Berikut adalah contoh komponen Lit paling sederhana:

// my-element.ts
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('my-element')
export class MyElement extends LitElement {
  static styles = css`
    :host {
      display: block;
      border: 1px solid #ccc;
      padding: 16px;
      font-family: sans-serif;
    }
    h2 {
      color: #333;
    }
  `;

  render() {
    return html`
      <h2>Halo dari MyElement!</h2>
      <p>Ini adalah Web Component pertama saya dengan Lit.</p>
    `;
  }
}

Untuk menggunakannya di HTML:

<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Lit Web Component</title>
  <script type="module" src="./my-element.js"></script>
</head>
<body>
  <h1>Aplikasi dengan Web Component</h1>
  <my-element></my-element>
</body>
</html>

Dalam contoh di atas:

3. State dan Properti Reaktif dengan Lit

Komponen UI yang statis tidak terlalu berguna. Kita membutuhkan cara agar komponen bisa menerima data dari luar (props) dan mengelola state internalnya. Lit menyediakan dekorator @property untuk mendefinisikan properti reaktif.

Ketika nilai properti yang didekorasi dengan @property berubah, Lit akan secara otomatis memicu re-render komponen Anda.

Mari kita buat komponen counter sederhana:

// my-counter.ts
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('my-counter')
export class MyCounter extends LitElement {
  static styles = css`
    :host {
      display: inline-block;
      padding: 10px 20px;
      border: 1px solid #007bff;
      border-radius: 5px;
      background-color: #f0f8ff;
      font-family: sans-serif;
      text-align: center;
      margin: 10px;
    }
    button {
      background-color: #007bff;
      color: white;
      border: none;
      padding: 8px 15px;
      border-radius: 3px;
      cursor: pointer;
      margin: 0 5px;
      font-size: 1em;
    }
    button:hover {
      background-color: #0056b3;
    }
    span {
      font-size: 1.5em;
      margin: 0 10px;
      font-weight: bold;
    }
  `;

  // Mendefinisikan properti reaktif 'count'
  @property({ type: Number })
  count = 0; // Nilai default

  private increment() {
    this.count++; // Perubahan ini akan memicu re-render
  }

  private decrement() {
    this.count--; // Perubahan ini juga akan memicu re-render
  }

  render() {
    return html`
      <p>Counter:</p>
      <button @click="${this.decrement}">-</button>
      <span>${this.count}</span>
      <button @click="${this.increment}">+</button>
    `;
  }
}

Penggunaan di HTML:

<my-counter count="5"></my-counter>
<my-counter></my-counter> <!-- Akan menggunakan nilai default 0 -->

Penjelasan:

Setiap kali this.count diubah melalui metode increment atau decrement, Lit akan mendeteksi perubahan tersebut dan hanya me-render ulang bagian template yang terpengaruh, menjadikannya sangat efisien.

4. Styling Komponen Lit: Terisolasi dan Fleksibel

Salah satu keuntungan terbesar Web Components adalah enkapsulasi style. CSS yang Anda definisikan dalam static styles di LitElement akan secara otomatis terisolasi di dalam Shadow DOM komponen, mencegah konflik CSS global.

// my-card.ts
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('my-card')
export class MyCard extends LitElement {
  static styles = css`
    :host {
      display: block;
      border: 1px solid #ddd;
      border-radius: 8px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      padding: 20px;
      margin: 15px;
      max-width: 300px;
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
      background-color: white;
    }
    h3 {
      color: #333;
      margin-top: 0;
      border-bottom: 1px solid #eee;
      padding-bottom: 10px;
    }
    p {
      color: #555;
      line-height: 1.6;
    }
    /* Menggunakan CSS Custom Properties untuk fleksibilitas */
    :host([variant="primary"]) {
      border-color: var(--card-primary-border, #007bff);
      background-color: var(--card-primary-bg, #e7f3ff);
    }
    :host([variant="primary"]) h3 {
      color: var(--card-primary-text, #0056b3);
    }
  `;

  @property({ type: String })
  title = 'Judul Kartu';

  @property({ type: String })
  description = 'Deskripsi singkat tentang isi kartu ini. Ini adalah contoh Web Component Lit.';

  @property({ type: String, reflect: true }) // reflect: true agar atribut HTML diupdate
  variant = '';

  render() {
    return html`
      <h3>${this.title}</h3>
      <p>${this.description}</p>
      <slot name="footer"></slot>
      <slot></slot> <!-- Default slot -->
    `;
  }
}

Penggunaan di HTML:

<style>
  /* Mengatur CSS Custom Property dari luar komponen */
  my-card[variant="primary"] {
    --card-primary-border: #28a745;
    --card-primary-bg: #d4edda;
    --card-primary-text: #155724;
  }
</style>

<my-card title="Produk Unggulan" description="Ini adalah detail produk yang sangat menarik.">
  <p slot="footer">Harga: Rp 1.500.000</p>
  <button>Beli Sekarang</button>
</my-card>

<my-card variant="primary" title="Promo Spesial" description="Dapatkan diskon besar-besaran untuk pembelian hari ini!">
  <p slot="footer">Diskon 20%!</p>
</my-card>

Tips styling:

5. Event Handling dan Slot: Berinteraksi dengan Dunia Luar

Komponen perlu berinteraksi dengan pengguna (event handling) dan menerima konten dinamis dari elemen parent (slot).

Event Handling

Lit membuat event handling sangat mudah dengan sintaks @event.

// my-button.ts
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';

@customElement('my-button')
export class MyButton extends LitElement {
  static styles = css`
    button {
      background-color: #4CAF50; /* Green */
      border: none;
      color: white;
      padding: 10px 20px;
      text-align: center;
      text-decoration: none;
      display: inline-block;
      font-size: 16px;
      margin: 4px 2px;
      cursor: pointer;
      border-radius: 5px;
      transition: background-color 0.3s ease;
    }
    button:hover {
      background-color: #45a049;
    }
  `;

  @property({ type: String })
  label = 'Klik Saya';

  private handleClick() {
    console.log('Tombol diklik!');
    // Memicu Custom Event untuk berkomunikasi dengan parent
    this.dispatchEvent(new CustomEvent('button-clicked', {
      detail: { message: 'Tombol Lit berhasil diklik!' },
      bubbles: true, // Event akan "menggelembung" ke atas DOM tree
      composed: true // Event bisa melewati Shadow DOM boundary
    }));
  }

  render() {
    return html`
      <button @click="${this.handleClick}">${this.label}</button>
    `;
  }
}

Slot

Slot adalah placeholder di dalam template komponen Anda di mana konten dari elemen parent akan didistribusikan.

Contoh my-card di bagian sebelumnya sudah menggunakan slot:

<slot name="footer"></slot>
<slot></slot> <!-- Default slot -->

Dan penggunaannya:

<my-card title="Produk Unggulan" description="Ini adalah detail produk yang sangat menarik.">
  <p slot="footer">Harga: Rp 1.500.000</p>
  <button>Beli Sekarang</button> <!-- Akan masuk ke default slot -->
</my-card>

Dengan event dan slot, komponen Lit Anda bisa menjadi sangat fleksibel dan interaktif, berfungsi layaknya elemen HTML natif.

6. Best Practices dan Tips Lanjutan

Untuk memaksimalkan penggunaan Lit dan Web Components, perhatikan beberapa tips ini:

✅ Performance & Optimasi

♿️ Accessibility (A11y)

🧪 Testing Lit Components

🤝 Integrasi dengan Framework Lain

Salah satu kekuatan terbesar Web Components adalah interoperabilitasnya.

🛠️ Tooling

Kesimpulan

Lit menawarkan cara yang modern, efisien, dan menyenangkan untuk membangun Web Components. Dengan Lit, Anda tidak hanya membuat komponen UI, tetapi juga berinvestasi pada solusi yang reusable, berperforma tinggi, dan framework-agnostic. Ini adalah fondasi yang sempurna untuk membangun Design System yang konsisten atau micro-frontends yang fleksibel, di mana komponen dapat dibagikan dan digunakan di berbagai proyek dan teknologi.

Jika Anda lelah dengan ketergantungan framework atau ingin membangun fondasi UI yang kuat dan tahan lama, Lit dan Web Components adalah jalan yang patut Anda jelajahi. Mulailah bereksperimen, dan rasakan kekuatan komponen UI yang benar-benar universal!

🔗 Baca Juga