Accessible Smooth Scrolling

Using Lenis Smooth Scroll to create accessible smooth scrolling

Fabrice
LenisScrollingNext.jsAccessible

Whether you like it or not, smooth scrolling is something you can find on almost every Awwward-winning website. There are various methods to accomplish this task. One such way is by using tools like GSAP or Locomotive Scroll. My preferred way is with Studio Lenis' Smooth scroll package. Many libraries tend to interfere with the user's natural scrolling behavior. However, Lenis' library doesn't interfere with the user's native scroll. Instead, it applies a smooth or gradual transition to the native scroll while providing the correct scroll position to any software or code that requires it.


For this tutorial, we will be using the Next.JS framework. Next.js simplifies building React websites. It helps them load faster for visitors, which improves user experience. It also benefits search engines, making your site more discoverable. Overall, Next.js saves you time coding and gets your website running smoothly.

Setting up the project

Let's start by creating a new Next.js project. You can do this by running the following command in your terminal:

bash
1bun create-next-app

This automatically installs dependencies using npm. You can use the following link as a reference: https://bun.sh/guides/ecosystem/nextjs.

Adding Lenis to your project

To install the package, run the following command:

bash
1bun add @studio-freight/lenis
jsx
1const lenis = new Lenis();
2
3lenis.on("scroll", (e) => {
4 console.log(e);
5});
6
7function raf(time) {
8 lenis.raf(time);
9 requestAnimationFrame(raf);
10}
11
12requestAnimationFrame(raf);

We will need to add some syntax. Import the following at the top of your file:

bash
1import Lenis from '@studio-freight/lenis'

For our code to work as intended, we want to place the logic inside the useLayoutEffect hook. This hook is called before the user can see any changes in the render, while the useEffect hook gets called after the user can see the changes. The goal is to execute the code inside the effect immediately before the browser paints the DOM; to ensure the user experiences smooth scrolling from the start. That's why we choose to use the useLayoutEffect hook. Since we want the logic to pass to the whole site, we want to add a children property with a ReactNode as its typing. The final result should look something like this:

jsx
1"use client";
2import { useLayoutEffect, useRef } from "react";
3import Lenis from "@studio-freight/lenis";
4
5interface SmoothScrollProps {
6 children: React.ReactNode;
7}
8
9const SmoothScrollWrapper: React.FC<SmoothScrollProps> = ({ children }) => {
10 const lenisRef = (useRef < Lenis) | (null > null);
11
12 useLayoutEffect(() => {
13 lenisRef.current = new Lenis();
14
15 function raf(time: number) {
16 if (lenisRef.current) {
17 lenisRef.current.raf(time);
18 }
19 requestAnimationFrame(raf);
20 }
21
22 requestAnimationFrame(raf);
23 }, []);
24
25 return <>{children}</>;
26};
27
28export default SmoothScrollWrapper;

Also, do not forget to put 'use client' at the top of your file. Next.js will throw an error otherwise because we use the UseRef hook.

Finishing touches

While not mandatory, I recommend adding the following styling properties to your global.css file. These changes optimize native browser functions to enhance performance and efficiency, ensuring smoother operation in various scenarios.

css
1html.lenis,
2html.lenis body {
3 height: auto;
4}
5
6.lenis.lenis-smooth {
7 scroll-behavior: auto !important;
8}
9
10.lenis.lenis-smooth [data-lenis-prevent] {
11 overscroll-behavior: contain;
12}
13
14.lenis.lenis-stopped {
15 overflow: hidden;
16}
17
18.lenis.lenis-scrolling iframe {
19 pointer-events: none;
20}

When the component is created you can use it in your layout file as follows:

jsx
1export default function RootLayout({
2 children,
3}: Readonly<{
4 children: React.ReactNode,
5}>) {
6 return (
7 <html lang="en">
8 <SmoothScrollWrapper>
9 <body className={inter.className}>{children}</body>
10 </SmoothScrollWrapper>
11 </html>
12 );
13}

Well, look at that, already Awwwards ready! If you have any questions, feel free to contact me. Or take a look at the source code for this project!

Darkroom Engineering created Lenis.


Cheers,


Fabrice