A framework-agnostic library for smooth page transitions in Next.js App Router applications.
- Framework Agnostic: Core library works with any framework
- Next.js Integration: Seamless integration with Next.js App Router
- View Transitions API: Leverages native browser View Transitions when available
- TypeScript: Full TypeScript support
- Accessibility: Respects
prefers-reduced-motion - Lightweight: Minimal bundle size impact (~4KB core)
npm install @sero/core @sero/nextjs
# or
pnpm add @sero/core @sero/nextjs// app/layout.tsx
'use client';
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
import { sero } from '@sero/core';
import './globals.css';
export default function RootLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
useEffect(() => {
// Handle back/forward navigations
sero.notifyExternalNavigation();
}, [pathname]);
return (
<html lang="en">
<body>{children}</body>
</html>
);
}// app/components/SeroLink.tsx
'use client';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import { sero } from '@sero/core';
import { MouseEvent } from 'react';
type Props = React.ComponentProps<typeof Link> & {
delay?: number;
onBeforeNavigate?: () => void | Promise<void>;
onAfterNavigate?: () => void | Promise<void>;
viewTransition?: boolean;
blockIf?: () => boolean;
};
export default function SeroLink({
href,
delay,
onBeforeNavigate,
onAfterNavigate,
viewTransition = true,
blockIf,
...rest
}: Props) {
const router = useRouter();
async function onClick(e: MouseEvent<HTMLAnchorElement>) {
if (typeof href === 'string' && href.startsWith('/')) {
e.preventDefault();
sero.setPaths(window.location.pathname, href);
await sero.beginTransition(() => router.push(href), {
delayMs: delay,
onBeforeNavigate,
onAfterNavigate,
viewTransition,
blockIf
});
}
}
return <Link href={href} onClick={onClick} {...rest} />;
}/* app/globals.css */
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
[data-sero-phase="exiting"] {
animation: fadeOut 200ms ease-out;
}
[data-sero-phase="entering"] {
animation: fadeIn 200ms ease-in;
}// app/page.tsx
import SeroLink from './components/SeroLink';
export default function Home() {
return (
<div>
<h1>Welcome to Sero</h1>
<SeroLink href="/about">Go to About</SeroLink>
</div>
);
}Configure global settings:
sero.configure({
delayMs: 200, // Delay before navigation
viewTransitions: true, // Use View Transitions API
respectReducedMotion: true, // Respect user preferences
timeoutMs: 1500 // Safety timeout
});Start a transition:
await sero.beginTransition(() => router.push('/about'), {
delayMs: 300,
onBeforeNavigate: () => document.body.classList.add('exiting'),
onAfterNavigate: () => document.body.classList.remove('exiting'),
viewTransition: true,
blockIf: () => false
});Set current and target paths:
sero.setPaths('/home', '/about');Subscribe to transition events:
const unsubscribe = sero.subscribe(({ phase, prevPath, nextPath }) => {
console.log(`Phase: ${phase}, ${prevPath} → ${nextPath}`);
});Notify of external navigation (back/forward):
sero.notifyExternalNavigation();idle- No transition in progressexiting- Current page is exitingnavigating- Navigation is happeningentering- New page is enteringidle- Transition complete
<SeroProvider
delayMs={200}
viewTransitions={true}
respectReducedMotion={true}
>
{children}
</SeroProvider><SeroLink
href="/about"
delayMs={300}
onBeforeNavigate={() => console.log('Navigating...')}
onAfterNavigate={() => console.log('Complete!')}
>
About
</SeroLink>- Next 15 Demo - Complete Next.js App Router example
- Basic Usage - Core library examples
- Chrome 111+ (View Transitions API)
- Firefox 89+ (View Transitions API)
- Safari 18+ (View Transitions API)
- All modern browsers (fallback mode)
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT © Troy Hancock