Loading data for routes
The post page needs the post's data, and the list page needs the list. Solid Router can load them for you: query describes a request and caches the result, createAsync reads it in a component, and the route's preload attribute starts loading before the page even renders.
Query functions
Collect the API requests in one file, src/api/posts.js:
// src/api/posts.js
import { query } from '@solidjs/router';
const API = 'https://jsonplaceholder.typicode.com';
export const getPosts = query(async () => {
const res = await fetch(`${API}/posts?_limit=10`);
return res.json();
}, 'posts');
export const getPost = query(async (id) => {
const res = await fetch(`${API}/posts/${id}`);
return res.json();
}, 'post');The second argument of query is a name for the cache. Calling getPost('7') again within a short time returns the already loaded result instead of a new request.
Reading in a component
// src/pages/Posts.jsx
import { For } from 'solid-js';
import { A, createAsync } from '@solidjs/router';
import { getPosts } from '../api/posts';
export default function Posts() {
const posts = createAsync(() => getPosts());
return (
<ul>
<For each={posts()}>
{(post) => (
<li>
<A href={`/posts/${post.id}`}>{post.title}</A>
</li>
)}
</For>
</ul>
);
}createAsync returns a getter function, like a signal. At http://localhost:3000/posts a list of ten posts from the server appears.
The post page
// src/pages/Post.jsx
import { Show } from 'solid-js';
import { useParams, createAsync } from '@solidjs/router';
import { getPost } from '../api/posts';
export default function Post() {
const params = useParams();
const post = createAsync(() => getPost(params.id));
return (
<Show when={post()}>
<article>
<h1>{post().title}</h1>
<p>{post().body}</p>
</article>
</Show>
);
}The function inside createAsync reads params.id, so the request runs again when you move to another post. At /posts/1 you see the title and text of the first post.
Preloading
// src/index.jsx
import { getPosts, getPost } from './api/posts';
<Route path="/posts" component={Posts} preload={() => getPosts()} />
<Route path="/posts/:id" component={Post} preload={({ params }) => getPost(params.id)} />preload is called when you navigate to the route and even when the mouse hovers over an A link. By the time the component calls the same function, the data is already in the cache.
Loading and errors
While data is on its way, createAsync behaves like a resource: wrap {props.children} in the App layout in Suspense with a fallback, and in ErrorBoundary if you like — then loading and errors of every page are handled in one place.
| Tool | Job |
|---|---|
query(fn, 'name') | Describe a request and cache it |
createAsync(() => ...) | Read the data in a component |
preload on Route | Start loading early |
Common mistake
Calling getPost(params.id) right in the component body and waiting for it with await. The component body is synchronous and runs once: the component will not wait for the data and will not update when the post changes. Always wrap the call in createAsync(() => ...).