MMM
MMM

Reputation: 11

How to Differentiate Between Navigation Route Change and Initial Load in Next.js 14?

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.

Upvotes: 1

Views: 614

Answers (2)

Takibul Hasan
Takibul Hasan

Reputation: 1

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.

Upvotes: 0

cihanolmalibu
cihanolmalibu

Reputation: 9

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;
}

Upvotes: 0

Related Questions