Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
39f3b9f
Fix useSubmission
amirhhashemi May 16, 2025
f3c6bd0
Update
amirhhashemi Jun 8, 2025
e9d0891
Update
amirhhashemi Jun 11, 2025
5a7ec5c
Update info callout titles (#1207)
amirhhashemi Jun 18, 2025
aec869e
update
amirhhashemi Jul 2, 2025
73da3ce
update
amirhhashemi Jul 3, 2025
ebd6265
Merge branch 'main' into fix-use-submission-situation
kodiakhq[bot] Jul 30, 2025
b505134
Merge branch 'main' into fix-use-submission-situation
kodiakhq[bot] Aug 1, 2025
207a728
Merge branch 'main' into fix-use-submission-situation
kodiakhq[bot] Aug 13, 2025
e138cd6
update
amirhhashemi Aug 5, 2025
9c274ac
update
amirhhashemi Aug 9, 2025
eb6d2e0
update
amirhhashemi Aug 14, 2025
bca58d4
update
amirhhashemi Aug 16, 2025
08af13a
Merge branch 'main' into fix-use-submission-situation
kodiakhq[bot] Aug 16, 2025
f36f2f8
Merge branch 'main' into fix-use-submission-situation
kodiakhq[bot] Aug 20, 2025
bf29f48
Merge branch 'main' into fix-use-submission-situation
kodiakhq[bot] Aug 20, 2025
65900a3
update
amirhhashemi Aug 23, 2025
01f6654
update
amirhhashemi Aug 24, 2025
319c28d
update
amirhhashemi Aug 25, 2025
a1cad3c
update
amirhhashemi Aug 25, 2025
0eff6f4
update
amirhhashemi Aug 25, 2025
da0fa78
Update src/routes/solid-router/concepts/queries.mdx
amirhhashemi Aug 26, 2025
d164be5
Update src/routes/solid-router/concepts/queries.mdx
amirhhashemi Aug 26, 2025
7f04981
Update src/routes/solid-router/concepts/queries.mdx
amirhhashemi Aug 26, 2025
a3f4d0c
Update src/routes/solid-router/concepts/queries.mdx
amirhhashemi Aug 26, 2025
b6a2b2d
Update src/routes/solid-router/concepts/queries.mdx
amirhhashemi Aug 26, 2025
784bb63
Update src/routes/solid-router/concepts/queries.mdx
amirhhashemi Aug 26, 2025
5fe813a
Update src/routes/solid-router/concepts/queries.mdx
amirhhashemi Aug 26, 2025
3f72aca
Update src/routes/solid-router/concepts/queries.mdx
amirhhashemi Aug 26, 2025
972cc02
Update src/routes/solid-router/concepts/queries.mdx
amirhhashemi Aug 26, 2025
e4cdc46
Update src/routes/solid-router/concepts/queries.mdx
amirhhashemi Aug 26, 2025
68aa8a6
Update src/routes/solid-router/concepts/queries.mdx
amirhhashemi Aug 26, 2025
51c9c7d
Update src/routes/solid-start/guides/data-fetching.mdx
amirhhashemi Aug 26, 2025
8f2a98e
Update src/routes/solid-start/guides/data-fetching.mdx
amirhhashemi Aug 26, 2025
8cc3137
apply review results
amirhhashemi Aug 26, 2025
9d3df64
experiment with new structure
amirhhashemi Aug 26, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/middleware/legacy-routes-redirect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ const LEGACY_ROUTES = {

"/solid-router/reference/response-helpers/revalidate":
"/solid-router/reference/data-apis/revalidate",

"/solid-start/guides/data-loading": "/solid-start/guides/data-fetching",
} as const;

function isLegacyRoute(path: string): path is keyof typeof LEGACY_ROUTES {
Expand Down
470 changes: 382 additions & 88 deletions src/routes/solid-router/concepts/actions.mdx

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/routes/solid-router/concepts/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"nesting.mdx",
"layouts.mdx",
"alternative-routers.mdx",
"queries.mdx",
"actions.mdx"
]
}
}
4 changes: 4 additions & 0 deletions src/routes/solid-router/data-fetching/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "Data fetching",
"pages": ["queries.mdx", "streaming.mdx", "revalidation.mdx", "how-to"]
}
4 changes: 4 additions & 0 deletions src/routes/solid-router/data-fetching/how-to/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"title": "How to",
"pages": ["preload-data.mdx", "handle-error-and-loading-states.mdx"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
title: "Handle pending and error states"
---

The `createAsync` primitive is designed to work with Solid's native components for managing asynchronous states.
It reports its pending state to the nearest [`<Suspense>` boundary](/reference/components/suspense) to display loading fallbacks, and propagate errors to an [`<ErrorBoundary>`](/reference/components/error-boundary) for handling and displaying error messages.

```tsx
import { Suspense, ErrorBoundary, For } from "solid-js";
import { query, createAsync } from "@solidjs/router";

const getNewsQuery = query(async () => {
// ... Fetches the latest news from an API.
}, "news");

function NewsFeed() {
const news = createAsync(() => getNewsQuery());

return (
<ErrorBoundary fallback={<p>Could not fetch news.</p>}>
<Suspense fallback={<p>Loading news...</p>}>
<ul>
<For each={news()}>{(item) => <li>{item.headline}</li>}</For>
</ul>
</Suspense>
</ErrorBoundary>
);
}
```
54 changes: 54 additions & 0 deletions src/routes/solid-router/data-fetching/how-to/preload-data.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
title: "Preload data"
---

Preloading data improves perceived performance by fetching the data for an upcoming page before the user navigates to it.

Solid Router initiates preloading in two scenarios:

- When a user indicates intent to navigate to the page (e.g., by hovering over a link).
- When the route's component is rendering.

This ensures data fetching starts as early as possible, often making data ready once the component renders.

Preloading is configured using the [`preload`](/solid-router/reference/preload-functions/preload) prop on a [`Route`](/solid-router/reference/components/route).
This prop accepts a function that calls one or more queries.
When triggered, the queries execute and their results are stored in a short-lived internal cache.
Once the user navigates and the destination route’s component renders, any `createAsync` calls within the page will consume the preloaded data.
Thanks to the [deduplication mechanism](#deduplication), no redundant network requests are made.

```tsx {18-20,27}
import { Show } from "solid-js";
import { Route, query, createAsync } from "@solidjs/router";

const getProductQuery = query(async (id: string) => {
// ... Fetches product details for the given ID.
}, "product");

function ProductDetails(props) {
const product = createAsync(() => getProductQuery(props.params.id));

return (
<Show when={product()}>
<h1>{product().name}</h1>
</Show>
);
}

function preloadProduct({ params }: { params: { id: string } }) {
getProductQuery(params.id);
}

function Routes() {
return (
<Route
path="/products/:id"
component={ProductDetails}
preload={preloadProduct}
/>
);
}
```

In this example, hovering a link to `/products/:id` triggers `preloadProduct`.
When the `ProductDetails` component renders, its `createAsync` call will instantly resolve with the preloaded data.
81 changes: 81 additions & 0 deletions src/routes/solid-router/data-fetching/queries.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
---
title: "Queries"
---

Queries are the core building blocks for data fetching in Solid Router.
They provide an elegant solution for managing data fetching.

## Defining queries

They are defined using the [`query` function](/solid-router/reference/data-apis/query).
It wraps the data-fetching logic and extends it with powerful capabilities like [request deduplication](#deduplication) and [automatic revalidation](#revalidation).

The `query` function takes two parameters: a **fetcher** and a **name**.

- The **fetcher** is an asynchronous function that fetches data from any source, such as a remote API.
- The **name** is a unique string used to identify the query.
When a query is called, Solid Router uses this name and the arguments passed to the query to create a unique key, which is used for the internal deduplication mechanism.

```tsx
import { query } from "@solidjs/router";

const getUserProfileQuery = query(async (userId: string) => {
const response = await fetch(
`https://api.example.com/users/${encodeURIComponent(userId)}`
);
const json = await response.json();

if (!response.ok) {
throw new Error(json?.message ?? "Failed to load user profile.");
}

return json;
}, "userProfile");
```

In this example, the defined query fetches a user's profile from an API.
If the request fails, the fetcher will throw an error that will be caught by the nearest [`<ErrorBoundary>`](/reference/components/error-boundary) in the component tree.

## Using queries in components

Defining a query does not by itself fetch any data.
To access its data, the query can be used with the [`createAsync` primitive](/solid-router/reference/data-apis/create-async).
`createAsync` takes an asynchronous function, such as a query, and returns a signal that tracks its result.

```tsx
import { For, Show } from "solid-js";
import { query, createAsync } from "@solidjs/router";

const getArticlesQuery = query(async () => {
// ... Fetches a list of articles from an API.
}, "articles");

function Articles() {
const articles = createAsync(() => getArticlesQuery());

return (
<Show when={articles()}>
<For each={articles()}>{(article) => <p>{article.title}</p>}</For>
</Show>
);
}
```

In this example, `createAsync` is used to call the query.
Once the query completes, `articles` holds the result, which is then rendered.

:::tip
When working with complex data types, such as arrays or deeply nested objects, the [`createAsyncStore` primitive](/solid-router/reference/data-apis/create-async-store) offers a more ergonomic and performant solution.
It works like `createAsync`, but returns a [store](/concepts/stores) for easier state management..
:::

## Deduplication

A key feature of queries is their ability to deduplicate requests, preventing redundant data fetching in quick succession.

One common use case is preloading: when a user hovers over a link, the application can begin preloading the data for the destination page.
If the user then clicks the link, the query has already been completed, and the data is available instantly without triggering another network request.
This mechanism is fundamental to the performance of Solid Router applications.

Deduplication also applies when multiple components on the same page use the same query.
As long as at least one component is actively using the query, Solid Router will reuse the cached result instead of refetching the data.
60 changes: 60 additions & 0 deletions src/routes/solid-router/data-fetching/revalidation.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: "Revalidation"
---

Since server data can change, Solid Router provides mechanisms to revalidate queries and keep the UI up to date.

The most common case is **automatic revalidation**.
After an [action](/solid-router/concepts/actions) completes successfully, Solid Router automatically revalidates all active queries on the page.
For more details, see the [actions documentation](/solid-router/concepts/actions#automatic-data-revalidation).

For more fine-grained control, you can trigger revalidation manually with the [`revalidate` function](/solid-router/reference/data-apis/revalidate).
It accepts a query key (or an array of keys) to target specific queries.
Each query exposes two properties for this: `key` and `keyFor`.

- `query.key` is the base key for a query and targets all of its instances.
Using this key will revalidate all data fetched by that query, regardless of the arguments provided.
- `query.keyFor(arguments)` generates a key for a specific set of arguments, allowing you to target and revalidate only that particular query.

```tsx
import { For } from "solid-js";
import { query, createAsync, revalidate } from "@solidjs/router";

const getProjectsQuery = query(async () => {
// ... Fetches a list of projects.
}, "projects");

const getProjectTasksQuery = query(async (projectId: string) => {
// ... Fetches a list of tasks for a project.
}, "projectTasks");

function Projects() {
const projects = createAsync(() => getProjectsQuery());

function refetchAllTasks() {
revalidate(getProjectTasksQuery.key);
}

return (
<div>
<button onClick={refetchAllTasks}>Refetch all tasks</button>
<For each={projects()}>{(project) => <Project id={project.id} />}</For>
</div>
);
}

function Project(props: { id: string }) {
const tasks = createAsync(() => getProjectTasksQuery(props.id));

function refetchTasks() {
revalidate(getProjectTasksQuery.keyFor(props.id));
}

return (
<div>
<button onClick={refetchTasks}>Refetch tasks for this project</button>
<For each={project.tasks}>{(task) => <div>{task.title}</div>}</For>
</div>
);
}
```
90 changes: 90 additions & 0 deletions src/routes/solid-router/data-fetching/streaming.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
title: "Streaming"
---

In traditional server-rendered applications, the server must fetch all data before rendering and sending the page to the browser.
If some queries are slow, this delays the initial load.
**Streaming** solves this by sending the page’s HTML shell immediately and progressively streaming data-dependent sections as they become ready.

When a query is accessed during a server-side render, Solid suspends the UI until the data resolves.
By default, this suspension affects the entire page.

To control this behavior, you can use suspense boundaries - regions of the component tree defined by a [`<Suspense>` component](/reference/components/suspense).
It isolates asynchronous behavior to a specific section of the page.

Content inside the boundary is managed by Solid’s concurrency system: if it isn’t ready, the boundary’s fallback UI is shown while the rest of the page renders and streams immediately.
Once the data resolves, the server streams the final HTML for that section, replacing the fallback and letting users see and interact with most of the page much sooner.

```tsx
import { Suspense, For } from "solid-js";
import { query, createAsync } from "@solidjs/router";

const getAccountStatsQuery = query(async () => {
// ... Fetches account statistics.
}, "accountStats");

const getRecentTransactionsQuery = query(async () => {
// ... Fetches a list of recent transactions.
}, "recentTransactions");

function Dashboard() {
const stats = createAsync(() => getAccountStatsQuery());
const transactions = createAsync(() => getRecentTransactionsQuery());

return (
<div>
<h1>Dashboard</h1>
<Suspense fallback={<p>Loading account stats...</p>}>
<For each={stats()}>
{(stat) => (
<p>
{stat.label}: {stat.value}
</p>
)}
</For>
</Suspense>

<Suspense fallback={<p>Loading recent transactions...</p>}>
<For each={transactions()}>
{(transaction) => (
<h2>
{transaction.description} - {transaction.amount}
</h2>
)}
</For>
</Suspense>
</div>
);
}
```

For example, each `<Suspense>` component creates its own independent boundary.
The server can stream the heading `<h1>Dashboard</h1>` immediately, while the `stats` and `transactions` are handled separately.
If the `transactions` query is slow, only its boundary will display a fallback, while `stats` will render as soon as its data is ready.

## When to disable streaming

While streaming is powerful, there are cases where it is better to wait for the data to load on the server.
In these situations, you can use the `deferStream` option in `createAsync`.

When `deferStream` is set to `true`, the server waits for the query to resolve before sending the initial HTML.

A common reason to disable streaming is for Search Engine Optimization (SEO).
Some search engine crawlers may not wait for streamed content to load.
If critical data, such as a page title or meta description, affects SEO, it should be included in the initial server response.

```tsx
import { query, createAsync } from "@solidjs/router";

const getArticleQuery = query(async () => {
// ... Fetches an article.
}, "article");

function ArticleHeader() {
const article = createAsync(() => getArticleQuery(), {
deferStream: true,
});

return <h1>{article()?.title}</h1>;
}
```
1 change: 1 addition & 0 deletions src/routes/solid-router/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"getting-started",
"concepts",
"rendering-modes",
"data-fetching",
"advanced-concepts",
"guides"
]
Expand Down
Loading