A wrapper that fades content in while it unblurs and slides a few pixels, once it scrolls into view, with a group that staggers its children.
New in 2.4
Ship faster, with less to configure
Everything below fades in one after the other, with a short blur that clears as each piece settles.
import { BlurFade, BlurFadeGroup } from "@/components/ballmac/blur-fade"
export default function BlurFadeDemo() {
return (
<BlurFadeGroup className="grid w-full max-w-md gap-3" stagger={0.1} item={{ inView: false }}>
<div>
<p className="font-mono text-xs text-muted-foreground">New in 2.4</p>
<h3 className="text-2xl font-semibold tracking-tight text-foreground">Ship faster, with less to configure</h3>
</div>
<p className="text-sm leading-6 text-muted-foreground">Everything below fades in one after the other, with a short blur that clears as each piece settles.</p>
<div className="grid grid-cols-3 gap-2">
{["Fast builds", "Typed APIs", "Dark mode"].map((t) => (
<div key={t} className="rounded-lg border bg-card p-3 text-center text-sm font-medium">
{t}
</div>
))}
</div>
<BlurFade inView={false} delay={0.5} direction="none">
<span className="text-xs text-muted-foreground">Stays still when reduced motion is on.</span>
</BlurFade>
</BlurFadeGroup>
)
}Installation
$ pnpm dlx shadcn@latest add @ballmac/blur-fadeInstall the dependencies.
$ pnpm add motion@^12Copy the source into your project.
components/ballmac/blur-fade.tsx// Ballmac UI: Blur Fade. https://ui.ballmac.com/components/blur-fade "use client" import * as React from "react" import { motion, useInView, useReducedMotion } from "motion/react" import { cn } from "@/lib/utils" type BlurFadeTag = "div" | "span" | "section" | "li" | "p" | "article" | "figure" type BlurFadeProps = Omit<React.ComponentProps<"div">, "ref"> & { /** Seconds to wait before starting. */ delay?: number /** Seconds the fade takes. */ duration?: number /** Where the content travels from. "none" only fades and unblurs. */ direction?: "up" | "down" | "left" | "right" | "none" /** Distance in pixels travelled while fading in. */ offset?: number /** Starting blur in pixels. */ blur?: number /** Wait until the element scrolls into view before playing. */ inView?: boolean /** Play only the first time it enters the view. */ once?: boolean /** Element to render. */ as?: BlurFadeTag } function BlurFade({ delay = 0, duration = 0.5, direction = "up", offset = 10, blur = 8, inView = true, once = true, as = "div", className, children, ...props }: BlurFadeProps) { const ref = React.useRef<HTMLElement>(null) const reduce = useReducedMotion() const seen = useInView(ref, { once, margin: "0px 0px -12% 0px" }) const show = inView ? seen : true const axis = direction === "left" || direction === "right" ? "x" : "y" const sign = direction === "up" || direction === "left" ? 1 : -1 const from = direction === "none" ? {} : { [axis]: offset * sign } const Tag = motion[as] as typeof motion.div return ( <Tag ref={ref as React.Ref<HTMLDivElement>} data-slot="blur-fade" initial={false} animate={ reduce || show ? { opacity: 1, x: 0, y: 0, filter: "blur(0px)" } : { opacity: 0, x: 0, y: 0, ...from, filter: `blur(${blur}px)` } } transition={reduce ? { duration: 0 } : { delay: show ? delay : 0, duration: show ? duration : 0, ease: [0.22, 1, 0.36, 1] }} className={className} {...(props as object)} > {children} </Tag> ) } type BlurFadeGroupProps = Omit<React.ComponentProps<"div">, "ref"> & { /** Seconds between each child. */ stagger?: number /** Seconds before the first child starts. */ delay?: number /** Settings passed to every child's BlurFade. */ item?: Pick<BlurFadeProps, "direction" | "offset" | "blur" | "duration" | "inView" | "once"> /** Element each child is wrapped in. */ itemAs?: BlurFadeTag } /** Wraps each child in a BlurFade and staggers them. */ function BlurFadeGroup({ stagger = 0.08, delay = 0, item, itemAs = "div", className, children, ...props }: BlurFadeGroupProps) { const items = React.Children.toArray(children) return ( <div data-slot="blur-fade-group" className={cn(className)} {...props}> {items.map((child, i) => ( <BlurFade key={React.isValidElement(child) && child.key !== null ? child.key : i} as={itemAs} delay={delay + i * stagger} {...item}> {child} </BlurFade> ))} </div> ) } export { BlurFade, BlurFadeGroup, type BlurFadeProps, type BlurFadeGroupProps }Update the import paths to match your project setup.
Usage
import { BlurFade, BlurFadeGroup } from "@/components/ballmac/blur-fade"The full example is in the Code tab above.
Examples
Directions and blur
direction
up
direction
down
direction
left
direction
right
import { BlurFade } from "@/components/ballmac/blur-fade"
const dirs = ["up", "down", "left", "right"] as const
export default function BlurFadeDirections() {
return (
<div className="grid w-full max-w-md grid-cols-2 gap-3">
{dirs.map((direction, i) => (
<BlurFade key={direction} inView={false} direction={direction} delay={i * 0.12} offset={18} blur={12}>
<div className="rounded-xl border bg-card p-4">
<p className="font-mono text-xs text-muted-foreground">direction</p>
<p className="text-lg font-semibold">{direction}</p>
</div>
</BlurFade>
))}
</div>
)
}API reference
<BlurFade>
| Prop | Type | Default |
|---|---|---|
delaySeconds to wait before starting. | number | 0 |
durationSeconds the fade takes. | number | 0.5 |
directionWhere the content travels from. "none" only fades and unblurs. | "up" | "down" | "left" | "right" | "none" | "up" |
offsetDistance in pixels travelled while fading in. | number | 10 |
blurStarting blur in pixels. | number | 8 |
inViewWait until the element scrolls into view before playing. | boolean | true |
oncePlay only the first time it enters the view. | boolean | true |
asElement to render. | BlurFadeTag | "div" |
<BlurFadeGroup>
| Prop | Type | Default |
|---|---|---|
staggerSeconds between each child. | number | 0.08 |
delaySeconds before the first child starts. | number | 0 |
itemSettings passed to every child's BlurFade. | Pick<BlurFadeProps, "direction" | "offset" | "blur" | "duration" | "inView" | "once"> | — |
itemAsElement each child is wrapped in. | BlurFadeTag | "div" |
Also accepts the standard attributes of its root element.
Accessibility
| Key | Action |
|---|---|
| Reduced motion | Content appears at once with no blur or travel |
| Screen readers | Content is in the DOM from the start; only opacity, blur and position change |
Use with AI
Wrap anything in <BlurFade delay direction offset blur inView once>. <BlurFadeGroup stagger> wraps each child and staggers them. Shows content instantly under reduced motion. With the shadcn MCP server set up (guide), ask your agent:
Add the Ballmac UI Blur Fade (@ballmac/blur-fade) to this project with the shadcn MCP, then use it where it fits.
Use it for
- Hero and section intros
- Lists and cards that should arrive in sequence
Not for
- Text that should animate letter by letter (text-animate)
- Content that must be visible without JavaScript before hydration
Registry JSON: https://ui.ballmac.com/r/blur-fade.json
Credits
Free to use in personal and commercial projects.
- npm
- motion@^12
- Registry
- shadcn/utils
Pairs well with
Text Animate
Reveals text by character, word or line with eight entrances (fade, blur, slide, scale, rotate), staggered and triggered on scroll, with the full text kept for screen readers.
Bento Grid
A responsive bento layout of feature cards that span columns and rows. Each BentoCard has a background visual slot, icon, title and description, and a link that slides up on hover or focus.
Card
A composable content surface with compact spacing, an action slot, and optional interactive feedback.
Animated Beam
An SVG beam that connects two elements with a curved path and sends a glowing gradient pulse along it. Follows layout changes, pauses off-screen, static under reduced motion.