How to Implement Transitions in Next.js App Router Using useTransition
Jasser Mark Arioste
Next.js provides powerful tools for creating seamless user experiences, and one such feature is the useTransition
hook from React. By implementing a transition system, you can visually indicate page navigation progress, enhancing user interaction. This article demonstrates how to set up transitions in a Next.js application using the app router.
Prerequisites #
Before you begin, ensure the following:
- React and Next.js versions:React 18.0 or later (for
useTransition
support).Next.js 13.4 or later (for the app router). - React 18.0 or later (for
useTransition
support). - Next.js 13.4 or later (for the app router).
- Installed Tailwind CSS (optional): The example includes styling using Tailwind CSS.
Demo #
Here's the link to the demo: https://kzmkrk3lvcsa7fpielho.lite.vusercontent.net/contact
Setting Up Transition Context and Components #
Here’s a step-by-step guide to implementing a transition system in your Next.js application.
1. Create the Transition Context
The transition context will manage the state of transitions globally.
"use client"; import { createContext, ReactNode, useContext, useTransition } from "react"; // Define the type for the transition state type State = { isPending: boolean; startTransition: (callback: () => void) => void; } | null; // Create a context for transitions const TransitionContext = createContext<State>(null); export function TransitionProvider({ children }: { children: ReactNode }) { const [isPending, startTransition] = useTransition(); return ( <TransitionContext.Provider value={{ isPending, startTransition }}> {children} </TransitionContext.Provider> ); } export const useGlobalTransition = () => { const context = useContext(TransitionContext); if (!context) { throw new Error( "useGlobalTransition must be used within a TransitionProvider" ); } return context; };
1234567891011121314151617181920212223242526272829303132
2. Add a Loading Indicator Component
The loading indicator provides visual feedback to users during transitions.
export function LoadingIndicator() { const { isPending } = useGlobalTransition(); return ( <div className={`fixed top-0 left-0 w-screen h-1 bg-teal-500 z-50 transition-opacity duration-300 ${ isPending ? "opacity-100" : "opacity-0" }`} role="progressbar" aria-valuetext={isPending ? "Loading" : "Loaded"} > <div className="h-full w-full bg-white animate-loading"></div> </div> ); }
123456789101112131415
3. Create the Transition Link Component
The TransitionLink
component wraps Next.js’s Link
and integrates the transition logic.
import Link, { LinkProps } from "next/link"; import { useRouter } from "next/navigation"; import { PropsWithChildren } from "react"; export function TransitionLink({ href, ...props }: LinkProps & PropsWithChildren) { const { startTransition } = useGlobalTransition(); const router = useRouter(); return ( <Link {...props} href={href} onClick={(e) => { e.preventDefault(); startTransition(() => { router.push(href.toString()); }); }} /> ); }
123456789101112131415161718192021222324
4. Add Styles for the Loading Indicator (Optional)
If you’re using Tailwind CSS, add the following animation to your tailwind.config.js
file:
module.exports = { theme: { extend: { animation: { loading: "loading 1.5s infinite", }, keyframes: { loading: { "0%": { transform: "translateX(-100%)" }, "100%": { transform: "translateX(100%)" }, }, }, }, }, };
123456789101112131415
5. Wrap Your Application with the Transition Provider
Wrap your app in the TransitionProvider
in the layout.tsx
file.
import { TransitionProvider } from "./path/to/TransitionProvider"; import { LoadingIndicator } from "./path/to/LoadingIndicator"; export default function RootLayout({ children }: { children: ReactNode }) { return ( <html lang="en"> <body> <TransitionProvider> <LoadingIndicator /> {children} </TransitionProvider> </body> </html> ); }
123456789101112131415
6. Use the Transition Link Component
Replace standard Link
components with TransitionLink
wherever you want the transition effect.
import { TransitionLink } from "./path/to/TransitionLink"; export default function ExamplePage() { return ( <div> <h1>Welcome to the Transition Example</h1> <TransitionLink href="/next-page">Go to Next Page</TransitionLink> </div> ); }
12345678910
Conclusion #
Using useTransition
and a custom context, you can create a seamless navigation experience in your Next.js application. The approach outlined above enhances user interaction with visual feedback during page transitions. Tailor the loading indicator's appearance and behavior to match your application's branding and requirements.