React SME Cookbook
All FAQs

Search Documentation

Search across all documentation pages

swrconditionaldependentnull-key

Conditional and Dependent Fetching

Recipe

Pass null or a falsy value as the key to prevent SWR from fetching. Use this to wait for dependencies, gate requests behind authentication, or chain sequential fetches.

"use client";
 
import useSWR from "swr";
 
function UserProjects({ userId }: { userId: string | null }) {
  // Won't fetch until userId is available
  const { data } = useSWR(userId ? `/api/users/${userId}/projects` : null, fetcher);
 
  return <div>{data?.length ?? 0} projects</div>;
}

Working Example

"use client";
 
import useSWR from "swr";
 
const fetcher = (url: string) => fetch(url).then((r) => r.json());
 
interface User {
  id: string;
  name: string;
  teamId: string;
}
 
interface Team {
  id: string;
  name: string;
  members: string[];
}
 
export default function UserTeamInfo({ userId }: { userId: string }) {
  // Step 1: Fetch user
  const { data: user, isLoading: userLoading } = useSWR<User>(
    `/api/users/${userId}`,
    fetcher
  );
 
  // Step 2: Fetch team only after user loads (dependent fetching)
  const { data: team, isLoading: teamLoading } = useSWR<Team>(
    user ? `/api/teams/${user.teamId}` : null,
    fetcher
  );
 
  if (userLoading) return <p>Loading user...</p>;
  if (teamLoading) return <p>Loading team for {user?.name}...</p>;
 
  return (
    <div>
      <p>{user?.name} belongs to team {team?.name}</p>
      <p>Team has {team?.members.length} members</p>
    </div>
  );
}

Deep Dive

How It Works

  • When the key is null, undefined, false, or "", SWR skips the fetch entirely and returns { data: undefined, error: undefined, isLoading: false }.
  • This is the idiomatic way to implement dependent (waterfall) fetching — the second request's key is derived from the first request's data.
  • Once the key becomes truthy, SWR triggers the fetch immediately.
  • The key function form () => condition ? key : null is also supported and evaluated on every render.

Variations

Key as a function:

const { data } = useSWR(
  () => (isLoggedIn ? `/api/profile` : null),
  fetcher
);

Multiple dependencies:

const { data: user } = useSWR("/api/me", fetcher);
const { data: org } = useSWR(user ? `/api/orgs/${user.orgId}` : null, fetcher);
const { data: billing } = useSWR(
  user && org ? `/api/orgs/${org.id}/billing` : null,
  fetcher
);

Conditional with array key:

const { data } = useSWR(
  token ? ["/api/data", token] : null,
  ([url, token]) =>
    fetch(url, { headers: { Authorization: `Bearer ${token}` } }).then((r) => r.json())
);

Throw in key function to pause:

// Throwing inside the key function also pauses fetching
const { data } = useSWR(
  () => `/api/users/${userId ?? throwUndefined()}`,
  fetcher
);
 
function throwUndefined(): never {
  throw new Error("not ready");
}

TypeScript Notes

  • When using conditional keys, the data type remains Data | undefined since the fetch may not have run yet.
  • If the key is null, TypeScript infers the key type as null. Use a union type or generic to keep it clean.
const { data } = useSWR<Team>(
  user ? `/api/teams/${user.teamId}` : null,
  fetcher
);
// data: Team | undefined

Gotchas

  • Dependent fetching creates a waterfall. Each chained request must wait for the previous one. Keep chains short to avoid slow page loads.
  • When the key transitions from null to a real value, isLoading becomes true for that transition. Plan your loading UI accordingly.
  • If you use a key function that throws, SWR treats it the same as a null key. However, thrown errors in the key function are silently caught and do not appear in error.
  • Do not confuse conditional fetching (null key) with error handling. A null key means "do not fetch," not "fetch failed."

Alternatives

ApproachProsCons
Null key (SWR)Declarative, automatic trigger on changeCreates waterfall for dependent fetches
enabled option (React Query)Explicit boolean flagNot available in SWR natively
useEffect guardFamiliar imperative patternLoses SWR caching and dedup benefits
Server-side joinSingle request, no waterfallRequires API changes, less flexible

FAQs

What values can I pass as a key to prevent SWR from fetching?

SWR skips the fetch when the key is null, undefined, false, or an empty string "". Any of these falsy values will prevent the request.

What does useSWR return when the key is null?

It returns { data: undefined, error: undefined, isLoading: false }. No fetch is made at all -- isLoading is false because there is nothing to load.

How do I chain sequential (dependent) fetches with SWR?

Derive the second request's key from the first request's data:

const { data: user } = useSWR("/api/me", fetcher);
const { data: team } = useSWR(
  user ? `/api/teams/${user.teamId}` : null,
  fetcher
);

The team fetch runs only after user is available.

Can I pass a function as the key to useSWR for conditional fetching?

Yes. SWR evaluates a key function on every render:

const { data } = useSWR(
  () => (isLoggedIn ? `/api/profile` : null),
  fetcher
);
Gotcha: Does dependent fetching create a waterfall, and how should I handle it?

Yes. Each chained request waits for the previous one. Keep dependency chains short (2-3 max) to avoid slow page loads. If possible, combine data into a single API endpoint on the server side.

What happens when a key function throws an error?

SWR treats a thrown error inside the key function the same as a null key -- it pauses fetching. However, the error is silently caught and does not appear in the error return value.

How do I use conditional fetching with array keys?
const { data } = useSWR(
  token ? ["/api/data", token] : null,
  ([url, token]) =>
    fetch(url, {
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.json())
);
Gotcha: Is a null key the same as a failed fetch?

No. A null key means "do not fetch." It is not an error state. error remains undefined and isLoading is false. Do not confuse conditional fetching with error handling.

How does TypeScript handle the data type when the key might be null?

The data type remains Data | undefined since the fetch may not have run yet. Use a union type or generic to keep it clean:

const { data } = useSWR<Team>(
  user ? `/api/teams/${user.teamId}` : null,
  fetcher
);
// data: Team | undefined
When the key transitions from null to a real value, what happens to isLoading?

isLoading becomes true for that transition while SWR fetches data for the newly-truthy key. Plan your loading UI to account for this state change.

How does SWR's conditional fetching compare to React Query's enabled option?

SWR uses null/falsy keys to prevent fetching, while React Query has an explicit enabled boolean option. SWR's approach is declarative and key-driven; React Query's is more explicit but not available natively in SWR.