Docs/Primitives/createScroll

createScroll

createScroll exposes four MotionValues: scrollX, scrollY, scrollXProgress, and scrollYProgress. They update on frames where the scroll position changes. Pair them with createTransform or createSpring to drive any visual property.

import { createScroll, createSpring, motion } from 'motion-solidjs'

function ProgressBar() {
  const { scrollYProgress } = createScroll()
  const smooth = createSpring(scrollYProgress, { stiffness: 120, damping: 30 })
  return <motion.div style={{ scaleX: smooth }} class="origin-left h-1 bg-pink-500" />
}
createScroll

Scroll line 1. The bar tracks scrollYProgress smoothly.

Scroll line 2. The bar tracks scrollYProgress smoothly.

Scroll line 3. The bar tracks scrollYProgress smoothly.

Scroll line 4. The bar tracks scrollYProgress smoothly.

Scroll line 5. The bar tracks scrollYProgress smoothly.

Scroll line 6. The bar tracks scrollYProgress smoothly.

Scroll line 7. The bar tracks scrollYProgress smoothly.

Scroll line 8. The bar tracks scrollYProgress smoothly.

Scroll line 9. The bar tracks scrollYProgress smoothly.

Scroll line 10. The bar tracks scrollYProgress smoothly.

Scroll line 11. The bar tracks scrollYProgress smoothly.

Scroll line 12. The bar tracks scrollYProgress smoothly.

Scroll line 13. The bar tracks scrollYProgress smoothly.

Scroll line 14. The bar tracks scrollYProgress smoothly.

Scroll line 15. The bar tracks scrollYProgress smoothly.

Scroll line 16. The bar tracks scrollYProgress smoothly.

Scroll line 17. The bar tracks scrollYProgress smoothly.

Scroll line 18. The bar tracks scrollYProgress smoothly.

Scroll line 19. The bar tracks scrollYProgress smoothly.

Scroll line 20. The bar tracks scrollYProgress smoothly.

scroll-progress.tsxOpen ↗
import { motion, createScroll, createSpring } from 'motion-solidjs'
import { createSignal } from 'solid-js'

export const meta = {
  slug: 'scroll-progress',
  title: 'Scroll progress',
  category: 'scroll',
  description: 'A spring-smoothed scrollYProgress on the inner container.',
  tag: 'createScroll',
} as const

export default function ScrollProgress() {
  const [container, setContainer] = createSignal<HTMLDivElement | null>(null)
  const { scrollYProgress } = createScroll({ container })
  const smooth = createSpring(scrollYProgress, { stiffness: 120, damping: 30 })

  return (
    <div
      ref={setContainer}
      class="relative h-64 w-56 overflow-y-scroll rounded-2xl border border-border bg-card"
    >
      <motion.span
        class="sticky top-0 left-0 z-10 block h-1 origin-left bg-grad-rose"
        style={{ scaleX: smooth }}
      />
      <div class="space-y-3 p-4 text-xs leading-relaxed text-fg-muted">
        {Array.from({ length: 20 }).map((_, i) => (
          <p>Scroll line {i + 1}. The bar tracks scrollYProgress smoothly.</p>
        ))}
      </div>
    </div>
  )
}

Options

  • container - scrollable element (defaults to the viewport). Pass an accessor () => HTMLElement
  • target - element whose enter/leave drives progress
  • axis - 'y' (default) or 'x'
  • offset - pair of offset strings, e.g. ['start end', 'end start']
  • layoutEffect - false to attach the scroll listener from an effect instead
createTransform

Scroll line 1

Scroll line 2

Scroll line 3

Scroll line 4

Scroll line 5

Scroll line 6

Scroll line 7

Scroll line 8

Scroll line 9

Scroll line 10

Scroll line 11

Scroll line 12

Scroll line 13

Scroll line 14

Scroll line 15

Scroll line 16

Scroll line 17

Scroll line 18

parallax.tsxOpen ↗
import { motion, createScroll, createTransform } from 'motion-solidjs'
import { createSignal } from 'solid-js'

export const meta = {
  slug: 'parallax',
  title: 'Parallax layers',
  category: 'scroll',
  description: 'Two shapes drift at different speeds as you scroll the box.',
  tag: 'createTransform',
} as const

export default function Parallax() {
  const [container, setContainer] = createSignal<HTMLDivElement | null>(null)
  const { scrollYProgress } = createScroll({ container })
  const yBack = createTransform(scrollYProgress, [0, 1], [0, -80])
  const yFront = createTransform(scrollYProgress, [0, 1], [0, -160])

  return (
    <div
      ref={setContainer}
      class="relative h-64 w-56 overflow-y-scroll rounded-2xl border border-border bg-card"
    >
      <div class="pointer-events-none sticky top-0 h-0">
        <motion.div
          style={{ y: yBack }}
          class="absolute top-10 left-6 h-20 w-20 rounded-2xl bg-grad-violet opacity-60 blur-[2px]"
        />
        <motion.div
          style={{ y: yFront }}
          class="absolute top-24 right-6 h-12 w-12 rounded-2xl bg-grad-rose"
        />
      </div>
      <div class="p-4 text-xs text-fg-muted">
        {Array.from({ length: 18 }).map((_, i) => (
          <p class="py-2">Scroll line {i + 1}</p>
        ))}
      </div>
    </div>
  )
}