You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
FEAT: route loaders now do not block SSR start, and they can no longer redirect or error the response. Instead, use request handlers like `onGet`. Consider each route loader as a separate request that does not impact the page.
5
+
route loaders gain a `blockSSR` option (default `true`); set `blockSSR: false` to run a loader in the background without blocking SSR
Copy file name to clipboardExpand all lines: packages/docs/src/routes/docs/(qwikrouter)/route-loader/index.mdx
+20-1Lines changed: 20 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -55,6 +55,7 @@ A few properties of route loaders:
55
55
-**Keyed by route, not by call site.** Two visits to the same URL share one logical "result" per "route + url params + search params" set, not one per `useProductDetails()` call.
56
56
-**Result is an `AsyncSignal`.** Components read `.value`, watch `.loading`, and check `.error`. Reading `.value` while the signal is in error state re-throws the error.
57
57
-**Each loader has its own JSON endpoint.** The client fetches `q-loader-{id}.{hash}.json` for navigation, polling, and post-action invalidation. Browser HTTP cache, ETags, and the `expires` option all apply.
58
+
-**Blocks SSR by default.** The server awaits the loader before rendering the page, so a `throw redirect()` or `throw error()` controls the response. Opt out with `blockSSR: false` to run it in the background — see [Background loaders](#background-loaders-blockssr).
58
59
59
60
This means a `routeLoader$()` should be a **pure function of the URL**: same `params` and `search` → same response. Mixing in any other input (action state, request bodies, one-shot tokens) makes the cached responses inconsistent — see [Loaders cannot read action state](#loaders-cannot-read-action-state).
60
61
@@ -97,7 +98,7 @@ Loaders signal failure two ways:
97
98
-**`throw requestEvent.error(status, data)`** — throw a `ServerError` directly. `signal.error` is `ServerError {status, data}`. Reading `signal.value` re-throws the error, so you should branch on `signal.error` first. This is the recommended way to signal failure from a loader.
98
99
-**`return requestEvent.fail(status, data)`** — returns an inline failure. `signal.value.failed` is `true` and `signal.value` contains the rest of the data.
99
100
100
-
In both cases, the HTTP response status is set to `status`, and the result is not cacheable.
101
+
In both cases the result is not cacheable. For a blocking loader (the default) the HTTP response status is also set to `status`; a `blockSSR: false` loader is isolated from the page response — see [Background loaders](#background-loaders-blockssr).
101
102
102
103
## Multiple `routeLoader$`s
103
104
@@ -256,6 +257,23 @@ export default () => ({
256
257
257
258
When using lazy-loaded data, branch on `.loading` to show a placeholder until it arrives — see [Reading the result in a component](#reading-the-result-in-a-component).
258
259
260
+
## Background loaders (`blockSSR`)
261
+
262
+
By default a loader **blocks SSR**: the server awaits it before rendering, so a `throw redirect()` or `throw error()` short-circuits the HTTP response. When several loaders block, the first one — in route order — to redirect or error wins.
263
+
264
+
Set `blockSSR: false` to run a loader in the background instead. The server starts it but does not wait: rendering begins immediately, and reading `.value` suspends only that read until the loader resolves. This starts SSR sooner, and is the recommended setting, unless you need to control the request from the loader.
A background loader is treated as a **separate request**: it cannot redirect or error the page response. If it calls `redirect()` or `error()`, that control flow is captured on its own signal (surfacing when `.value` is read) and never changes the page's status or headers. Keep route guards that must redirect or set a status in a [request handler](/docs/middleware) such as `onRequest`, or in a blocking loader.
274
+
275
+
Note: when the client fetches a background loader for SPA navigation, there is no difference between a blocking and non-blocking loader — the SPA fetch is always async, and the page is already rendered. Errors are not surfaced directly, but redirects are followed automatically.
276
+
259
277
## Accessing `RequestEvent`
260
278
261
279
The loader function receives the same [`RequestEvent`](/docs/middleware#requestevent) as middleware. Use it to read params, headers, cookies, the URL, and so on.
0 commit comments