React cookbook · Beginner

Poll a long-running job's status

Show progress for a report generation job until it finishes, then stop polling.

Approach

Use useQuery with a dynamic refetchInterval that returns a number while the job is running and false when it's done or failed. TanStack Query pauses polling in background tabs by default.

Solution

import { useQuery } from "@tanstack/react-query";

type Job = { id: string; status: "queued" | "running" | "done" | "failed"; progress: number; download_url?: string };

export function JobProgress({ jobId }: { jobId: string }) {
  const { data: job } = useQuery({
    queryKey: ["jobs", jobId],
    queryFn: () => fetch(`/api/jobs/${jobId}`).then((r) => r.json() as Promise<Job>),
    refetchInterval: (query) => {
      const status = query.state.data?.status;
      return status === "done" || status === "failed" ? false : 2000;
    },
  });

  if (!job) return <p>Starting…</p>;
  if (job.status === "failed") return <p role="alert">The report failed. Please try again.</p>;
  if (job.status === "done") return <a href={job.download_url}>Download report</a>;
  return (
    <div>
      <progress value={job.progress} max={100} aria-label="Report progress" />
      <span aria-live="polite">{job.progress}%</span>
    </div>
  );
}

Pitfalls

  • Polling forever when the job fails.
  • Very short intervals hammering the API.
  • Polling for many jobs individually — batch status requests.

Variations

  • Server-Sent Events or WebSockets for instant updates.
  • Exponential backoff on the interval for long jobs.

Further reading: TanStack Query: useQuery

Reference: https://tanstack.com/query/latest/docs/framework/react/reference/useQuery

More React recipes