Dhaka--:--
All notes

20 Jun 2026 · 2 min read

Surviving signup spikes with React Query

When hundreds of people hit the same page at once, caching is a feature. How stale-while-revalidate keeps your server calm and the UI instant.

  • React
  • React Query
  • Performance

Tournament registration opens, a link goes out in a group chat, and suddenly a few hundred people refresh the same page. Every refresh is a request. If each one hits the database, the server falls over exactly when it matters.

You can't stop the stampede, but you can make it cheap.

Cache on the client first

React Query's core idea is stale-while-revalidate: show what you already have instantly, then refresh quietly in the background. Tell it how long data stays "fresh":

const { data: event } = useQuery({
  queryKey: ["event", slug],
  queryFn: () => fetch(`/api/events/${slug}`).then((r) => r.json()),
  staleTime: 30_000,      // no refetch for 30s, even across component remounts
  gcTime: 5 * 60_000,     // keep unused data for 5 minutes
});

Navigating back and forth between pages now costs nothing. Previously every visit was a fresh request.

Keep the previous page on screen while filters change

const { data } = useQuery({
  queryKey: ["players", { page, search }],
  queryFn: () => fetchPlayers({ page, search }),
  placeholderData: keepPreviousData,
});

Users see the old list until the new one arrives, so the UI never flashes empty.

Optimistic registration

Registering should feel instant. Update the cache first, roll back if the server says no:

const register = useMutation({
  mutationFn: registerForEvent,
  onMutate: async () => {
    await qc.cancelQueries({ queryKey: ["event", slug] });
    const prev = qc.getQueryData(["event", slug]);
    qc.setQueryData(["event", slug], (e: any) => ({ ...e, joined: true }));
    return { prev };
  },
  onError: (_e, _v, ctx) => qc.setQueryData(["event", slug], ctx?.prev),
  onSettled: () => qc.invalidateQueries({ queryKey: ["event", slug] }),
});

Don't forget the server

Client caching reduces repeat visits, but first visits still land on your API. Pair it with:

  • HTTP cache headers or CDN caching for read-only event data
  • Rate limiting on the write endpoint
  • A unique constraint on (event, player) so double-clicks can't double-register

The mindset

Treat a spike as a read-heavy workload with a tiny write path. Make reads nearly free, make writes safe, and the stampede becomes just another Tuesday.