HTML-STREAMING WEB-COMPONENTS DECLARATIVE-SHADOW-DOM PROGRESSIVE-RENDERING WEB-PERFORMANCE FRONTEND-ARCHITECTURE UI-UX HTML WEB-STANDARDS JAVASCRIPT HYDRATION SERVER-SIDE-RENDERING USER-EXPERIENCE MODERN-WEB

HTML Streaming untuk Web Components: Membangun UI Interaktif Progresif dengan Declarative Shadow DOM

⏱️ 9 menit baca
👨‍💻

HTML Streaming untuk Web Components: Membangun UI Interaktif Progresif dengan Declarative Shadow DOM

1. Pendahuluan

Di era aplikasi web modern yang serba cepat, performa adalah raja. Pengguna mengharapkan pengalaman yang instan dan responsif, terutama pada initial load. Metrik seperti Largest Contentful Paint (LCP) dan Interaction to Next Paint (INP) telah menjadi standar emas untuk mengukur seberapa cepat dan interaktif website kita.

Namun, membangun UI yang kaya interaktivitas seringkali datang dengan tantangan performa. Terutama saat menggunakan Web Components, yang dikenal dengan isolasi dan reusabilitasnya. Jika Web Components dirender sepenuhnya di sisi klien, kita menghadapi masalah “konten kosong” atau flash of unstyled content (FOUC) hingga JavaScript selesai diunduh dan dieksekusi. Ini menyebabkan Time to First Byte (TTFB) yang tinggi dan First Contentful Paint (FCP) yang lambat, berujung pada pengalaman pengguna yang kurang ideal.

Bagaimana jika kita bisa mendapatkan manfaat isolasi dan reusabilitas Web Components, sekaligus memberikan konten visual kepada pengguna secepat mungkin, bahkan sebelum JavaScript diunduh? Jawabannya ada pada kombinasi kuat antara HTML Streaming dan Declarative Shadow DOM (DSD).

Artikel ini akan membawa Anda menyelami bagaimana dua teknologi web ini bekerja sama untuk merevolusi cara kita merender Web Components, menghadirkan pengalaman pengguna yang progresif dan performa yang superior.

2. Memahami HTML Streaming: Fondasi Pengalaman Instan

Pernahkah Anda membuka sebuah website dan melihat konten muncul secara bertahap, bukan sekaligus? Itulah HTML Streaming!

📌 Apa itu HTML Streaming? HTML Streaming adalah teknik di mana server mengirimkan dokumen HTML ke browser secara bertahap, bukan menunggu seluruh dokumen selesai dibuat. Browser kemudian mulai mem-parsing dan me-render bagian-bagian HTML yang sudah diterima, bahkan sebelum koneksi HTTP selesai.

💡 Manfaat Utama HTML Streaming:

Secara tradisional, ketika browser menerima respons HTTP, ia akan menunggu hingga seluruh respons diterima sebelum mulai mem-parsing dan me-render. Dengan HTML Streaming, server memanfaatkan sifat streaming dari protokol HTTP untuk mengirim data sedikit demi sedikit. Browser yang cerdas akan mulai bekerja begitu menerima potongan HTML pertama, menampilkan header, navigasi, atau bagian konten awal, sementara bagian lain masih dalam perjalanan.

Analogi: Bayangkan Anda sedang membaca buku. Daripada menunggu seluruh buku dicetak dan dikirim ke Anda, HTML Streaming seperti menerima halaman-halaman buku satu per satu. Anda bisa mulai membaca halaman pertama saat halaman kedua sedang dicetak, dan seterusnya. Ini membuat pengalaman membaca terasa lebih cepat.

3. Declarative Shadow DOM (DSD): Shadow DOM di Server-Side

Web Components menawarkan isolasi yang luar biasa untuk markup, style, dan perilaku melalui Shadow DOM. Namun, ada satu masalah klasik: Shadow DOM secara imperatif dibuat dengan JavaScript di sisi klien. Ini berarti, jika Anda melakukan Server-Side Rendering (SSR) untuk sebuah Web Component, konten di dalam Shadow DOM-nya tidak akan muncul di HTML awal yang dikirim server. Browser hanya akan melihat elemen kustom kosong, dan Shadow DOM baru akan “dihidrasi” (dibuat) setelah JavaScript komponen diunduh dan dieksekusi. Ini bisa menyebabkan FOUC atau layout shift yang tidak menyenangkan.

🎯 Declarative Shadow DOM (DSD) adalah solusinya. DSD memungkinkan kita untuk menyertakan Shadow DOM langsung di dalam HTML awal yang dikirim server. Ini adalah markup HTML standar yang dapat di-parse oleh browser, sehingga konten Shadow DOM dapat langsung ditampilkan tanpa menunggu JavaScript.

Cara kerjanya adalah dengan menggunakan <template> tag khusus di dalam elemen kustom Anda:

<!-- HTML yang dikirim dari server -->
<my-card>
  <template shadowrootmode="open">
    <style>
      /* Style untuk Shadow DOM */
      :host {
        display: block;
        border: 1px solid #ccc;
        padding: 16px;
        border-radius: 8px;
        font-family: sans-serif;
      }
      h3 {
        color: #333;
      }
    </style>
    <h3>Judul Kartu dari Shadow DOM</h3>
    <p>Ini adalah konten di dalam Shadow DOM yang dirender oleh server.</p>
  </template>
  <!-- Ini adalah Light DOM atau fallback content jika Shadow DOM belum dihidrasi -->
  <p slot="description">Deskripsi kartu (light DOM).</p>
</my-card>

Ketika browser menerima HTML ini:

  1. Browser akan melihat <my-card> dan <template shadowrootmode="open">.
  2. Secara otomatis, browser akan memindahkan isi dari <template> tersebut ke Shadow DOM dari <my-card>.
  3. Konten dan styling Shadow DOM akan langsung muncul di layar, bahkan sebelum JavaScript untuk my-card diunduh.
  4. Ketika JavaScript my-card dimuat, ia akan “mengambil alih” Shadow DOM yang sudah ada, tanpa perlu membuatnya dari awal. Ini adalah proses “hydration” yang lebih efisien.

Keunggulan DSD:

4. Menggabungkan HTML Streaming dan DSD untuk Web Components

Sekarang kita memiliki dua alat yang ampuh: HTML Streaming untuk mengirim konten secara bertahap, dan DSD untuk merender Web Components di sisi server. Kombinasi keduanya menciptakan sinergi yang luar biasa untuk performa dan pengalaman pengguna.

Bayangkan Anda memiliki halaman dengan banyak Web Components yang tersebar di berbagai bagian, seperti header, sidebar, dan beberapa kartu konten.

<!-- Server mulai mengirimkan HTML -->
<!DOCTYPE html>
<html lang="id">
<head>
    <meta charset="UTF-8">
    <title>Aplikasi Streaming Komponen</title>
    <link rel="stylesheet" href="/style.css">
</head>
<body>
    <app-header>
        <template shadowrootmode="open">
            <style>/* Header styles */</style>
            <h1>Selamat Datang!</h1>
        </template>
    </app-header>
    <main>
        <!-- Konten lain yang di-stream -->
        <section>
            <h2>Berita Terbaru</h2>
            <!-- Server mulai stream Web Component pertama -->
            <news-card>
                <template shadowrootmode="open">
                    <style>/* Card styles */</style>
                    <h3>Berita Penting A</h3>
                    <p>Detail berita A...</p>
                </template>
            </news-card>
            <!-- Server terus stream Web Component kedua -->
            <news-card>
                <template shadowrootmode="open">
                    <style>/* Card styles */</style>
                    <h3>Berita Penting B</h3>
                    <p>Detail berita B...</p>
                </template>
            </news-card>
        </section>
        <!-- ... dan seterusnya untuk komponen lainnya -->
    </main>
    <script type="module" src="/components.js"></script>
</body>
</html>

Bagaimana alurnya bekerja:

  1. Server Memulai Streaming: Server tidak menunggu seluruh halaman selesai digenerate. Ia segera mengirimkan bagian <head> dan <body> awal.
  2. Browser Mulai Parsing dan Rendering: Begitu browser menerima tag <app-header> dengan DSD-nya, ia langsung merender header tersebut beserta Shadow DOM-nya. Pengguna segera melihat bagian atas halaman.
  3. Konten Utama Mengalir: Server melanjutkan streaming konten <main>, termasuk <news-card> pertama dengan DSD. Browser langsung merender kartu berita tersebut.
  4. Interaktivitas Progresif: Sementara browser masih menerima dan merender <news-card> kedua dan seterusnya, JavaScript untuk app-header dan news-card sudah mulai diunduh (misalnya, melalui <script type="module" src="/components.js">).
  5. Hydration: Ketika JavaScript untuk sebuah Web Component selesai diunduh dan dieksekusi (misalnya, customElements.define('news-card', NewsCard)), ia akan “menghidrasi” Web Component yang sudah ada di DOM. Proses ini relatif ringan karena markup dan Shadow DOM sudah ada. Komponen tersebut kini sepenuhnya interaktif.

Dengan pendekatan ini, pengguna mendapatkan perceived performance yang luar biasa. Mereka melihat konten utama hampir instan, dan interaktivitasnya akan menyusul secara progresif tanpa ada jeda visual yang mengganggu.

// /components.js
// JavaScript untuk Web Components
class AppHeader extends HTMLElement {
  constructor() {
    super();
    // Shadow DOM sudah ada dari DSD, tinggal tambahkan event listener atau logika lain
    this.addEventListener('click', () => console.log('Header diklik!'));
  }
}
customElements.define('app-header', AppHeader);

class NewsCard extends HTMLElement {
  constructor() {
    super();
    this.shadowRoot.querySelector('h3').addEventListener('click', () => {
      alert('Judul berita diklik!');
    });
  }
}
customElements.define('news-card', NewsCard);

Dalam contoh di atas, setelah components.js terunduh dan dieksekusi, AppHeader dan NewsCard akan menjadi interaktif. Mereka tidak perlu lagi membuat Shadow DOM, karena DSD sudah menyediakannya.

5. Manfaat Nyata untuk Developer dan Pengguna

Penerapan HTML Streaming dengan Declarative Shadow DOM untuk Web Components membawa sejumlah keuntungan signifikan: