Atelier UI®

DocsShader StudioGithub
Docs 1.0.0

Getting started

  • Installation
  • How to contribute

tools

  • Browse Catalog
  • Shader Studio
    new
  • Fluid Scene

Components (36)

  • Clip Reveal
  • Stripe Wipe
  • Sweep Exit
  • Orbit Gallery
  • Sphere Gallery
  • Glowing Fog
    pro
  • Gradient Flow
    pro
  • Halftone Glow
    pro
  • Edge Bounce
  • Fluid Distortion
  • Image Trail
  • Lens Media
  • Liquid Media
  • Magnetic Dot Grid
  • Pixel Media
  • Pixel Trail
  • Pixelated Text
  • Text Bounce
  • Text Fluid
  • Text Roll
  • Text Scramble
  • Curve Media
  • Infinite Gallery
  • Infinite Parallax
  • Infinite Zoom
  • Pixel Scroll
  • Scattered Scroll
  • Elastic Stick
    pro
  • Wavy Scroll
    pro
  • 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. Components
  4. /
  5. Smooth Scroll

Smooth Scroll

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

Foundation Block
Lenis
Motion

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

npx atelier-ui add smooth-scroll
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>
    )
}
prompt.md
## Integrate the <SmoothScroll /> component from Atelier UI

You are helping integrate an open-source React component into an existing application.

### Component: SmoothScroll
### Description: Lenis smooth scrolling for the whole page, driven by one shared frame loop.
### Dependencies: lenis, motion

---

### Usage Example

Wrap your root layout with the provider.

```tsx title="Root layout"
import { SmoothScroll } from "@/components/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.

```tsx title="Root layout"
<SmoothScroll>
    <WebglProvider>{children}</WebglProvider>
</SmoothScroll>
```

### Control scrolling from components

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

```tsx
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](https://github.com/darkroomengineering/lenis#instance-settings) can be passed through the `options` prop, `syncTouch` is enabled by default:

```tsx title="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.

---

### Props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `children` | `ReactNode` | - | Your app or page content. Required. |
| `options` | `LenisOptions` | `{ syncTouch: true }` | Lenis instance settings. `autoRaf` is always `false`. |

---

### Full Component Source

#### src/components/smooth-scroll/smooth-scroll.tsx

```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>
    )
}
```

---

### Integration Instructions

1. If you can execute shell commands, run `npx atelier-ui add smooth-scroll` from the project root instead of steps 2-3 (it installs everything automatically).
2. Install the npm dependencies: lenis, motion.
3. Copy each file from the component source above to the exact path shown.
4. Render `<SmoothScroll />` where it belongs in the app, using the usage example as a starting point and the props table to adjust it.

Full documentation: https://atelier-ui.com/en/docs/components/primitive/smooth-scroll

Usage

Wrap your root layout with the provider.

Root layout
import { SmoothScroll } from "@/components/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.

Credits

Lenis Smooth scroll library by darkroom.engineering.

Motion React animation library whose frame loop drives the page.

  • Install
  • Usage
  • With the WebGL Provider
  • Control scrolling from components
  • Pass Lenis options
  • API
  • Credits
Star on githubBuy me a coffeellms.txt