Critical rendering path adalah jalur yang dilewati browser dari menerima HTML sampai menggambar piksel pertama di layar. Disebut critical karena semua yang ada di jalur ini harus beres dulu sebelum pengunjung melihat apa pun. Bayangin masak nasi goreng: nasi, bumbu, dan wajan wajib siap sebelum mulai, kerupuk bisa menyusul. Kalau kamu menunggu kerupuk digoreng dulu baru mulai masak, makannya ikut telat. Tugasmu: pisahkan mana yang wajib, mana yang bisa menyusul.
🧪 Tebakan cepat: di halaman contoh pelajaran ini, kamu cuma menambahkan
deferke script. First paint-nya pindah dari 1.350 ms ke berapa: 500 ms, 1.050 ms, atau tetap 1.350 ms? Jawabannya ada di bagian "Satu per satu, lalu semuanya".
Dari HTML ke piksel
- DOM. Browser membaca (parse) HTML dari atas ke bawah dan menyusunnya jadi DOM, pohon berisi semua elemen halaman.
- CSSOM. Semua CSS disusun jadi CSSOM, kumpulan aturan style.
- Render tree. DOM dan CSSOM digabung. Isinya cuma elemen yang tampil, jadi yang
display: nonedibuang. - Layout. Browser menghitung posisi dan ukuran tiap elemen.
- Paint. Browser mewarnai pikselnya: teks, warna, border, gambar.
- Composite. Kalau halaman terbagi jadi beberapa lapisan (layer), lapisannya ditumpuk jadi satu gambar akhir.
Saat pertama kali browser menggambar sesuatu di layar disebut first paint. Makin cepat first paint, makin cepat pengunjung tahu halamannya hidup.
Yang menahan render
Resource yang render-blocking bikin browser menunda menggambar halaman sampai resource itu selesai.
- CSS dari
<link rel="stylesheet">render-blocking secara bawaan. Browser gak mau menggambar halaman tanpa style lalu tiba-tiba berubah bentuk. <script src="...">biasa, tanpadeferatauasync, malah parser-blocking: browser berhenti membaca HTML, men-download script itu, menjalankannya, baru lanjut membaca. HTML di bawahnya belum jadi DOM, jadi belum ada yang bisa digambar.- Gambar dan font gak menahan first paint. Tapi teks yang memakai font custom belum kelihatan sampai font-nya datang (browser menyembunyikannya sampai sekitar 3 detik).
Halaman contoh
Ini <head> halaman yang dipakai di Lab. CSS untuk bagian atas halaman sudah ditulis langsung di <style>, jadi non-critical.css cuma berisi style untuk bagian yang belum kelihatan:
<head>
<title>Landing Page</title>
<style>/* CSS kritis: header dan hero */</style>
<link rel="stylesheet" href="non-critical.css">
<script src="app.js"></script>
</head>
Di jaringan HP yang lambat, waktunya kira-kira begini (simulasi):
| Resource | Mulai | Selesai | Menahan? |
|---|---|---|---|
index.html |
0 ms | 400 ms | |
non-critical.css (60 KB) |
400 ms | 1.000 ms | render |
app.js (120 KB) |
400 ms | 1.100 ms, lalu jalan 150 ms | parser |
hero.woff2 (font judul) |
1.300 ms | 1.600 ms |
Parser berhenti di app.js dari 400 ms sampai script-nya selesai jalan di 1.250 ms, lalu butuh 50 ms membaca sisa HTML. First paint baru di 1.350 ms. Font judul baru ketahuan dibutuhkan setelah render tree jadi, jadi judulnya baru tampil di 1.600 ms.
Satu per satu, lalu semuanya
| Yang diubah | First paint | Judul hero tampil |
|---|---|---|
| belum diubah | 1.350 ms | 1.600 ms |
defer saja |
1.050 ms | 1.300 ms |
media="print" saja |
1.350 ms | 1.600 ms |
defer dan media="print" |
500 ms | 750 ms |
| ditambah preload font | 500 ms | 700 ms |
Itu jawaban tebakannya: dengan defer saja, first paint pindah ke 1.050 ms. Parser gak ketahan lagi, tapi non-critical.css masih menahan render sampai 1.000 ms. Sebaliknya, media="print" saja gak mengubah apa pun, karena script-nya masih menahan. Baru dua-duanya yang bikin first paint jatuh ke 500 ms.
Tiga perbaikan di head
<head>
<title>Landing Page</title>
<style>/* CSS kritis: header dan hero */</style>
<link rel="preload" href="/fonts/hero.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">
<script src="app.js" defer></script>
</head>
rel="preload"artinya "download ini sekarang, nanti pasti kepakai".as="font"memberi tahu jenisnya,type="font/woff2"formatnya.crossoriginwajib untuk font: font selalu diambil dengan mode CORS, dan preload tanpacrossorigingak cocok dengan permintaan aslinya, jadi font-nya di-download dua kali.media="print"bilang stylesheet ini untuk dicetak, jadi gak menahan tampilan di layar. Browser tetap men-download-nya. Begitu selesai,onload="this.media='all'"mengganti media-nya jadiall, dan style-nya berlaku di layar.deferbikin script di-download sambil HTML terus dibaca, lalu dijalankan setelah HTML selesai dibaca, sesuai urutan tulisnya.asyncjuga download sambil jalan, tapi script langsung dijalankan begitu selesai di-download, urutannya gak dijamin. Cocok untuk script yang berdiri sendiri, misalnya analytics.
Cara yang sama berlaku untuk gambar utama halaman: <link rel="preload" href="hero.webp" as="image"> (gambar gak butuh crossorigin). Jadi kalau halaman punya CSS besar dan beberapa script di <head>, tiga langkah paling ampuh: taruh CSS kritis langsung di <style>, kasih defer ke script, dan preload gambar terbesarnya.
⚠️ Jebakan yang sering ketemu
- Preload font tanpa
crossorigin. Gak ada error, tapi font-nya di-download dua kali dan preload-nya sia-sia. media="print"tanpaonload. CSS-nya cuma berlaku waktu halaman dicetak, gak pernah di layar. Dua atribut itu harus di tag<link rel="stylesheet">yang sama.deferdi script tanpasrc.defercuma berlaku untuk script yang men-download file. Di script inline,deferdiabaikan.- Trik
media="print"di CSS kritis. Halaman sempat tampil tanpa style lalu meloncat (FOUC, flash of unstyled content). Trik ini cuma untuk CSS yang gak dibutuhkan di layar pertama. asyncuntuk script yang butuh urutan. Kalauapp.jsbutuh library di script sebelumnya,asyncbisa menjalankannya duluan. Pakaidefer.
TL;DR: Browser mengubah HTML jadi piksel lewat DOM, CSSOM, render tree, layout, paint, dan composite. CSS menahan render secara bawaan, dan <script> tanpa defer atau async menahan parser, jadi keduanya menunda first paint. Perbaiki <head> dengan tiga langkah: defer di script yang punya src, media="print" plus onload="this.media='all'" di CSS yang gak kritis, dan <link rel="preload" as="font" crossorigin> untuk font penting. Di contoh ini first paint turun dari 1.350 ms ke 500 ms, dan judul hero dari 1.600 ms ke 700 ms.