Text Reveal
MotionReveals its children with a soft de-blur + fade as they scroll into view — the 'text develops into focus' effect. Fires once, respects reduced-motion, and is polymorphic via `as` so it can wrap a heading, paragraph, or a single list item.
Text that develops into focus.
Each line de-blurs and fades up as it enters the viewport — fires once, and respects reduced-motion.
components/ui/text-reveal.tsx
"use client";
import * as React from "react";
import { motion, useReducedMotion } from "motion/react";
type RevealTag = "div" | "li" | "span" | "p" | "h2" | "h3";
export interface TextRevealProps {
children: React.ReactNode;
/** Stagger this reveal after siblings (seconds). */
delay?: number;
className?: string;
style?: React.CSSProperties;
/** Element to render as — lets it wrap headings, list items, inline text. */
as?: RevealTag;
}
/**
* Reveals its children with a soft de-blur + fade as they scroll into view —
* the "text develops into focus" effect used on marketing pages. Fires once,
* respects `prefers-reduced-motion` (drops the blur), and is polymorphic via
* `as` so it can wrap a heading, a paragraph, or a single list item.
*/
export function TextReveal({
children,
delay = 0,
className = "",
style,
as = "div",
}: TextRevealProps) {
const shouldReduceMotion = useReducedMotion();
const MotionTag = motion[as];
return (
<MotionTag
initial={{
opacity: 0,
filter: shouldReduceMotion ? "blur(0px)" : "blur(15px)",
}}
whileInView={{ opacity: 1, filter: "blur(0px)" }}
viewport={{ once: true, margin: "-80px" }}
transition={{ duration: 0.4, ease: [0.6, 0.9, 0.9, 1], delay }}
className={className}
style={style}
>
{children}
</MotionTag>
);
}Installation
Terminal
npx shadcn@latest add https://ui.saumyarex.xyz/r/text-reveal.json1. Install dependencies
Terminal
npm install motion2. Copy the source into your project
components/ui/text-reveal.tsx
"use client";
import * as React from "react";
import { motion, useReducedMotion } from "motion/react";
type RevealTag = "div" | "li" | "span" | "p" | "h2" | "h3";
export interface TextRevealProps {
children: React.ReactNode;
/** Stagger this reveal after siblings (seconds). */
delay?: number;
className?: string;
style?: React.CSSProperties;
/** Element to render as — lets it wrap headings, list items, inline text. */
as?: RevealTag;
}
/**
* Reveals its children with a soft de-blur + fade as they scroll into view —
* the "text develops into focus" effect used on marketing pages. Fires once,
* respects `prefers-reduced-motion` (drops the blur), and is polymorphic via
* `as` so it can wrap a heading, a paragraph, or a single list item.
*/
export function TextReveal({
children,
delay = 0,
className = "",
style,
as = "div",
}: TextRevealProps) {
const shouldReduceMotion = useReducedMotion();
const MotionTag = motion[as];
return (
<MotionTag
initial={{
opacity: 0,
filter: shouldReduceMotion ? "blur(0px)" : "blur(15px)",
}}
whileInView={{ opacity: 1, filter: "blur(0px)" }}
viewport={{ once: true, margin: "-80px" }}
transition={{ duration: 0.4, ease: [0.6, 0.9, 0.9, 1], delay }}
className={className}
style={style}
>
{children}
</MotionTag>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
as | "div" | "li" | "span" | "p" | "h2" | "h3" | "div" | Element to render as, so it can wrap different content. |
delay | number | 0 | Stagger this reveal after siblings, in seconds. |
children | React.ReactNode | — | The content that de-blurs into view. |
Dependencies
motion