@ssgoi/react
v6.6.6
Published
React bindings for SSGOI - Native app-like page transitions for React applications
Maintainers
Readme
@ssgoi/react
React bindings for SSGOI - Native app-like page transitions for React applications.
try this: ssgoi.dev

AI-Assisted Setup
Using Claude, Cursor, ChatGPT, or another AI assistant? Point it at:
https://ssgoi.dev/llms.txtIt has the full setup guide, every transition, the API, and troubleshooting — everything an agent needs to wire SSGOI into your app.
What is SSGOI?
SSGOI brings native app-like page transitions to the web. Transform your static page navigations into smooth, delightful experiences that users love.
✨ Key Features
- 🌍 Works Everywhere - Unlike the browser's View Transition API, SSGOI works in all modern browsers (Chrome, Firefox, Safari)
- 🚀 SSR Ready - Perfect compatibility with Next.js. No hydration issues, SEO-friendly
- 🎯 Use Your Router - Keep your existing routing. React Router, Next.js App Router - all work seamlessly
- 💾 State Persistence - Remembers animation state during navigation, even with browser back/forward
- ⚛️ React Optimized - Built specifically for React with hooks and modern patterns
Installation
npm install @ssgoi/react
# or
yarn add @ssgoi/react
# or
pnpm add @ssgoi/reactQuick Start
1. Wrap your app
import { Ssgoi } from "@ssgoi/react";
import { fade } from "@ssgoi/react/view-transitions";
import { SsgoiTransitionBoundary } from "./ssgoi-transition-boundary";
const config = {
transitions: [fade({ paths: ["/", "/about"] })],
};
export default function App() {
return (
<div className="relative z-0 min-h-dvh bg-white">
{/* Layout shell above: positioned ancestor + stacking context for the OUT clone. */}
<Ssgoi config={config}>
{/* Routed content marker. Layout positioning belongs to the outer wrapper. */}
<SsgoiTransitionBoundary className="min-h-full bg-white">
{/* Your app */}
</SsgoiTransitionBoundary>
</Ssgoi>
</div>
);
}2. Keep pages unmarked
export default function HomePage() {
return (
<main>
<h1>Welcome</h1>
{/* Page content */}
</main>
);
}Create one router-specific SsgoiTransitionBoundary utility in your layout.
It reads the current pathname internally, sets the transition boundary key, and
uses that pathname as a logical page id matched by config such as
/products/*. Use /products/** when the parent path itself should match too.
Next.js implementation:
"use client";
import { type ElementType, type ReactNode } from "react";
import { usePathname } from "next/navigation";
export function SsgoiTransitionBoundary({
children,
as,
className,
}: {
children: ReactNode;
as?: ElementType;
className?: string;
}) {
const pathname = usePathname();
const Component = as ?? "div";
return (
<Component
key={pathname}
data-ssgoi-transition={pathname}
className={className}
>
{children}
</Component>
);
}React Router and TanStack Router use the same component body with their own pathname hook.
That's it! Your configured pages now transition smoothly with a fade effect.
Layout Requirements
The outer element wrapping the SSGOI provider / <Ssgoi> needs
position: relative and z-index: 0 (relative z-0 in Tailwind).
When a page leaves, SSGOI clones it back into the DOM with position: absolute
so it can animate out while the new page animates in. Without a positioned,
stacking-context ancestor the clone jumps to the wrong place or falls behind the
background. Add overflow-x-clip too if you use horizontal transitions
(slide, drill). Keep these layout classes on the outer wrapper, not on the
route boundary marker.
Advanced Transitions
Route-based Transitions
Each transition factory returns a path-transition group. Drop the results straight into config.transitions — nested arrays are flattened automatically:
import { fade, drill, zoom } from "@ssgoi/react/view-transitions";
const config = {
transitions: [
// Calm cross-fade between tabs
fade({ paths: ["/home", "/about"] }),
// iOS-style drill-in when entering details
drill({ enter: "/products/*", exit: "/products" }),
// Card-to-detail zoom (needs matching data-zoom-*-key)
zoom({ paths: ["/gallery", "/photo/*"], type: "expand" }),
],
};Transitions come in three shapes:
{ paths }— symmetric: every pair animates with the same physics (fade,hero,zoom,blind,film,rotate,strip,jaemin){ enter, exit, type? }— directional: enter and exit get different physics (drill,sheet){ paths }— ordered: path order decides forward / back direction (slide,scroll,axis)
Individual Element Animations
Animate specific elements during mount/unmount with transition():
import { transition } from "@ssgoi/react";
import { fade, slide } from "@ssgoi/react/transitions";
function Card() {
return (
<div
ref={transition({
key: "card",
in: fade(),
out: slide({ direction: "up" }),
})}
>
<h2>Animated Card</h2>
</div>
);
}Auto Key Plugin
The Auto Key Plugin automatically generates unique keys for your transitions based on the file location (file:line:column), eliminating the need to manually provide keys.
Benefits:
- Automatic Key Generation: No need to manually specify
keyintransition()calls - Collision-Free: Keys are based on exact code location
- Cleaner Code: Less boilerplate in your components
⚠️ Important: For list items rendered with .map(), just use JSX key prop - the plugin automatically appends it to generate unique keys.
Setup with Next.js
// next.config.ts
import type { NextConfig } from "next";
import SsgoiAutoKey from "@ssgoi/react/unplugin/webpack";
const nextConfig: NextConfig = {
webpack: (config) => {
config.plugins.push(SsgoiAutoKey());
return config;
},
};
export default nextConfig;Setup with Vite
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import SsgoiAutoKey from "@ssgoi/react/unplugin/vite";
export default defineConfig({
plugins: [react(), SsgoiAutoKey()],
});Usage Examples
WITH Auto Key Plugin (Recommended):
import { transition } from "@ssgoi/react";
import { fade, slide } from "@ssgoi/react/transitions";
function SimpleCard() {
return (
<div ref={transition(fade())}>
<h2>Fades in on mount</h2>
</div>
);
}WITHOUT Auto Key Plugin:
// Explicit key required for transition state tracking
function Card() {
return (
<div
ref={transition({
key: "my-card",
...fade(),
})}
>
<h2>Animated Card</h2>
</div>
);
}List Items:
// In .map() lists, just use JSX key - the plugin appends it automatically
function List() {
return (
<ul>
{items.map((item) => (
<li
key={item.id} // JSX key is enough - plugin generates file:line:col:${key}
ref={transition(fade())}
>
{item.name}
</li>
))}
</ul>
);
}Next.js App Router Example
// app/ssgoi-provider.tsx
"use client";
import { type ReactNode } from "react";
import { Ssgoi } from "@ssgoi/react";
import { drill, fade } from "@ssgoi/react/view-transitions";
import { SsgoiTransitionBoundary } from "./ssgoi-transition-boundary";
const config = {
transitions: [
drill({ enter: "/post/*", exit: "*" }),
fade({ paths: ["/", "/about"] }),
],
};
export function SsgoiProvider({ children }: { children: ReactNode }) {
return (
<Ssgoi config={config}>
<SsgoiTransitionBoundary className="min-h-full bg-white">
{children}
</SsgoiTransitionBoundary>
</Ssgoi>
);
}
// app/layout.tsx
import { type ReactNode } from "react";
import { SsgoiProvider } from "./ssgoi-provider";
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html>
<body>
<main className="relative z-0 min-h-screen bg-white">
<SsgoiProvider>{children}</SsgoiProvider>
</main>
</body>
</html>
);
}// app/page.tsx
export default function Page() {
return <main>{/* Your page content */}</main>;
}API Reference
Components
<Ssgoi>
The provider component that manages transition context.
<Ssgoi config={ssgoiConfig}>{children}</Ssgoi>Route boundary
Use one route boundary utility inside <Ssgoi>. It sets
data-ssgoi-transition from the current pathname internally, so individual page
components do not need transition markers.
<SsgoiTransitionBoundary className="min-h-full bg-white">
{children}
</SsgoiTransitionBoundary>Hooks
useTransition()
Access transition state and controls.
const { isTransitioning, direction } = useTransition();Functions
transition()
Apply transitions to individual elements.
<div
ref={transition({
key: "unique-key",
in: fade(),
out: fade(),
})}
>
Content
</div>Built-in Transitions
Page Transitions (@ssgoi/react/view-transitions)
fade()- Calm cross-fade. Safe default for unrelated pagesdrill()- iOS-style hierarchical navigation (list → detail)slide()- Horizontal push for tabs / sequential flowsscroll()- Vertical page scroll for onboarding / paginated viewsaxis()- Material/Flutter shared-axis swap for sibling/tab routessheet()- Bottom sheet that slides up (modal-like flows)hero()- Shared element transition (matchingdata-hero-enter-key/data-hero-exit-key)zoom()- Card-to-detail expansion (matchingdata-zoom-enter-key/data-zoom-exit-key)strip()- 3D Y-axis perspective flipblind()- Window-blinds wipe revealfilm()- Cinematic shrink + tile (gallery / lightbox)rotate()- Card flip between siblingsjaemin()- Playful rotated zoom for special moments
Element Transitions (@ssgoi/react/transitions)
fade()- Fade in/outscale()- Scale in/outslide()- Slide (direction: up/down/left/right)rotate()- Rotatebounce()- Bounceblur()- Blurfly()- Fly (custom x, y position)
Spring Physics Configuration
All transitions use spring physics for natural motion:
slide({
direction: "left",
spring: {
stiffness: 300, // 1-1000, higher = faster
damping: 30, // 0-100, higher = less oscillation
},
});TypeScript Support
SSGOI is written in TypeScript and provides full type definitions:
import type { SsgoiConfig, TransitionConfig } from "@ssgoi/react";
const config: SsgoiConfig = {
// Full type safety
};Browser Support
- Chrome/Edge 88+
- Firefox 78+
- Safari 14+
- All modern mobile browsers
Performance
- Minimal bundle size (~8kb gzipped)
- Hardware-accelerated animations
- Automatic cleanup and memory management
- Smart preloading for instant transitions
Documentation
Visit https://ssgoi.dev for:
- Complete API reference
- Interactive examples
- Advanced patterns
- Migration guides
Contributing
We welcome contributions! Please see our contributing guide for details.
License
MIT © MeurSyphus
