Optimasi Core Web Vitals di Astro: Meningkatkan LCP, CLS, dan INP untuk Skor PageSpeed
Tiga Metrik yang Menentukan Pengalaman Nyata Pengunjung
Core Web Vitals mengukur pengalaman pengunjung lewat tiga metrik yang saling melengkapi. LCP (Largest Contentful Paint) mencatat waktu sampai elemen terbesar di viewport selesai dirender. CLS (Cumulative Layout Shift) menghitung seberapa jauh layout bergeser tanpa diminta pengunjung. INP (Interaction to Next Paint) mengukur jeda antara interaksi pengguna dan respons visual berikutnya.
PageSpeed Insights memakai ambang batas yang cukup tegas untuk ketiga metrik tersebut. Nilai LCP di bawah 2,5 detik, CLS di bawah 0,1, dan INP di bawah 200 milidetik masuk kategori baik. Nilai di atas ambang itu ditandai sebagai perlu perbaikan, dan angka yang jauh di atasnya masuk kategori buruk.
Astro sudah memberi keunggulan awal pada dua dari tiga metrik. Halaman dikirim sebagai HTML statis, JavaScript hanya dimuat untuk komponen island yang benar-benar interaktif, dan tidak ada hydration runtime global. Dua keuntungan ini membuat LCP lebih cepat dirender dan INP lebih responsif dibanding aplikasi yang menghidrasi seluruh halaman.
Keunggulan bawaan tidak otomatis menghasilkan skor hijau. Hero image berukuran besar tanpa prioritas tetap memperlambat LCP. Komponen island yang menyisipkan konten setelah halaman tampil tetap menggeser layout. Handler interaksi yang berat tetap membuat INP melonjak. Fokus artikel ini adalah menemukan penyebab tersebut di kode proyek Astro, memperbaikinya dengan perubahan terukur, lalu memverifikasi hasilnya lewat Lighthouse.
Membaca Hasil Lighthouse dan Menghubungkannya ke Kode
Laporan PageSpeed Insights memuat dua jenis data yang perlu dibedakan. Field data berasal dari CrUX dan merangkum pengalaman pengunjung nyata selama 28 hari terakhir. Lab data berasal dari satu kali audit Lighthouse dengan perangkat dan jaringan yang disimulasikan. Field data cocok untuk menilai kondisi produksi, sedangkan lab data cocok untuk mereproduksi masalah secara lokal.
Bagian yang paling berguna untuk debugging ada di daftar peluang perbaikan dan diagnostik. Untuk LCP, laporan menunjuk elemen konkret yang menjadi kandidat terbesar, misalnya gambar hero atau heading utama. Untuk CLS, laporan mendaftar elemen dengan kontribusi pergeseran terbesar beserta penyebabnya. Untuk INP, kita perlu membuka panel Performance dan merekam interaksi yang lambat, karena Lighthouse tidak selalu menangkap interaksi nyata pengunjung.
Workflow audit yang konsisten membuat perbaikan bisa dibandingkan. Kita build proyek dalam mode produksi, jalankan preview, lalu audit halaman yang sama dengan kondisi throttling yang sama setiap kali.
npm run build
npx astro preview --port 4321
npx lighthouse http://localhost:4321 \
--only-categories=performance \
--preset=desktop \
--output=json \
--output-path=./reports/baseline.jsonPerintah di atas menyimpan laporan baseline.json sebagai pembanding. Setelah satu perubahan diterapkan, jalankan perintah yang sama dengan nama file berbeda, lalu bandingkan nilai LCP, CLS, dan INP antar laporan. Audit pada build produksi penting karena build development memuat modul tambahan yang tidak ada di produksi, sehingga angkanya menyesatkan.
Mempercepat LCP dengan Prioritas Aset yang Benar
Penyebab LCP lambat di proyek Astro biasanya berulang. Gambar hero dikirim dalam resolusi penuh tanpa format modern. Font eksternal memblokir render teks. CSS kritis dimuat setelah JavaScript lain selesai. Selain itu, komponen island yang terlalu tinggi di halaman menunda elemen utama muncul.
Komponen Image dari astro:assets menangani tiga hal sekaligus: konversi format ke webp atau avif, pembuatan beberapa ukuran responsif, dan penambahan atribut dimensi. Atribut loading="eager" dan fetchpriority="high" hanya diberikan pada gambar yang berada di atas lipatan halaman. Gambar lain tetap loading="lazy" agar bandwidth tidak dipakai untuk aset yang belum terlihat.
---
import { Image } from 'astro:assets';
import heroPerf from '../assets/hero-performance.jpg';
---
<section class="hero">
<Image
src={heroPerf}
alt="Dashboard performa website dengan grafik Core Web Vitals"
widths={[640, 960, 1280]}
sizes="(max-width: 768px) 100vw, 60vw"
loading="eager"
fetchpriority="high"
format="webp"
/>
<h1>Optimasi Core Web Vitals di Astro</h1>
</section>Susunan ini membuat browser memilih ukuran gambar sesuai lebar viewport, bukan mengunduh satu file besar untuk semua perangkat. Prioritas tinggi memastikan permintaan gambar dikirim sebelum aset sekunder, sehingga elemen terbesar muncul lebih cepat.
Langkah yang sama berlaku untuk font. Cukup satu file font utama yang di-preload, dan deklarasikan font-display: swap agar teks tetap tampil dengan font fallback selama file dimuat.
<link
rel="preload"
href="/fonts/inter-latin.woff2"
as="font"
type="font/woff2"
crossorigin
/>
Advanced Architecture with Laravel Containers & Queues
A problem-driven, project-based course designed to elevate intermediate develope...
Hasil yang diharapkan adalah penurunan LCP pada laporan berikutnya, biasanya dari kisaran tiga detik ke bawah 2,5 detik untuk halaman artikel. Bila angka belum turun, periksa kembali elemen LCP yang dilaporkan, karena kandidatnya bisa berpindah dari gambar ke heading setelah perbaikan pertama.

Gambar: Urutan render halaman dari FCP sampai elemen terbesar selesai dilukis sebagai LCP — Sumber: web.dev
Menghilangkan CLS dari Gambar, Font, dan Komponen Island
CLS muncul ketika browser harus mengubah posisi elemen yang sudah terlihat. Tiga sumber paling sering di proyek Astro adalah gambar tanpa rasio aspek, font swap yang mengubah tinggi baris, dan island yang menyisipkan konten setelah hydration selesai.

Gambar: Ambang penilaian CLS, di mana nilai di bawah 0,1 masuk kategori baik dan di atas 0,25 masuk kategori buruk — Sumber: web.dev
Perbaikan untuk gambar cukup eksplisit: berikan width dan height atau wadah dengan aspect-ratio. Browser memakai informasi tersebut untuk menyiapkan ruang sebelum file gambar selesai diunduh. Untuk font, fallback dengan metrik mirip dan size-adjust mengurangi pergeseran teks saat font utama aktif.
Island memerlukan pendekatan berbeda. Directive client:visible menunda hydration sampai komponen masuk viewport, dan penundaan itu membuat komponen baru menggambar isinya ketika pengunjung sudah menggulir. Ruang tetap harus disiapkan sebelum hydration terjadi.
---
import { Image } from 'astro:assets';
import PriceChart from '../components/PriceChart.tsx';
import cover from '../assets/cover-pricing.jpg';
---
<article class="card">
<div class="card__media">
<Image src={cover} alt="Ilustrasi paket harga" width={480} height={270} loading="lazy" />
</div>
<div class="card__island">
<PriceChart client:visible />
</div>
</article>
<style>
.card__media {
aspect-ratio: 16 / 9;
overflow: hidden;
}
.card__media :global(img) {
width: 100%;
height: 100%;
object-fit: cover;
}
.card__island {
min-height: 220px;
}
</style>Kelas card__island menyediakan tinggi minimum yang sama dengan tinggi komponen setelah hydration. Ketika PriceChart selesai dimuat, ruangnya sudah ada, sehingga tidak ada pergeseran layout. Hasil yang diharapkan adalah CLS mendekati nol pada laporan Lighthouse, dan grafik Layout Shift di panel Performance tidak lagi menampilkan bar pergeseran besar.
Meningkatkan INP lewat Strategi Hydration dan JavaScript Minimal
INP mengukur jeda dari input pengguna sampai frame berikutnya tampil. Jeda ini membengkak ketika main thread sibuk, misalnya saat hydration berjalan, saat handler melakukan komputasi berat, atau saat banyak komponen dimuat sekaligus.

Gambar: Ambang penilaian INP, dengan batas 200 milidetik untuk kategori baik dan 500 milidetik untuk kategori buruk — Sumber: web.dev
Pemilihan directive hydration adalah keputusan paling berpengaruh. client:load hanya untuk kontrol yang harus siap sejak halaman tampil, seperti tombol navigasi utama. client:idle cocok untuk widget yang boleh menunggu browser senggang, misalnya kotak pencarian atau toggle tema. client:only dipakai ketika komponen memang tidak bisa dirender di server.
Selain directive, pekerjaan di dalam handler perlu dipisahkan antara update mendesak dan update yang bisa ditunda. Pada framework reaktif, mekanisme transisi menandai update berat sebagai non-urgent sehingga input pengguna tetap dilukis lebih dulu.
import { useState } from 'preact/hooks';
export default function SearchBox() {
const [query, setQuery] = useState('');
const [results, setResults] = useState<string[]>([]);
async function handleInput(event: Event) {
const value = (event.target as HTMLInputElement).value;
setQuery(value);
const found = await filterCatalog(value);
setResults(found);
}
return (
<div class="search">
<input value={query} onInput={handleInput} placeholder="Cari produk" />
{results.length > 0 && <ul>{results.map((item) => <li>{item}</li>)}</ul>}
</div>
);
}Komponen tersebut dipasang dengan client:idle di halaman Astro, sehingga hydration tidak berebut waktu dengan render awal. Fungsi filterCatalog sebaiknya melakukan pekerjaan berat di luar render, misalnya dengan requestIdleCallback atau pemrosesan indeks yang sudah disiapkan sebelumnya.
Untuk mengukur hasilnya, rekam interaksi di panel Performance lalu cari long task di atas 50 milidetik. Pindahkan pekerjaan dari event handler ke langkah terpisah, dan ulangi pengukuran sampai INP turun di bawah 200 milidetik. Pada banyak kasus, perubahan directive saja sudah memangkas jeda interaksi secara signifikan karena hydration tidak lagi menahan input pertama.
Menjaga Skor Stabil dengan Pengukuran Berkelanjutan
Skor yang sudah hijau akan turun kembali ketika artikel bertambah, dependensi diperbarui, atau komponen baru ditambahkan. Karena itu pengukuran perlu menjadi bagian dari pipeline, bukan aktivitas sekali jalan.
Lighthouse CI menjalankan audit pada setiap push dan menggagalkan build ketika metrik melewati anggaran yang ditetapkan. Konfigurasi lighthouserc.json menyimpan ambang batas untuk ketiga metrik utama.
{
"ci": {
"collect": { "staticDistDir": "./dist" },
"assert": {
"assertions": {
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
"cumulative-layout-shift": ["error", { "maxNumericValue": 0.1 }],
"interaction-to-next-paint": ["error", { "maxNumericValue": 200 }]
}
}
}
}Workflow GitHub Actions cukup memanggil @lhci/cli autorun setelah proses build selesai. Bila salah satu ambang terlampaui, pipeline berhenti dengan status gagal dan laporan HTML bisa diunduh sebagai artefak untuk diperiksa.
name: performance-budget
on:
push:
branches: [main]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm run build
- run: npx @lhci/cli autorunSelain lab data, kirim field data dari pengunjung nyata memakai library web-vitals. Data lapangan menunjukkan kondisi perangkat dan jaringan yang tidak bisa direplikasi Lighthouse, sehingga keputusan optimasi tetap berpijak pada pengalaman pengguna.
Urutan prioritas perbaikan yang praktis: selesaikan LCP lebih dulu karena berdampak langsung pada persepsi kecepatan, lanjutkan ke CLS agar halaman terasa stabil, lalu tutup dengan INP melalui penyederhanaan hydration. Daftar periksa akhirnya sederhana: gambar responsif dengan dimensi eksplisit, font terbatas dengan preload seperlunya, island sesedikit mungkin, dan tidak ada komponen dinamis yang menyisipkan konten tanpa ruang cadangan.
Ingin membangun website Astro yang cepat sejak awal dengan praktik performa yang terukur? Program Web Development di Rumah Coding membahas arsitektur Astro, Core Web Vitals, dan performance budget langsung melalui proyek nyata.
Kursus Terkait
Advanced Architecture with Laravel Containers & Queues
A problem-driven, project-based course designed to elevate intermediate developers to a senior architectural mindset. Instead of just memorizing documentation, students will tackle real-world bottleneck issues by mastering the Inversion of Control (IoC) principle, Dependency Injection, and asynchronous background processing. Learn to decouple services and orchestrate robust queues to build scalable, high-performance applications that never freeze under heavy loads.
Asynchronous Bulk E-Certificate & Notification Engine
- Decoupled Service Architecture: PDF generation and Email delivery are built as independent, interface-driven services injected via the Service Container.
- Job Chaining: Ensuring processes run in strict asynchronous order (Generate PDF ➔ Upload to Storage ➔ Send Email).
- Job Batching & Real-Time Tracking: Grouping hundreds of jobs together and displaying a live progress bar (e.g., "65% Completed") on the admin dashboard.
API Development with Golang
A hands-on, project-based course designed to teach beginners how to build robust, high-performance RESTful APIs using Golang. Starting from core Go syntax and concurrency, students will progress through routing, database integration, security, and finally containerize their application for production deployment.
EduTrack: Learning Management & Assessment API
- Role-Based Access Control (RBAC)
- Course & Module Management
- Enrollment & Progress Tracking
Building Modern Apps with Filament
Learn how to build modern, full-stack web applications rapidly using FilamentPHP and the TALL stack. This project-based course is designed for beginners, guiding you step-by-step to create a fully functional backend administration panel. By the end of the course, you will have mastered Filament's powerful Panel Builder, robust CRUD operations, complex database relationships, and interactive dashboards.
Mini Academic Portal (Learning Management System)
- Master Data Management: Comprehensive CRUD functionality to manage Instructors and Course catalogs.
- Student Registry: A dedicated module to manage student profiles and personal information.
- Dynamic Enrollment System: Handling Many-to-Many relationships to enroll students into specific courses using Filament's pivot features.