A lightweight, type-safe React library for scroll-triggered GSAP animations — built for modern React (App Router, RSC, SSR-safe).
- ✅ Scroll-based reveal animations powered by
IntersectionObserver - ✅ Built-in animation presets
- ✅ Custom animation support via
createRevealSystem - ✅ Fully type-safe animation names (default + custom inferred)
- ✅ Optional defaults provider (animation, repeat, GSAP options)
- ✅ Stagger support
- ✅ Repeat on re-entry
- ✅ Polymorphic
asprop for semantic markup - ✅ SSR-safe (Next.js compatible)
- ✅ Singleton
IntersectionObserverfor optimal performance
npm install @theo-js/react-gsap-reveal gsapor
pnpm add @theo-js/react-gsap-reveal gsapBy default, the library creates a singleton IntersectionObserver with sensible defaults.
You only need to use RevealObserverSetup if you want to override the default observer options.
import { RevealObserverSetup } from "@theo-js/react-gsap-reveal";
export default function RootLayout({ children }) {
return (
<html>
<body>
<RevealObserverSetup threshold={1} rootMargin="0px" />
{children}
</body>
</html>
);
}- The observer is a singleton, created once on first use.
- Changing props of
RevealObserverSetupafter it has mounted has no effect. - Mounting
RevealObserverSetupafter a<Reveal />has already been rendered has no effect. - The observer configuration must be defined before the first Reveal mounts.
This design ensures maximum performance and avoids multiple observers running simultaneously.
If you don’t need custom observer options, you can safely omit this component.
"use client";
import { createRevealSystem } from "@theo-js/react-gsap-reveal";
export const { Reveal } = createRevealSystem();
export default function Example() {
return (
<Reveal>
<div>Hello world</div>
</Reveal>
);
}By default, the library includes:
fadeUpfadeInslideLeftslideRightscaleIn
Usage:
<Reveal animation="slideLeft">
<div>Slide from left</div>
</Reveal>All animation names are fully type-safe.
| Prop | Type | Default | Description |
|---|---|---|---|
animation |
AnimationName |
"fadeUp" |
Animation preset to use |
repeat |
boolean |
false |
Re-run animation when re-entering viewport |
options |
GSAPTweenVars |
— | GSAP animation options (duration, delay, ease, stagger, etc.) |
as |
ElementType |
"span" |
Wrapper element |
childAs |
ElementType |
"span" |
Child wrapper element |
childProps |
ComponentProps or (index) => ComponentProps |
— | Props applied to each child |
<Reveal options={{ stagger: 0.3 }} as="ul" childAs="li">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</Reveal><Reveal repeat>
<div>Repeat me</div>
</Reveal>The reveal will be retriggered everytime it enters the viewport.
When using repeat with custom animations, make sure to reset the revealed element by implementing the onLeave property in your animation definition.
To add custom animations, use createRevealSystem.
"use client";
import { createRevealSystem } from "@theo-js/react-gsap-reveal";
import gsap from "gsap";
export const { Reveal: CustomReveal, RevealDefaultsProvider } =
createRevealSystem({
customAnimations: {
rotateIn: {
fromStyles: {
opacity: 0,
transform: "rotate(-90deg) scale(0.8)",
},
onEnter: ({ elements, options }) => {
gsap.to(elements, {
opacity: 1,
scale: 1,
rotation: 0,
...options,
});
},
onLeave: ({ elements, options }) => {
gsap.to(elements, {
opacity: 0,
scale: 0.8,
rotation: -90,
...options,
});
},
},
},
});Now fully typed:
<CustomReveal animation="rotateIn" />TypeScript automatically includes:
"fadeUp" | "fadeIn" | "slideLeft" | "slideRight" | "scaleIn" | "rotateIn"
No .d.ts augmentation required.
You can define default animation settings via RevealDefaultsProvider.
<RevealDefaultsProvider animation="rotateIn" options={{ duration: 2 }}>
<CustomReveal>
<div>Uses rotateIn (2s)</div>
</CustomReveal>
<CustomReveal options={{ duration: 6 }}>
<div>Overrides to 6s</div>
</CustomReveal>
</RevealDefaultsProvider>- Instance props
- Defaults provider
- Library defaults
- A single
IntersectionObserverinstance tracks all reveal elements. - This avoids unnecessary observers and improves performance.
- Initial styles (
fromStyles) are applied before entering the viewport. onEntertriggers the GSAP animation.- Optional
onLeaveruns when exiting. - Repeat logic is handled internally.
Fully compatible with:
- App Router
- Server Components
- Client Components
- SSR
Only animation components run client-side.
RevealObserverSetup is optional and only required if you need custom observer configuration.
The library is built around:
createRevealSystem()→ animation system factoryReveal→ scroll-triggered animation componentRevealDefaultsProvider→ optional runtime defaultsRevealObserverSetup→ optional singleton observer configurationInView→ polymorphic component that detects when its children enter or leave the viewport
This ensures:
- No unnecessary observers
- Fully isolated animation systems
- Predictable behavior
- Clean TypeScript inference
- Minimal abstraction over GSAP
This library focuses on:
- Simplicity
- Type safety
- Modern React patterns
- Performance via a singleton observer
- Predictable scroll-based animations
It does not aim to replace GSAP ScrollTrigger — it provides a lighter alternative when full timeline orchestration is not required.
ISC