skip to Main Content

I’m currently working with Next.js 14 and its App Router paradigm to manage navigation within my application. I have implemented a background color transition on certain pages that is triggered when navigating between routes. However, I want this transition to occur only on route changes, not when the page is accessed directly by typing the URL into the browser.

Problem: My layout includes a CSS transition effect for the background color that I only want to trigger when users navigate to specific pages from other parts of the app (not on initial page loads).

Question: Is there a way in Next.js 14 to detect if a page load is the result of a navigation route change versus an initial direct access? I need this to conditionally apply the background color transition.

Any guidance or suggestions on how to approach this would be greatly appreciated!

I attempted to adjust my layout.tsx to make it a client component, thinking it might help differentiate between navigation changes and direct loads. However, this approach did not resolve the issue as it didn’t logically fit the requirements.

2

Answers


  1. To solve this you can use a custom hook to detect route changes, then a wrapper component to use it wherever we want :

    import { useEffect, useRef } from 'react';
    import { usePathname, useSearchParams } from 'next/navigation';
    
    export function useIsFirstMount() {
      const isFirst = useRef(true);
      const pathname = usePathname();
      const searchParams = useSearchParams();
    
      useEffect(() => {
        if (isFirst.current) {
          isFirst.current = false;
          return;
        }
        // This will run on route changes, but not on the initial mount as you wanted
        console.log('Route changed');
      }, [pathname, searchParams]);
    
      return isFirst.current;
    }

    Then create a client-side component wrapper that uses this hook (After that we can use this wrapper to wrap any component and apply this styling logic):

    'use client';
    
    import React, { ReactNode } from 'react';
    import { useIsFirstMount } from './useIsFirstMount';
    
    interface TransitionWrapperProps {
      children: ReactNode;
    }
    
    export function TransitionWrapper({ children }: TransitionWrapperProps) {
      const isFirstMount = useIsFirstMount();
    
      return (
        <div className={`transition-bg ${isFirstMount ? '' : 'bg-changed'}`}>
          {children}
        </div>
      );
    }

    For example i can use it inside the layout.tsx file :

    return (
        <html lang="en">
          <body>
            <TransitionWrapper>{children}</TransitionWrapper>
          </body>
        </html>
      );

    After this one you can try to create some routes and try this out. By the way i added these styles but you can try more :

    .transition-bg {
      transition: background-color 1.5s ease;
    }
    
    .bg-changed {
      background-color: blue;
    }
    Login or Signup to reply.
  2. Using Router.events to track route changes
    You can use Router.events from the next/router package to track route changes in Next.js. The main idea is to set a flag after the initial page load and then listen to route change events to detect client-side navigations.

        import { useEffect, useState } from 'react';
    import { useRouter } from 'next/router';
    
    const MyApp = ({ Component, pageProps }) => {
      const router = useRouter();
      const [isInitialLoad, setIsInitialLoad] = useState(true);
    
      useEffect(() => {
        // Runs only on the initial load
        if (isInitialLoad) {
          console.log("Initial load");
          setIsInitialLoad(false); // Set flag to false after the first render
        }
    
        const handleRouteChange = (url) => {
          console.log("Client-side route change to:", url);
        };
    
        // Listen for route changes
        router.events.on('routeChangeStart', handleRouteChange);
    
        // Cleanup the event listener
        return () => {
          router.events.off('routeChangeStart', handleRouteChange);
        };
      }, [isInitialLoad, router.events]);
    
      return <Component {...pageProps} />;
    };
    
    export default MyApp;
    

    Alternative: Using a Global State or Context

    You can also track the initial load and client-side navigations by keeping a global state (using useContext or other state management tools like Redux). This might be more useful in a larger app.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search