Docs/Functions/scroll

scroll

scroll is the imperative companion to createScroll. Pass a callback that receives progress from 0 to 1, or pass AnimationPlaybackControls from animate to scrub that animation with scroll.

import { animate, scroll } from 'motion-solidjs'

// Scrub an animate() through scroll
const controls = animate('.bar', { scaleX: 1 }, { duration: 1 })
scroll(controls)

// Callback
scroll((progress) => console.log(progress), { container: '#scroll-area' })
imperative
0%

Scroll line 1. The bar tracks progress imperatively.

Scroll line 2. The bar tracks progress imperatively.

Scroll line 3. The bar tracks progress imperatively.

Scroll line 4. The bar tracks progress imperatively.

Scroll line 5. The bar tracks progress imperatively.

Scroll line 6. The bar tracks progress imperatively.

Scroll line 7. The bar tracks progress imperatively.

Scroll line 8. The bar tracks progress imperatively.

Scroll line 9. The bar tracks progress imperatively.

Scroll line 10. The bar tracks progress imperatively.

Scroll line 11. The bar tracks progress imperatively.

Scroll line 12. The bar tracks progress imperatively.

Scroll line 13. The bar tracks progress imperatively.

Scroll line 14. The bar tracks progress imperatively.

Scroll line 15. The bar tracks progress imperatively.

Scroll line 16. The bar tracks progress imperatively.

Scroll line 17. The bar tracks progress imperatively.

Scroll line 18. The bar tracks progress imperatively.

scroll-fn-imperative.tsxOpen ↗
import { scroll } from 'motion-solidjs'
import { createSignal, createEffect, onCleanup, For } from 'solid-js'

export const meta = {
  slug: 'scroll-fn-imperative',
  title: 'scroll()',
  category: 'scroll',
  description: 'Imperative scroll() — drive a progress bar with a callback.',
  tag: 'imperative',
} as const

export default function ScrollFnImperativeExample() {
  const [container, setContainer] = createSignal<HTMLDivElement | null>(null)
  const [bar, setBar] = createSignal<HTMLDivElement | null>(null)
  const [progress, setProgress] = createSignal(0)

  createEffect(() => {
    const root = container()
    const bare = bar()
    if (!root || !bare) return

    const stop = scroll(
      (p) => {
        bare.style.width = `${p * 100}%`
        setProgress(p)
      },
      { container: root },
    )

    onCleanup(() => stop())
  })

  return (
    <div class="flex flex-col items-center gap-2">
      <div class="h-1 w-56 overflow-hidden rounded-full bg-card">
        <div ref={setBar} class="h-full bg-grad-rose" style={{ width: '0%' }} />
      </div>
      <span class="text-[10px] tabular-nums text-fg-dim">{(progress() * 100).toFixed(0)}%</span>
      <div
        ref={setContainer}
        class="h-56 w-56 overflow-y-scroll rounded-2xl border border-border bg-card"
      >
        <div class="space-y-3 p-4 text-xs leading-relaxed text-fg-muted">
          <For each={Array.from({ length: 18 })}>
            {(_, i) => <p>Scroll line {i() + 1}. The bar tracks progress imperatively.</p>}
          </For>
        </div>
      </div>
    </div>
  )
}