Use Lenis with Next.js 13
Next.js 13 brought a lot of exciting new features, but perhaps one of the most significant changes is the introduction of the App Router, replacing the conventional Pages Router used in previous versions.
In this article, we'll explore how to leverage this powerful new router alongside Lenis by Studio Freight, a lightweight smooth-scrolling library which uses native scroll to enhance your Next.js projects. We'll be using TypeScript for this tutorial, but fear not, the process is adaptable for vanilla Javascript as well, by simply removing the types and using .jsx
instead of .tsx
file extensions.
Installing the necessary packages
Before diving into the implementation details, make sure you have a Next.js 13 project set up with the new /app
directory structure. With that done, let's start by installing the required npm
packages for Lenis and its companion library, Tempus:
npm i @studio-freight/lenis @studio-freight/tempus
Creating the Lenis custom component
Next, we'll create a custom component that encapsulates the Lenis functionality. In your project's /src/components/
directory, create a file named Lenis.tsx
. The contents of this file should resemble the following:
import React, { useEffect, useLayoutEffect, useRef } from 'react';
import Tempus from '@studio-freight/tempus';
import Lenis from '@studio-freight/lenis';
import { usePathname, useSearchParams } from 'next/navigation';
export default function SmoothScroller() {
const lenis = useRef<Lenis | null>(null);
const pathname = usePathname();
const searchParams = useSearchParams();
useEffect(() => {
if (lenis.current) lenis.current.scrollTo(0, { immediate: true });
}, [pathname, searchParams, lenis]);
useLayoutEffect(() => {
lenis.current = new Lenis({
smoothWheel: true,
// Customize other instance settings here
});
const resize = setInterval(() => {
lenis.current!.resize();
}, 150);
function onFrame(time: number) {
lenis.current!.raf(time);
}
const unsubscribe = Tempus.add(onFrame);
return () => {
unsubscribe();
clearInterval(resize);
lenis.current!.destroy();
lenis.current = null;
};
}, []);
return null;
}
With this code we define a React component named SmoothScroller
. It imports the necessary libraries and hooks, including React
, Lenis
, Tempus
, usePathname
, and useSearchParams
. Inside the component, it initializes a lenis ref to store a reference to the Lenis instance.
The useEffect
hook watches for changes in the pathname or searchParams (representing the URL). When a change is detected, it scrolls the page to the top using Lenis if an instance exists.
The useLayoutEffect
hook runs when the component is mounted and performs the following tasks:
- Initializes a new instance of
Lenis
with smooth wheel scrolling enabled. - Sets up an interval to handle resizing of the Lenis instance.
- Defines an
onFrame
function to handle animation frames. - Subscribes to animation frames using
Tempus
and callsonFrame
on each frame. - Returns a cleanup function to unsubscribe from animation frames, clear the resize interval, and destroy the Lenis instance when the component unmounts.
The component itself doesn't render any visible UI elements, so it returns null.
Feel free to adjust the Lenis instance settings to match your specific requirements. For a comprehensive list of settings, consult the Lenis documentation on their GitHub repository.
Importing and using the custom Lenis component
With the custom SmoothScroller
component in place, you can now integrate it into your layout.tsx
file as follows:
import React from "react";
import SmoothScroller from "@/components/Lenis"; // Adjust the import path as needed
export default function RootLayout({
children,
}: {
children: React.ReactNode,
}) {
return (
<html>
<body>
<SmoothScroller />
{children}
</body>
</html>
);
}
By adding the SmoothScroller
component to your layout.tsx
, you initialize the Lenis instance on the client-side and ensure its seamless application across server-side rendered (SSR) pages.
In conclusion
In this article, we've seen how to maintain clean and modular code by encapsulating Lenis functionality within a client-side component. Enjoy your enhanced Next.js 13 project with the App Router and Lenis integration!