Saya menambahkan Lenis untuk smooth scroll ke sebuah situs sinematik yang saya bangun, dan bukannya scroll yang mulus, saya malah dapat dua masalah sekaligus. Pertama, console langsung melempar:
TypeError: document.body is null
Kedua, halaman terasa lebih lambat tampil — ada jeda render yang sebelumnya tidak ada. Animasi belum sempat dinikmati, malah situsnya makin berat di muka.
Kenapa ini terjadi
Kedua masalah ini berasal dari satu kesalahan yang sama: library Lenis di-enqueue di dalam <head> dokumen.
Saat sebuah script ditaruh di <head>, ia dieksekusi sebelum browser selesai mem-parse <body>. Pada titik itu, document.body masih null. Lenis butuh elemen body saat inisialisasi (ia memasang listener dan membaca dimensi dari body), jadi ketika init jalan terlalu dini:
const lenis = new Lenis(); // document.body masih null di sini…ia langsung melempar TypeError: document.body is null. Smooth scroll-nya bahkan tidak pernah aktif.
Dan masalah kedua adalah konsekuensi dari posisi yang sama: script di <head> itu render-blocking. Browser harus mengunduh dan mengeksekusinya sebelum melanjutkan render dokumen. Jadi selain error, Lenis di head juga menahan first paint.
Perbaikannya: enqueue di footer, bukan head
Perbaikan untuk kedua masalah ini sama persis dan cuma satu langkah: pindahkan enqueue library Lenis dan init-nya ke footer dokumen.
Di lingkungan WordPress, itu berarti set argumen $in_footer ke true:
wp_enqueue_script(
'lenis',
'https://unpkg.com/lenis@1/dist/lenis.min.js',
[],
null,
true // ← in_footer: jalankan setelah body ada
);Atau di HTML biasa, cukup taruh <script> tepat sebelum </body>:
<!-- ... isi halaman ... -->
<script src="/js/lenis.min.js"></script>
<script src="/js/lenis-init.js"></script>
</body>Begitu Lenis dimuat di footer, ia jalan setelah <body> ada — document.body tidak lagi null, init berhasil, TypeError hilang. Dan karena tidak lagi di head, ia berhenti memblokir render. Satu pemindahan, dua masalah selesai.
Pengerasan tambahan dari proyek yang sama
Sambil membereskan ini, ada beberapa hal lain yang harus saya tangani agar Lenis tidak bertabrakan dengan bagian lain situs:
- Scroller di dalam modal/overlay. Body navigasi dan overlay pencarian punya scroll sendiri. Lenis akan "menelan" scroll-nya. Tambahkan
data-lenis-preventagar elemen itu tetap pakai scroll native:
<div class="search-overlay__body" data-lenis-prevent>
<!-- isi yang bisa di-scroll secara native -->
</div>-
Halaman dengan 3D customizer/canvas. Di halaman yang punya canvas 3D dengan
OrbitControls, wheel event Lenis bentrok dengan kontrol kamera. Solusinya: jangan aktifkan Lenis sama sekali di halaman itu, biarkan OrbitControls memegang penuh roda mouse. -
prefers-reduced-motion. Hormati preferensi aksesibilitas — lewati Lenis kalau pengguna minta gerakan dikurangi:
const reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!reduceMotion) {
const lenis = new Lenis();
// ... raf loop
}Pelajaran
Library smooth-scroll yang menyentuh document.body harus dimuat setelah body ada — enqueue di footer, jangan pernah di head. Dan ingat, script di head itu render-blocking apa pun isinya, jadi memindahkannya ke footer sekaligus menyembuhkan error document.body is null dan jeda render dalam satu gerakan. Sisanya — data-lenis-prevent di scroller dalam, melewati Lenis di halaman canvas 3D, dan menghormati prefers-reduced-motion — adalah pengerasan yang membuat smooth scroll terasa benar tanpa merusak interaksi lain.
