Lenis: a smooth scroll library that wraps native scroll instead of replacing it
Smooth scroll as it should be
At a glance
- What is it?
- Lenis is a TypeScript smooth scroll library from darkroom.engineering that runs on top of the browser's own scroll position, so sticky elements, anchor links and accessibility keep working. Here is how it installs, how the raf loop drives it, and where it stops being the right tool.
- Who is it for?
- Adopt Lenis if you are building a marketing site, a WebGL or GSAP-driven page, or any layout where you want inertia on the scroll and you are willing to run one raf loop. Skip it if you need scroll behaviour to be identical across every input device, or if your page is a long document where native scroll is already the correct answer.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 8 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Lenis actually replaces, and what it leaves alone
Most smooth scroll libraries take over the scroll position. They listen for wheel and touch events, prevent the default, and translate a container with a transform. That approach breaks position: sticky, breaks anchor links, and breaks the browser's own accessibility behaviour, because the document never actually scrolls. Lenis takes the opposite route. The README states it "runs on native scroll" and "wraps the browser's own scroll, so position: sticky, anchor links, and accessibility keep working." The library interpolates toward a target scroll position and sets the real scrollTop, rather than faking the position with CSS.
The audience follows from that choice. Lenis is for front-end engineers building sites where the scroll is part of the design: parallax layers, WebGL scenes that need a scroll value every frame, GSAP ScrollTrigger timelines. The README names those cases directly, listing WebGL scroll syncing and parallax effects, and the package ships adapters for React, Vue and Framer plus a snap plugin. It is not a general-purpose utility you add to a documentation site and forget. It is a component of a motion system, and it expects to be wired into one.
The raf loop is the whole mechanism
Lenis does not animate on its own by default. The README's basic setup passes autoRaf: true, which is the shortcut. The custom setup shows what that flag is doing underneath: you construct the instance, then call lenis.raf(time) on every frame and re-request the next frame yourself.
const lenis = new Lenis();
function raf(time) {
lenis.raf(time);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);That single call is where interpolation happens. Because the loop is explicit, Lenis can be driven by something other than requestAnimationFrame. The README's GSAP ScrollTrigger example does exactly that: it subscribes lenis.on('scroll', ScrollTrigger.update) so ScrollTrigger recalculates when Lenis moves, then adds lenis.raf to gsap.ticker instead of running a second loop, converting the GSAP tick from seconds to milliseconds with time * 1000. It also calls gsap.ticker.lagSmoothing(0) so GSAP does not smooth a frame that Lenis already smoothed.
That is a real architectural decision and it has a cost. Two animation loops fighting over the same scroll value is the classic source of jitter in these setups, and the README's answer is to have exactly one. If your stack already has a ticker, you have to find it and hand Lenis's raf to it.
Installing Lenis and getting a first page scrolling
Lenis installs from npm, yarn or pnpm, and the package is named lenis. The README gives all three commands and then a single default import.
npm i lenisimport Lenis from 'lenis'The README also documents a script tag build for projects without a bundler, pointing at https://unpkg.com/[email protected]/dist/lenis.min.js, and a stylesheet at lenis/dist/lenis.css. The CSS is described as recommended rather than required, and the README offers three ways to get it: import it, link it, or copy the stylesheet by hand from packages/core/lenis.css.
For the smallest working page, the README's no-code section is one line of HTML plus a constructor call. It enables autoRaf, autoToggle, anchors, allowNestedScroll, naiveDimensions and stopInertiaOnNavigate at once, and the README says the result handles modals, smooth anchors and scroll reset on page change. That is the fastest way to see what the library does before you integrate it into a build.
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/lenis.css">
<script src="https://unpkg.com/[email protected]/dist/lenis.min.js"></script>
<script>new Lenis({ autoRaf: true, autoToggle: true, anchors: true, allowNestedScroll: true, naiveDimensions: true, stopInertiaOnNavigate: true })</script>For framework work, the repository is a workspace with packages/react, packages/vue and packages/snap, each with its own README. The root package.json lists @nuxt/kit, react and vue as peer dependencies, all marked optional in peerDependenciesMeta, so nothing is pulled in unless you install it. Note that the root package.json lists @nuxt/kit at >=3.0.0 and the root README does not document a Nuxt setup, so read packages/ before assuming the integration path.
Where Lenis is the wrong tool
The README carries a Limitations section as a top-level heading, which is more honest than most libraries manage. The constraint that matters most is that Lenis changes the feel of scrolling, and feel is not uniform across input devices. A trackpad, a mouse wheel and a touch screen all produce different event streams, and interpolation tuned for one reads as lag on another. That is not a bug in the implementation; it is inherent to adding inertia on top of input the operating system already handles.
The second case is documents. If your page is a long text article, a changelog or a settings screen, native scroll is the correct behaviour and Lenis is pure overhead: an extra raf loop, an extra stylesheet, and a scroll that no longer matches the user's system settings. The README's Troubleshooting section exists because integrations do go wrong, and the no-code example's long option list (autoToggle, allowNestedScroll, naiveDimensions, stopInertiaOnNavigate) is a signal that the default behaviour needed patching for common cases like modals and nested scroll areas. If your layout has nested scroll containers, expect to spend time there.
Third, anything where scroll position is load-bearing for correctness rather than presentation. Lenis interpolates toward a target, so the reported position during a scroll is a moving value, not the final one. Code that reads scroll position at a single moment and acts on it needs to account for that.
Lenis compared with GSAP ScrollTrigger
These are not competitors, and the README treats them as collaborators: its GSAP example wires Lenis's raf into gsap.ticker and forwards scroll events to ScrollTrigger.update. The difference in approach is what each one owns. ScrollTrigger is a timeline and trigger system. It watches a scroll position and fires animations at defined points; it does not change how the page scrolls. Lenis changes how the page scrolls and does not know anything about timelines.
So the choice is not either/or. If you already use ScrollTrigger and want smoother motion, you add Lenis and hand it the ticker. If you want scroll-linked animation without adding a scroll library, ScrollTrigger alone gives you that, at the cost of the native scroll feel. The README's GSAP section is the concrete integration recipe for the combined case, including the lagSmoothing(0) call, which is the part people miss.
Licence, maintenance and upgrade cost
Lenis is MIT licensed, stated in both the repository and package.json. MIT permits commercial use, modification and redistribution provided the copyright notice and permission notice are retained; that is the general shape of the licence, and anything beyond it is a question for your own counsel rather than for this article.
The repository is not archived, and the last push was on 2026-09-18, three days before this writing. Releases are frequent and small: v1.3.26 on 2026-08-05, v1.3.25 on 2026-06-26, v1.3.24 on 2026-06-24. That cadence means patch upgrades are cheap to take and you should expect to take them, because a smooth scroll library is exposed to browser behaviour that changes without notice.
The upgrade cost sits in the option surface, not the version number. The README documents Settings, Properties, Methods and Events as separate tables, and the no-code example passes six options in one call. Options that alter default behaviour are the ones that break when defaults shift. The repository also carries a V2-ROADMAP.md at the top level, which tells you the maintainers are planning a major version; the README does not document what changes in it, so treat that file as the thing to read before you pin a version for a long-lived project.
Editorial conclusion
Adopt Lenis if you are building a marketing site, a WebGL or GSAP-driven page, or any layout where you want inertia on the scroll and you are willing to run one raf loop. Skip it if you need scroll behaviour to be identical across every input device, or if your page is a long document where native scroll is already the correct answer. Before committing, check the Settings table for autoRaf, autoToggle and anchors, confirm which peer dependency your framework needs, and read the Limitations section of the README, which the project keeps as its own heading rather than burying in prose.
Frequently asked questions
What does the name Lenis mean?
The README states that Lenis is Latin for "smooth", which is also how the project describes its purpose.
What is Lenis used for?
It is a smooth scroll library for websites. The README names WebGL scroll syncing and parallax effects as typical uses, and the package includes adapters for React, Vue and Framer plus a snap plugin.
How do I install Lenis?
The README gives npm i lenis, with yarn add lenis and pnpm add lenis as alternatives, followed by import Lenis from 'lenis'. A script tag build is also documented for projects without a bundler.
How do I use Lenis smooth scroll in React?
The repository is a workspace with a packages/react directory and its own README, and react is listed as an optional peer dependency at >=17.0.0. The root README does not document the React setup itself, so the React package README is the place to start.
How does Lenis compare with GSAP?
The README treats them as complementary rather than competing. Its ScrollTrigger example forwards Lenis scroll events to ScrollTrigger.update and adds lenis.raf to gsap.ticker so there is only one animation loop.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/darkroomengineering-lenis)