Ringkasan Utama
Interaction to Next Paint (INP) adalah Core Web Vital yang menggantikan First Input Delay sejak 2024, menilai interaksi paling lambat pada sebuah halaman, bukan hanya klik pertamanya. Memperbaiki skor INP yang buruk berarti memecah long task JavaScript, melakukan yielding ke main thread, dan mengukur pengguna nyata dengan pustaka web-vitals, bukan hanya mengandalkan hasil uji di lab.
First Input Delay dulu hanya mengukur satu hal, yaitu berapa lama browser menunggu sebelum bisa mulai menangani klik, tap, atau tekanan tombol pertama pada sebuah halaman. Metrik ini resmi dipensiunkan pada Maret 2024 dan digantikan oleh Interaction to Next Paint, sebuah Core Web Vital yang mengamati setiap interaksi selama seluruh kunjungan dan melaporkan interaksi paling lambat yang masih terhitung setelah outlier disingkirkan. Jika tim Anda bertahun-tahun mengejar skor FID yang baik namun tetap melihat INP berwarna merah di Search Console, inilah alasannya, FID tidak pernah memedulikan apa yang terjadi setelah tap pertama, sementara sebagian besar kelambatan sesungguhnya terjadi belakangan, jauh di dalam sesi, setelah state, listener, dan node DOM menumpuk.
Artikel ini membahas apa yang sesungguhnya diukur oleh INP, mengapa long task JavaScript hampir selalu menjadi akar penyebab skor yang buruk, dan teknik konkret untuk mengembalikan INP ke bawah ambang baik dua ratus milidetik, mulai dari memecah long task, yielding ke main thread dengan API penjadwalan modern, memangkas pekerjaan yang tidak perlu di dalam event handler, hingga menginstrumentasi pengguna nyata dengan pustaka web-vitals sehingga optimasi Anda didasarkan pada data lapangan sesungguhnya, bukan sekadar satu trace lab.
Apa yang Sebenarnya Diukur oleh INP
INP mengamati setiap klik, tap, dan interaksi keyboard yang terjadi selama kunjungan halaman lalu menghitung nilai latensi untuk masing-masing, yang terdiri dari tiga fase, yaitu input delay sebelum event handler mulai berjalan, processing time saat handler dieksekusi, dan presentation delay hingga browser menggambar frame berikutnya. INP kemudian melaporkan interaksi mana yang paling lambat setelah sejumlah kecil outlier statistik dibuang pada halaman dengan banyak interaksi, dievaluasi pada persentil ke tujuh puluh lima dari kunjungan nyata, dipisah antara mobile dan desktop. Sebuah halaman dengan satu dropdown lambat yang tersembunyi di bagian bawah tetap bisa gagal INP meski setiap klik lain terasa instan, karena metrik ini memang dirancang untuk menangkap pengalaman terburuk yang dialami pengunjung biasa, bukan pengalaman rata-rata.
| Peringkat | Ambang Persentil ke 75 |
|---|---|
| Baik | 200 milidetik atau kurang |
| Perlu Perbaikan | Antara 200 dan 500 milidetik |
| Buruk | Lebih dari 500 milidetik |
Mengapa Long Task Merusak Responsivitas
Main thread browser hanya bisa mengerjakan satu hal pada satu waktu. Layout, kalkulasi ulang style, eksekusi JavaScript, dan proses menggambar semuanya berebut thread yang sama, sehingga skrip apa pun yang berjalan tanpa henti lebih dari lima puluh milidetik akan dikategorikan sebagai long task dan menghambat semua hal lain, termasuk handler klik yang sedang ditunggu pengguna. Long task biasanya muncul dari beberapa penyebab umum pada aplikasi Next.js dan React pada umumnya.
- Update state sinkron berskala besar yang me-render ulang pohon komponen berukuran besar dalam satu langkah, terutama tabel, dashboard, atau daftar yang menampilkan ratusan baris tanpa virtualisasi
- Skrip pihak ketiga seperti widget chat, tag analitik, atau pustaka iklan yang mengambil alih main thread saat hidrasi atau saat diklik, sering di luar kendali aplikasi itu sendiri
- Transformasi data di sisi klien, pengurutan, penyaringan, atau parsing JSON dari payload besar yang dijalankan secara sinkron di dalam event handler tanpa dipecah atau dipindahkan keluar main thread
Buka Chrome DevTools, rekam Performance trace sambil mengklik interaksi yang paling lambat, lalu perhatikan bilah long task bertanda merah pada jalur Main. Panel Performance Insights yang lebih baru akan langsung memberi label pada setiap fase, sehingga Anda jarang perlu menebak bagian mana dari interaksi yang menjadi penyebab utamanya.
Memecah Long Task dan Yielding ke Main Thread
Perbaikan untuk long task jarang berupa membuat pekerjaannya sendiri lebih cepat, melainkan memecah pekerjaan tersebut menjadi bagian-bagian kecil dan mengembalikan kendali ke browser di antara bagian-bagian itu agar browser bisa menggambar, menjalankan input handler, dan tetap responsif. Pola ini disebut yielding. Dahulu developer mensimulasikannya dengan setTimeout, yang menunda callback ke akhir antrean task, namun pendekatan itu membuat pekerjaan berprioritas rendah bisa menyerobot antrean sebelum pekerjaan yang ditunggu pengguna. Metode scheduler.yield, yang rilis stabil di Chrome 129 pada September 2024 dan kini didukung di browser berbasis Chromium serta Firefox, mengatasi hal ini dengan menghentikan eksekusi sementara dan menempatkan lanjutannya di depan antrean, sehingga kode Anda melanjutkan segera setelah penanganan input berprioritas lebih tinggi selesai, bukan menunggu di belakang setiap timer lain yang mengantre.
// Feature-detect scheduler.yield(), fall back to a microtask-friendly helper
function yieldToMain() {
if ('scheduler' in window && 'yield' in window.scheduler) {
return window.scheduler.yield()
}
// Fallback: postMessage-based macrotask yield (setTimeout(0) is throttled)
return new Promise((resolve) => setTimeout(resolve, 0))
}
async function processLargeList(items, renderRow) {
for (let i = 0; i < items.length; i++) {
renderRow(items[i])
// Yield every N items instead of every item, or every frame budget (~5ms)
if (i % 20 === 0) {
await yieldToMain()
}
}
}
// In an interaction handler: paint feedback first, then do heavy work
button.addEventListener('click', async () => {
showSpinner() // cheap, paints immediately
await yieldToMain() // let the browser paint the spinner
await processLargeList(rows, renderRow)
hideSpinner()
})Daftar Periksa Praktis untuk Yielding
Saat Anda merefaktor interaksi yang memicu long task, ikuti langkah-langkah berikut secara berurutan daripada langsung memakai scheduler.yield di mana-mana, karena yielding yang terlalu sering juga membawa overhead tersendiri.
- Berikan feedback visual segera, misalnya keadaan tertekan atau spinner, sebelum melakukan pekerjaan berat apa pun, sehingga proses menggambar yang ditunggu pengguna terjadi dalam beberapa milidetik terlepas dari berapa lama sisa interaksi berjalan
- Pecah perulangan atas array besar sehingga setiap potongan cukup masuk dalam anggaran lima milidetik, lalu yielding di antara potongan menggunakan scheduler.yield bila didukung dan beralih ke yield berbasis macrotask sebagai cadangan pada browser yang belum mendukungnya
- Pindahkan komputasi yang benar-benar berat, seperti parsing payload JSON besar atau pengurutan kompleks, ke Web Worker sehingga main thread tidak pernah terhambat olehnya sejak awal, dan simpan yielding hanya untuk pekerjaan yang harus tetap berada di main thread karena bersentuhan dengan DOM
Jangan membaca properti DOM yang memicu layout seperti offsetHeight atau getBoundingClientRect segera setelah menulis perubahan style di dalam task yang sama. Kombinasi itu memaksa kalkulasi ulang layout secara sinkron, yang dikenal sebagai layout thrashing, dan diam-diam menambah processing time tanpa muncul sebagai satu long task yang jelas, hanya berupa kelambatan kumulatif di banyak interaksi.
Mengukur INP Pengguna Nyata dengan web-vitals
Alat lab seperti Lighthouse menyimulasikan beberapa interaksi terskrip pada satu mesin, yang berguna untuk menangkap regresi di CI namun tidak memberi tahu apa pun tentang bagaimana pengunjung sesungguhnya, dengan perangkat dan jaringan mereka sendiri, merasakan responsivitas. Pustaka open source web-vitals dari Google mendengarkan Event Timing API yang sama dengan yang dipakai Chrome secara internal dan melaporkan INP langsung dari sesi nyata, lengkap dengan data atribusi yang menyebutkan target DOM dan jenis event yang menjadi penyebab interaksi paling lambat, sehingga Anda bisa langsung berpindah dari peringatan dashboard ke komponen tepat yang menyebabkannya.
import { onINP } from 'web-vitals'
onINP((metric) => {
// metric.value is in milliseconds, metric.rating is
// 'good' | 'needs-improvement' | 'poor'
const target = metric.attribution?.interactionTarget
const eventType = metric.attribution?.interactionType
navigator.sendBeacon('/analytics/web-vitals', JSON.stringify({
name: metric.name,
value: metric.value,
rating: metric.rating,
target,
eventType,
id: metric.id,
}))
}, { reportAllChanges: false })Menggulirkan Pemantauan INP Tanpa Berlebihan
Anda tidak memerlukan pipeline analitik khusus untuk mulai memantau INP di produksi. Integrasi kecil yang ditambahkan di atas sistem pelacakan page view yang sudah ada sudah cukup untuk mulai menemukan regresi nyata dalam hitungan hari.
- Panggil fungsi onINP sekali di setiap halaman dan kirimkan nama metrik, nilai, peringkat, serta target atribusi ke endpoint analitik yang sudah ada menggunakan sendBeacon agar laporan tetap terkirim meski halaman ditutup
- Segmentasikan dashboard berdasarkan jenis perangkat dan kecepatan koneksi sebelum menarik kesimpulan, karena ponsel Android kelas menengah pada koneksi yang dibatasi akan melaporkan distribusi INP yang sangat berbeda dibanding browser Chrome desktop yang biasa dipakai kebanyakan engineer untuk menguji
- Tetapkan ambang peringatan pada batas baik dua ratus milidetik di persentil ke tujuh puluh lima daripada memantau rata-rata, karena INP memang dirancang untuk menampilkan pengalaman umum terburuk, bukan rata-rata yang justru menyembunyikannya
Tim yang memadukan scheduler.yield untuk pekerjaan di main thread dengan Web Worker untuk komputasi yang benar-benar berat biasanya mendapatkan peningkatan INP terbesar dengan perubahan kode paling sedikit, karena kedua teknik ini tidak memerlukan perombakan cara state mengalir di dalam aplikasi.

