Next.js 14 App Router: useEffect not running on route change when navigating between dynamic segments

1 week ago 10
ARTICLE AD BOX

I am building a Next.js 14 app using the App Router. I have a dynamic route /products/[id] and I'm using useEffect with the params.id as a dependency to fetch data whenever the user navigates to a different product.

The problem: When I navigate from /products/1 to /products/2 using <Link>, the useEffect does not re-run. The component does not re-render with the new product data — the page still shows the old product.

My component (app/products/[id]/page.tsx):

'use client'; import { useEffect, useState } from 'react'; import { useParams } from 'next/navigation'; interface Product { id: number; title: string; description: string; } export default function ProductPage() { const params = useParams(); const id = params.id as string; const [product, setProduct] = useState<Product | null>(null); useEffect(() => { const fetchProduct = async () => { const res = await fetch(`https://dummyjson.com/products/${id}`); const data = await res.json(); setProduct(data); }; fetchProduct(); }, [id]); // id is in deps array if (!product) return <p>Loading...</p>; return ( <div> <h1>{product.title}</h1> <p>{product.description}</p> </div> ); }

Navigation link (in another component):

<Link href="/products/2">Go to Product 2</Link>

What I have tried:

Added id to the useEffect dependency array — still doesn't re-run. Tried using router.push('/products/2') instead of <Link> — same result. Added key={id} to the component in a parent layout — this forces a full remount but I want to understand why useEffect doesn't fire on its own. Checked that params.id does actually change by logging it — it does update in the render, but useEffect still doesn't trigger.

Expected behaviour: Every time id changes (i.e. user navigates to a different product), useEffect should re-run and fetch the new product data.

Actual behaviour: useEffect only runs on the initial mount. Navigating to /products/2 renders the component but the effect does not fire, so the old product data remains displayed.

Environment:

Next.js: 14.2.x React: 18.x TypeScript: 5.x Using App Router (not Pages Router)

Is this a known issue with Next.js 14 App Router and useEffect? What is the correct pattern to fetch data when dynamic route params change?

Read Entire Article