Komponen React Dijadikan Web Component Biasa
Section group-network dipisah dari about-teaser, terus kepikiran buat portabel: dibangun ulang jadi custom element shadow DOM yang bisa ditempel di mana saja.
Ringkasan
Jadi penulis refaktor komponen group-network dari React jadi web component native. Ternyata JSX nggak bisa langsung nempel di HTML biasa, butuh build step dan React runtime. Solusinya bikin custom element pakai HTMLElement, Shadow DOM, sama template, jadi komponennya portable dan bisa dipakai tim lain tanpa React.
Saya lagi refaktor halaman about di proyek pribadi. Ada bagian group-network yang tadinya nempel langsung di dalam about-teaser.jsx. Saya extract jadi file sendiri, terus tiba-tiba kepikiran: gimana kalo bagian ini nanti perlu dipake sama tim lain yang sama sekali nggak pakai React?
Awalnya saya punya tebakan naif. Saya pikir, ya tinggal copy paste JSX-nya, terus sisipkan lewat script tag biasa, beres. Ternyata nggak segampang itu. JSX itu bukan HTML. Dia butuh build step dan React runtime buat bisa jalan. Kalau dipaksa render di HTML polos tanpa setup itu, browser cuma bakal bingung atau malah ngelempar error sintaks.
Di sinilah saya sadar kalau solusinya bukan membungkus React dengan wrapper berat, tapi membuat web component dari komponen react yang sudah ada menjadi standar native browser. Ini adalah langkah yang bikin komponen tersebut benar-benar bisa dipakai di mana saja, mewujudkan encapsulated reuse lewat tiga teknologi inti web component [9].
Dari JSX ke Custom Element
Langkah pertama adalah mengganti class komponen React menjadi class yang extend HTMLElement. Nama tag-nya harus mengikuti aturan lowercase-hyphen, jadi saya namai group-network. Aturan lowercase-hyphen ini bukan sekadar gaya penulisan, tapi syarat wajib browser buat mengenali autonomous elements dan memicu timing connectedCallback dengan benar [10].
Di dalam class itu, saya definisikan method connectedCallback. Method ini jalan tepat ketika elemen ditempel ke DOM, mirip dengan useEffect dengan dependency array kosong di React. Di sinilah tempat yang pas buat ngecek dan ngubah state awal elemen.
class GroupNetwork extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
// logika render dan parsing atribut di sini
}
}
customElements.define('group-network', GroupNetwork);
Saya sengaja memilih pendekatan autonomous custom element ini. Kenapa? Karena pendekatan ini paling stabil dan didukung konsisten di semua browser modern tanpa perlu polyfill yang rumit.
Shadow DOM dan Template
Buat pengganti JSX, saya pakai di dalam HTML. Konten di dalam tag template ini tidak akan dirender secara default oleh browser, dan isinya berupa read-only DocumentFragment [12]. Ini sempurna buat jadi cetakan markup yang bisa diduplikasi tanpa takut ngerusak performa awal halaman.
Saat connectedCallback dipanggil, saya clone konten dari template tersebut, lalu memasukkannya ke dalam shadowRoot. Shadow DOM ini yang bikin internals komponen tersembunyi dari CSS dan JS halaman utama [11]. Mode open yang saya pakai di attachShadow memang mengizinkan akses via JavaScript dari luar, tapi ingat, ini bukan mekanisme keamanan. Ini cuma soal enkapsulasi style dan struktur, bukan proteksi data sensitif.
const template = document.getElementById('group-network-template');
class GroupNetwork extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.appendChild(template.content.cloneNode(true));
const nodesData = this.getAttribute('data-nodes');
if (nodesData) {
this.renderNodes(JSON.parse(nodesData));
}
}
}
Bagaimana dengan data? Di React kita biasa pakai props. Di Web Component, kita pakai atribut HTML. Untuk group-network, saya pilih membaca atribut data-nodes saat elemen terhubung, lalu parse JSON-nya buat render daftar node. Ini lebih deklaratif dan gampang dibaca kalau dilihat dari inspector browser.
Hasilnya? Komponen group-network ini sekarang punya batas yang jelas. Style CSS di halaman utama nggak bakal bocor masuk, dan style di dalam komponen nggak bakal bocor keluar.
Kenapa milih autonomous custom elements
Mungkin ada yang bertanya-tanya, kenapa nggak extend HTMLDivElement aja biar lebih semantik? Jawabannya singkat: Safari.
Browser buatan Apple ini secara tegas menolak dukungan untuk customized built-in elements. Mereka lebih memilih fokus ke autonomous custom elements. Daripada saya harus menulis polyfill atau workaround yang rumit cuma buat satu browser, lebih baik saya ikuti jalan yang sudah mulus. Saya pilih HTMLElement sebagai basis, dan masalah compatibilitas langsung hilang.
Proses migrasi web component dari komponen react ini emang butuh penyesuaian pola pikir. Kita nggak lagi bergantung pada state management React atau lifecycle hooks yang familiar. Tapi trade-off ini sepadan. Saya dapat komponen yang benar-benar portabel. Tim frontend yang pakai Vue bisa pakai ini. Tim yang pakai framework lain, atau bahkan cuma pakai HTML statis, bisa langsung tempel tanpa perlu instalasi npm tambahan.
Ini adalah definisi dari reuse yang sebenarnya. Kita nggak memaksa dunia luar untuk adaptasi dengan stack kita — kita yang bikin komponen kita adaptif terhadap dunia luar.
Sources: [9] https://developer.mozilla.org/en-US/docs/Web/API/Web_components [10] https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements [11] https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM [12] https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/template