Loading data: createResource and Suspense
A resource is a signal for asynchronous data: it calls the loading function itself, keeps the result and reports whether loading is in progress or an error happened. This is how Solid loads server data without hand-written effects and flags.
From this lesson on, the example becomes a small blog. Create a new project the same way as the to-do list:
npx degit solidjs/templates/js blog
cd blog
npm install
npm run devPosts will come from the test API https://jsonplaceholder.typicode.com.
createResource
Create the component src/components/PostList.jsx:
// src/components/PostList.jsx
import { createResource, For, Show } from 'solid-js';
const fetchPosts = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/posts?_limit=5');
return res.json();
};
export default function PostList() {
const [posts] = createResource(fetchPosts);
return (
<Show when={!posts.loading} fallback={<p>Loading...</p>}>
<ul>
<For each={posts()}>{(post) => <li>{post.title}</li>}</For>
</ul>
</Show>
);
}Render <PostList /> in src/App.jsx. At http://localhost:3000 you first see "Loading...", then a list of five post titles.
What a resource has
| Property | Value |
|---|---|
posts() | The loaded data or undefined |
posts.loading | true while the request is running |
posts.error | The error, if the request failed |
The second element of the pair is an object with the functions refetch (load again) and mutate (replace the data by hand): const [posts, { refetch }] = createResource(fetchPosts).
A resource with a parameter
If you pass a signal as the first argument, the resource reloads each time it changes:
// src/components/PostView.jsx
import { createSignal, createResource, Show } from 'solid-js';
const fetchPost = async (id) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`);
return res.json();
};
export default function PostView() {
const [id, setId] = createSignal(1);
const [post] = createResource(id, fetchPost);
return (
<div>
<button onClick={() => setId(id() + 1)}>Next post</button>
<Show when={post()}>{(p) => <h2>{p().title}</h2>}</Show>
</div>
);
}Each click loads the next post and shows its title. The signal's value arrives in fetchPost as the first argument.
Suspense
Suspense shows fallback content until every resource read inside it has loaded:
// src/App.jsx
import { Suspense } from 'solid-js';
import PostList from './components/PostList';
export default function App() {
return (
<Suspense fallback={<p>Loading the blog...</p>}>
<PostList />
</Suspense>
);
}This way you can drop Show from PostList and describe loading once for the whole page.
Common mistake
Loading data in createEffect or onMount and putting it into a signal by hand. It works, but you have to add loading and error flags yourself, and Suspense knows nothing about such a request. For server data use createResource.