Using SolidJS createResource for Declarative Data Fetching in Client‑Side Components
Learn how to fetch, cache, and handle loading and error states with SolidJS’s createResource. This guide walks through setting up a source signal, defining a fetcher, and using the resource in JSX, with checks and recovery strategies.
01 Jan 2026, 23:59 UTC

Desired Outcome
Use createResource to declaratively fetch remote data in a SolidJS component. The component should automatically show a loading UI while the fetch is in progress, display data once resolved, and render an error UI if the fetch rejects. When the source signal changes, the fetch should re‑execute without manual effects, and the resource should cache results per component instance.
Prerequisites
- SolidJS 1.6+ (or SolidStart with client‑side rendering enabled).
- JSX transform configured (e.g.,
solid-js/jsx-runtime). - Basic understanding of Solid’s fine‑grained reactivity: signals, accessors, and effects.
- Knowledge of
Suspensefor async boundaries (optional but recommended).
Step‑by‑Step Procedure
1. Define a Source Signal
A source signal drives when the resource should re‑fetch. It can be a simple createSignal or an accessor that derives from props. Example: a user ID that changes when the user selects a different profile.
import { createSignal } from 'solid-js';
const [userId, setUserId] = createSignal(1); // initial ID
2. Create a Fetcher Function
The fetcher receives the source value and must return a Promise. Keep it pure; avoid side effects that could trigger duplicate requests. If you need cancellation, wrap the fetch with an AbortController and expose the abort logic via the returned promise.
const fetchUser = async (id) => {
const controller = new AbortController();
const signal = controller.signal;
const response = await fetch(`https://api.example.com/users/${id}`, { signal });
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
return { data, abort: () => controller.abort() };
};
3. Declare the Resource
Pass the source signal and fetcher to createResource. The returned object exposes data, loading, and error accessors, and methods refetch and mutate.
import { createResource } from 'solid-js';
const [user, { refetch, mutate }] = createResource(userId, fetchUser);
4. Use the Resource in JSX
Read the data via user(). While the promise is pending, user.loading() is true; if it rejects, user.error() holds the error. Wrap the component or a portion of it in <Suspense> to automatically suspend rendering until the data arrives.
import { Suspense } from 'solid-js';
function UserProfile() {
return (
<div>
<h3>User Profile</h3>
<Suspense fallback={ <div>Loading…</div> }>
<UserDetails user={user()} />
</Suspense>
<ErrorBoundary fallback={ErrorUI}>
<Show when={user.error()} fallback={null}>
<ErrorUI error={user.error()} />
</Show>
</ErrorBoundary>
</div>
);
}
Expected Checks
- Re‑render Trigger: Changing
userId(e.g.,setUserId(2)) should re‑executefetchUserand update the UI without manual effects. - Loading UI: While the fetch is pending,
user.loading()is true, and theSuspensefallback renders. - Error UI: If
fetchUserrejects,user.error()is populated and the error component displays. - Cache Confirmation: Re‑rendering without changing
userIdshould not trigger a new fetch; verify via network tab or console logs. - Cleanup on Unmount: Unmount the component while a fetch is pending and ensure no state updates occur afterward (no “setState on unmounted component” warnings).
Recovery Options
- Fallback UI: Provide a graceful fallback for errors, e.g., a retry button that calls
refetchor resetsuserId. - Retry Logic: Wrap
fetchUserwith exponential backoff; expose aretrymethod that internally callsrefetch. - Abort Handling: If the fetcher returns an
abortfunction, expose it viamutateor a custom hook to cancel in-flight requests when the component unmounts or when a new source value arrives. - Isolate Resources: Keep the resource declaration inside the component to avoid shared state across mounts; if you need shared caching, lift the source signal to a parent or context.
Limitations & Caveats
- Server vs Client:
createResourcebehaves differently in server rendering; it will not suspend on the server unlessSuspenseis explicitly enabled in SolidStart. - Caching Scope: The cache is per component instance. Remounting the component clears the cache; consider using a shared store if persistence across mounts is required.
- Side Effects in Fetcher: Side effects (e.g., localStorage writes) inside the fetcher can cause duplicate operations if the source changes rapidly.
- Abort Support: Not all fetch targets honor
AbortSignal; test abort behavior manually.
Practical Verification Checklist
- Open the component in a browser, open devtools, and observe the network tab when
userIdchanges. - Force
fetchUserto reject (e.g., by returningPromise.reject(new Error('boom'))) and confirm the error UI renders. - Change
userIdwhile a fetch is pending and verify that the previous request is aborted or ignored. - Unmount the component mid‑fetch and check the console for any “setState on unmounted component” warnings.
- Inspect the resource’s
loadinganderrorflags via a temporaryconsole.log(user.loading(), user.error())inside acreateEffect.
Conclusion
By following this guide, you can harness SolidJS’s createResource to build robust, reactive data‑fetching components that automatically handle loading, error, and caching states. The declarative pattern keeps the component logic concise, while the fine‑grained reactivity ensures minimal unnecessary network traffic. Remember to test the component under real‑world conditions—changing source values rapidly, simulating network errors, and unmounting during pending requests—to validate that your recovery strategies work as intended.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.