Atelier UI®

DocsCatalogShader StudioPricingGithub
Docs 1.0.0

tools

  • Browse Catalog
  • Shader Studio
    pro
  • Collage
    new

Documentation

  • How it works
  • License
  • MCP

Page Transition (04)

  • Clip Transition
  • Stripe Transition
  • Pixel Transition
  • Band Transition

Components (36)

  • Orbit Gallery
  • Sphere Gallery
  • Spiral Gallery
  • Glowing Fog
  • Gradient Flow
  • Halftone Glow
  • Scattered Grid
  • Tag Cloud
  • Edge Bounce
  • Fluid Distortion
  • Image Trail
  • Lens Media
  • Liquid Media
  • Magnetic Dot Grid
  • Pixel Media
  • Pixel Trail
  • Dither Cursor
  • Hover Burst
  • Image Bloom
  • Curve Media
  • Infinite Gallery
  • Infinite Parallax
  • Infinite Zoom
  • Pixel Scroll
  • Scattered Scroll
  • Elastic Stick
  • Letter Swarm
  • Magnify Trail
  • Stacking Grid
  • Wavy Scroll
  • Pixelated Text
  • Text Bounce
  • Text Fluid
  • Text Scramble
  • Falling Text
  • Text Roll

Foundation Blocks (07)

  • Smooth Scroll
  • Text Split
  • WebGL Image
  • WebGL Provider
  • WebGL Scene
  • WebGL Text
  • WebGL Video
Atelier UI 1.0.0 ©2026
Star on githubBuy me a coffeellms.txt
  1. Docs
  2. /
  3. Smooth Scroll

Smooth Scroll

Lenis smooth scrolling for the whole page, driven by one shared frame loop.

  • Install
  • With the WebGL Provider
  • Control scrolling from components
  • Pass Lenis options
  • API

Adds Lenis smooth scrolling to your page, with Motion as the clock.

Scroll, JS animations, and WebGL usually each run on their own loop, and they can fall out of sync.
Smooth Scroll runs them on a single loop, in a fixed order, so they always move together without delay.


Install

Prompt
Add Atelier's Smooth Scroll to my app.

If there is no components.json, run: npx shadcn@latest init -d
Then run: npx shadcn@latest add https://atelier-ui.com/r/smooth-scroll.json
That writes the atelier-ui skill under .agents/skills and .claude/skills. Follow it.

This command will install all the dependencies this component uses.

npx shadcn@latest add https://atelier-ui.com/r/smooth-scroll.json

Install the dependencies first, then feel free to copy the files into your project as you see fit.

npm install lenis motion
smooth-scroll.tsx
"use client"

import type { LenisOptions } from "lenis"
import { type LenisRef, ReactLenis } from "lenis/react"
import { cancelFrame, type FrameData, frame } from "motion"
import { type ReactNode, useEffect, useRef } from "react"

type SmoothScrollProps = {
    children: ReactNode
    options?: LenisOptions
}

/**
 * Smooth scroll for the whole page (for now).
 *
 * Motion is the clock: scroll, animations, and WebGL usually each run on
 * their own loop, and can fall out of sync. This provider runs them on a
 * single loop, in a fixed order, so they always move together.
 */
export function SmoothScroll({ children, options }: SmoothScrollProps) {
    const lenisRef = useRef<LenisRef>(null)

    useEffect(() => {
        function update(data: FrameData) {
            lenisRef.current?.lenis?.raf(data.timestamp)
        }
        frame.update(update, true)
        return () => cancelFrame(update)
    }, [])

    return (
        <ReactLenis root ref={lenisRef} options={{ syncTouch: true, ...options, autoRaf: false }}>
            {children}
        </ReactLenis>
    )
}

Wrap your root layout with the provider.

Root layout
import { SmoothScroll } from "@/components/smooth-scroll/smooth-scroll"

export default function RootLayout({ children }) {
    return <SmoothScroll>{children}</SmoothScroll>
}

With the WebGL Provider

Both providers run on Motion's clock, nesting order does not matter.

Root layout
<SmoothScroll>
    <WebglProvider>{children}</WebglProvider>
</SmoothScroll>

Control scrolling from components

Any component under the provider can access the Lenis instance with useLenis:

import { useLenis } from "lenis/react"

function BackToTop() {
    const lenis = useLenis()

    return <button onClick={() => lenis?.scrollTo(0)}>Back to top</button>
}

Pass Lenis options

Any Lenis option can be passed through the options prop, syncTouch is enabled by default:

Custom scroll feel
<SmoothScroll options={{ lerp: 0.08, syncTouch: false }}>
    {children}
</SmoothScroll>

The one option you can't change is autoRaf: the provider drives Lenis itself.


API

NameTypeDefaultDescription
childrenReactNode—Your app or page content. Required.
optionsLenisOptions{ syncTouch: true }Lenis instance settings. autoRaf is always false.

Motion
React animation library.

Star on githubBuy me a coffeellms.txt