Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Force pages to re-render (Next.js App-Dir)

For this question I have created the following demo Next.js application using the App directory:

  1. It has a standard RootLayout in app/layout.tsx.
  2. It has a nested Layout in a route group app/(app)/layout.tsx. This layout contains <nav />.
  3. It has two server-side rendered pages: app/(app)/a/page.tsx and app/(app)/b/page.tsx.
  4. Each page calls getCurrentTime() (defined on app/functions.ts) and then display the render time.

I want each navigation to a page to cause a re-render and run getCurrentTime() again, but I couldn't make the pages not cache. I've tried adding both export const revalidate = 0 and export const dynamic = "force-dynamic" (docs), but couldn't make it work.

I’m sure it’s pretty basic, but I still couldn’t get the hang of it.

View on StackBlitz

enter image description here

like image 887
Iftach Avatar asked Sep 03 '25 03:09

Iftach


2 Answers

According to the Next.js docs, seems like the mechanism responsible for caching pages on the client is the Router Cache and its purpose is to reduce server requests on navigation (source).

The documentation says: (source)

Opting Out ‒ It's not possible to opt out of the Router Cache. However, you can invalidate it by calling router.refresh, revalidatePath, or revalidateTag . This will clear the cache and make a new request to the server, ensuring the latest data is shown.

Since revalidatePath and revalidateTag can only be used in a server action, I ended with this creating a pseudo client component that will refresh the page on any navigation to it.

It's not the prettiest solution but it works.

"use client";
import { useRouter } from "next/navigation";
import { useEffect, useRef } from "react";

const Refresh = () => {
  const router = useRouter();
  const pathname = usePathname();
  const InitialPathname = useRef(pathname);

  useEffect(() => {
    if (InitialPathname.current === pathname) return;
    router.refresh();
  }, [router, pathname]);
  return null;
};

export default Refresh;
like image 123
Iftach Avatar answered Sep 04 '25 23:09

Iftach


Expanding on Iftach's answer a bit, I was able to make a refresh button as follows:

"use client";
import { useRouter } from "next/navigation";
import { Button } from "react-bootstrap";

const ButtonRefresher = () => {
  const router = useRouter();
  return (<Button onClick={router.refresh}>Refresh</Button>);
};

export default ButtonRefresher;

On my page, I have a server component that uses fetch to grab some data from a url. The fetch looks like this: await fetch(url, { cache: "no-cache"});. The no-cache option forces NextJS to fetch the data on the url every time the component is re-rendered.

Including this button on the page causes the whole page to be re-rendered on the server side and sent to the client which includes fetching the data and re-rendering how the data is displayed.

like image 41
beefsupreme Avatar answered Sep 05 '25 00:09

beefsupreme