Scroll Reveal
Fades and slides content in from any direction as it enters the viewport. Configurable distance, delay and replay; respects reduced-motion.
Example
Installation
npx shadcn@latest add https://hirael.com/r/scroll-reveal.jsonUsage
import { ScrollReveal } from "@/components/scroll-reveal"Paths assume the default @/ alias from your components.json. The example above shows the parts composed.
API
<ScrollReveal />
+ native element props| Prop | Type | Default |
|---|---|---|
directionDirection the content travels in from. | ScrollRevealDirection | 'up' |
distanceTravel distance, in px. | number | 24 |
delayDelay before the reveal starts, in ms. | number | 0 |
durationReveal duration, in ms. | number | 600 |
onceReveal once and stay, or replay every time it re-enters the viewport. | boolean | true |
amountVisible fraction (0–1) that triggers the reveal. | number | 0.3 |
Component source
'use client';
import { type HTMLMotionProps, motion, useReducedMotion } from 'motion/react';
type ScrollRevealDirection = 'up' | 'down' | 'left' | 'right';
interface ScrollRevealProps extends HTMLMotionProps<'div'> {
/** Direction the content travels in from. */
direction?: ScrollRevealDirection;
/** Travel distance, in px. */
distance?: number;
/** Delay before the reveal starts, in ms. */
delay?: number;
/** Reveal duration, in ms. */
duration?: number;
/** Reveal once and stay, or replay every time it re-enters the viewport. */
once?: boolean;
/** Visible fraction (0–1) that triggers the reveal. */
amount?: number;
}
const offsetFor = (direction: ScrollRevealDirection, distance: number) => {
switch (direction) {
case 'up':
return { y: distance };
case 'down':
return { y: -distance };
case 'left':
return { x: distance };
case 'right':
return { x: -distance };
}
};
const ScrollReveal = ({
direction = 'up',
distance = 24,
delay = 0,
duration = 600,
once = true,
amount = 0.3,
...props
}: ScrollRevealProps) => {
const reduced = useReducedMotion();
if (reduced) {
return <motion.div data-slot="scroll-reveal" {...props} />;
}
return (
<motion.div
data-slot="scroll-reveal"
initial={{ opacity: 0, ...offsetFor(direction, distance) }}
whileInView={{ opacity: 1, x: 0, y: 0 }}
viewport={{ once, amount }}
transition={{
duration: duration / 1000,
delay: delay / 1000,
ease: [0.22, 1, 0.36, 1],
}}
{...props}
/>
);
};
export { ScrollReveal };
Dependencies
npm
motion