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
docs: Loading on takes effect only when nothing outside waits on the change
Teach on as a region's offer to leave the update: the update accepts
only when that region was the only thing waiting. Show the heading-
outside-the-boundary case where on has no visible effect, restate the
rule for sibling panels, ask the organizing question about the update
rather than the refetch, and add the second cause to the Boundaries
common problem, the debugging guide, the data-fetching guide, and the
glossary.
Co-authored-by: Cursor <cursoragent@cursor.com>
@@ -354,15 +354,41 @@ The `on` prop names the value whose change should make the boundary eligible to
354
354
</Loading>
355
355
```
356
356
357
-
When `accountId()` changes and the new account is not ready, this boundary shows its fallback and handles the wait itself instead of holding the update.
358
-
The panel no longer keeps the write from committing, so controls outside the boundary see the new id as soon as nothing else in the update is holding it, and the panel shows its skeleton until the new account arrives.
357
+
When `accountId()` changes and the new account is not ready, the boundary offers to take its region out of the update: it will show the skeleton and wait on its own, so the rest of the update can commit without it.
358
+
If the panel was the only thing waiting, the update accepts.
359
+
Controls outside the boundary see the new id at once, the panel shows its skeleton, and the content arrives when the account does.
359
360
Pending work caused by other values, such as a refresh of the same account, leaves the content in place as usual.
360
361
362
+
The update accepts the offer only when the region was the only thing waiting.
363
+
An update commits when everything it changed is ready, and `on` does not change that; it only lets one region stop being a reason to wait.
364
+
When something outside the boundary is also waiting on the new account, the update stays held, and the region waits inside it with everything else, old content in place and no skeleton:
365
+
366
+
```tsx
367
+
// account is createMemo(() => fetchAccount(accountId()))
368
+
369
+
// Avoid: the heading reads the same account outside the boundary
In the `Avoid` version, picking another account looks like nothing happened until the account loads, then the heading and the details change together; the `on` had no visible effect because the heading kept the update waiting.
383
+
So the rule for `on` is that nothing outside the boundary may be waiting on the same change.
384
+
Enclose every reader of the new subject's data, and when that is not possible, because a breadcrumb, a title, or a sibling panel shows the same subject, drop `on` and acknowledge the wait with `isPending` and `latest` instead.
385
+
361
386
`on` is a value, not an accessor.
362
387
The boundary compares it across updates, so pass `accountId()` rather than `accountId`.
363
388
364
-
Put `on` on each panel that should show its own placeholder.
365
-
A panel without it reads the shared input the normal way and still holds the write, so in the dashboard above, one panel with `on` and two without still waits for the two.
389
+
The same rule decides the dashboard above.
390
+
Give each panel its own boundary with `on={period()}` and the period write commits at once: the selector flips, every panel shows its skeleton, and each panel's content arrives as its own request lands.
391
+
Give `on` to one panel only and the other two, reading the shared input the normal way, still hold the write; the page waits for the slowest request, and the panel with `on` shows no skeleton either.
366
392
[Boundaries](/concepts/boundaries) covers placement and how `Reveal` orders several boundaries.
367
393
368
394
:::deep-dive[A placeholder value instead of a fallback: loadingValue]
@@ -449,7 +475,7 @@ Read every reactive input at the top of the function, before the first `await`.
449
475
- Requests are ordered by data dependency, not by component nesting; a waterfall exists only when one request needs another's response.
450
476
- After a value has settled, a change to its input is held: the current screen stays visible and everything commits together when the new value lands.
451
477
- Acknowledge a held update with `isPending` on the content and `latest` on the control the user touched.
452
-
- Put `on={key}` on a `Loading` boundary whose subject changed and should show a placeholder instead of the old content.
478
+
- Put `on={key}` on a `Loading` boundary whose subject changed and should show a placeholder instead of the old content; it takes effect only when nothing outside the boundary is waiting on the same change.
453
479
- Read every reactive input before the first `await`.
Copy file name to clipboardExpand all lines: src/routes/(2)concepts/(4)boundaries.mdx
+9Lines changed: 9 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -90,6 +90,10 @@ The `on` prop names the value whose change should make an initialized boundary s
90
90
Without it, choosing a new product keeps the old product on screen while the new one loads; with `on={productId()}`, the boundary compares the id across updates and shows the skeleton when the subject changed.
91
91
Pending work caused by anything else, such as a refresh of the same product, leaves the content in place.
92
92
93
+
`on` lets the region stop being a reason for the update to wait; it does not make the update commit.
94
+
When something outside the boundary also waits on the new product, such as a heading that reads the same product or a sibling region without `on`, the update stays held and the boundary shows no skeleton.
95
+
Enclose every reader of the new subject, or give each sibling its own boundary with `on`; the [Async reactivity](/concepts/async-reactivity#show-a-placeholder-again-loading-on) page shows both cases.
96
+
93
97
:::pitfall[Passing the accessor to on instead of its value]
94
98
`on` is compared with `!==` across updates, so pass a primitive; join several inputs into one string rather than passing an array.
95
99
@@ -255,6 +259,10 @@ The request can stay where it was; the [Async reactivity](/concepts/async-reacti
255
259
That is the default: after a first answer, the boundary keeps its content and the update is held.
256
260
Add `on={id()}` to the boundary when a change of subject should show the fallback, and pass the value rather than the accessor.
257
261
262
+
If `on` is already there and the old item still stays, something outside the boundary is waiting on the same change: a title reading the same item, or a sibling boundary without `on`.
263
+
The update cannot commit until that reader is ready, so the fallback never shows.
264
+
Move the boundary out to enclose the other reader, give the sibling its own `on`, or drop `on` and show the wait with `isPending`.
265
+
258
266
### Retry shows the same error
259
267
260
268
`reset` re-runs the failed sources with the same inputs.
@@ -271,6 +279,7 @@ Give the group `order="natural"`, wrap the independent regions in a nested `<Rev
271
279
- Put `Loading` around the smallest region its fallback should replace, and keep the controls the user needs outside it.
272
280
- Put `Errored` around the smallest region that can fail and recover as a unit; both boundaries can wrap the same region.
273
281
- After a first answer, `Loading` keeps its content during updates; add `on={key()}` when a changed subject should show the fallback again, and pass a value, not an accessor.
282
+
It takes effect only when nothing outside the boundary is waiting on the same change.
274
283
- An errored region recovers when an input changes or a refresh succeeds; `reset` is for when nothing upstream will change.
275
284
- An error thrown by a fallback needs a boundary above it.
276
285
- Use `Reveal` to control the order sibling regions appear in; it changes timing, not what each boundary observes.
Copy file name to clipboardExpand all lines: src/routes/(5)guides/(10)debugging-reactivity.mdx
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -263,6 +263,7 @@ The repair is always to add feedback, never to remove the hold:
263
263
264
264
A `Loading` boundary that has not shown content yet, or whose `on` value changed, is a different situation: the read shows the fallback instead of entering a hold, so there is no `SILENT_HOLD` to report.
265
265
A boundary that has already revealed holds like everything else.
266
+
So does a boundary with `on` when something outside it waits on the same change: the update is held regardless, the fallback never shows, and the report names the source the click waited on; look for a reader of that source outside the boundary.
266
267
A hold that was acknowledged but still ran long is reported as `[LONG_HOLD]` from 500ms of waiting, a warning from 1000ms, with the suggestion to add a `Loading on={...}` boundary; the thresholds are `attribution.enable({ longHolds: { infoMs, warnMs } })`.
267
268
Shorter acknowledged holds are recorded as `late` in the feedback tables so the cost is visible without blaming code that waited correctly.
Copy file name to clipboardExpand all lines: src/routes/(5)guides/(6)data-fetching-patterns.mdx
+3-1Lines changed: 3 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -133,7 +133,9 @@ If the page should not reveal out of order, wrap the boundaries in [`Reveal`](/r
133
133
134
134
When `props.id` changes, all three requests start again and the update is held until all three have answered; the page then swaps as one.
135
135
That is usually what you want for a detail page, where the header and the reviews must describe the same product.
136
-
If a slow section should not delay the others on a subject change, give it `on={props.id}` and it will show its skeleton instead of holding the rest.
136
+
If a slow section should not delay the others on a subject change, give it `on={props.id}`: it stops holding the update, so the page swaps as soon as the other two have answered, and the slow section shows its skeleton from that swap until its own data lands.
137
+
Give all three `on` and the page swaps on the click itself, each section on its skeleton until its data arrives.
138
+
The [Async reactivity](/concepts/async-reactivity#show-a-placeholder-again-loading-on) page explains why `on` only helps when nothing outside its boundary is waiting on the same change.
Copy file name to clipboardExpand all lines: src/routes/(7)glossary.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -284,7 +284,7 @@ See [Server and client boundaries](/concepts/rendering-and-ssr#server-and-client
284
284
### Loading boundary
285
285
286
286
`Loading` renders its fallback while an async value read in its subtree has not produced a first answer.
287
-
Once it has shown content it keeps that content during later updates; the `on` prop names the value whose change should bring the fallback back.
287
+
Once it has shown content it keeps that content during later updates; the `on` prop names the value whose change should bring the fallback back, which happens only when nothing outside the boundary is waiting on the same change.
0 commit comments