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 : nullis 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 | undefinedsince the fetch may not have run yet. - If the key is
null, TypeScript infers the key type asnull. Use a union type or generic to keep it clean.
const { data } = useSWR<Team>(
user ? `/api/teams/${user.teamId}` : null,
fetcher
);
// data: Team | undefinedGotchas
- 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
nullto a real value,isLoadingbecomestruefor that transition. Plan your loading UI accordingly. - If you use a key function that throws, SWR treats it the same as a
nullkey. However, thrown errors in the key function are silently caught and do not appear inerror. - Do not confuse conditional fetching (null key) with error handling. A null key means "do not fetch," not "fetch failed."
Alternatives
| Approach | Pros | Cons |
|---|---|---|
| Null key (SWR) | Declarative, automatic trigger on change | Creates waterfall for dependent fetches |
| enabled option (React Query) | Explicit boolean flag | Not available in SWR natively |
| useEffect guard | Familiar imperative pattern | Loses SWR caching and dedup benefits |
| Server-side join | Single request, no waterfall | Requires 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 | undefinedWhen 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.