Reputation: 11
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
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
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