Refresh App Router

Use a server action to automatically refresh a Next.js app whenever data in an external system changes.

Demo code from Refresh React Server Components. Watch on YouTube.

page.tsx

A React Server Component that lives in app/posts/[postId]/page.tsx.

import { RefreshCache } from "./refresh-cache";

export default async function PostPage({
  params,
}: {
  params: { postId: string };
}) {
  const post = await getPost(params.postId);
  const postLastUpdated = post.updatedAt.getTime();

  async function checkIfPostChanged() {
    "use server";

    const checkPost = await getPost(params.postId);
    const checkPostLastUpdated = checkPost.updatedAt.getTime();
    const didChange = postLastUpdated !== checkPostLastUpdated;

    if (didChange) {
      revalidatePath("/");
    }
  }

  return (
    <div>
      {/* ... */}
      <RefreshCache check={checkIfPostChanged} />
    </div>
  );
}

refresh-cache.tsx

"use client";

import { useInterval } from "interval-hooks";
import { useEffect, useState } from "react";

export function RefreshCache({ check }: { check: () => Promise<void> }) {
  const [shouldRun, setShouldRun] = useState(
    typeof document !== "undefined" && document.hasFocus()
  );

  useEffect(() => {
    const onFocus = () => {
      check();
      setShouldRun(true);
    };
    const onBlur = () => setShouldRun(false);

    window.addEventListener("focus", onFocus);
    window.addEventListener("blur", onBlur);

    return () => {
      window.removeEventListener("focus", onFocus);
      window.removeEventListener("blur", onBlur);
    };
  }, [check]);

  useInterval(check, shouldRun ? 1000 : null);

  return null;
}

Get our latest in your inbox.

Join our newsletter to hear about Sam and Ryan's newest blog posts, code recipes, and videos.