Suspense
<suspense> is the loading boundary for lazily loaded
components: it shows a fallback while they are in flight, then swaps in the
real thing.
<Suspense> is the same component.
<script>
const Chart = lazy('Chart')
const Map = lazy('Map')
</script>
<suspense fallback="<p>Loading...</p>">
<Chart />
<Map />
</suspense>
What it waits for is the tags this component claimed with
lazy() - see Lazy Loading.
A boundary around ordinary components has nothing to wait for and mounts them straight away.
How it behaves
- It waits for all the lazy children under it, then swaps once. The children mount through the ordinary pipeline: their scripts run, their bindings bind, their transitions play.
- If every lazy child is already loaded, the children render immediately and the fallback never appears - not even for a frame.
-
The nearest boundary owns a tag. A nested
<suspense>waits for its own children, and the outer one does not wait for them - two boundaries, two loading states.
<suspense fallback="<p>Loading the page...</p>">
<Header />
<!-- This one has its own boundary: the outer swap does not wait for it -->
<suspense fallback="<p>Loading the chart...</p>">
<Chart />
</suspense>
</suspense>
fallback
fallback is the only prop.
There is no delay, timeout, minDuration
or onLoad.
It is a literal HTML string, written straight into the boundary. The compiler never walks
it, so two shapes are build errors naming the fallback rather than quiet
disappointments: an expression, fallback="{count}", and a component tag,
fallback="<Spinner />".
Write plain markup. Changed in 0.1 - the braces used to ship as three characters of text and the component tag used to render nothing at all.
It Is Not for Data
<suspense> has no connection to query(): it does not react
to in-flight requests, and there is no "throw a promise" convention.
For loading states around data, read the query's own status:
<script>
const users = query("/api/users")
</script>
{users.loading && <p>Loading users...</p>}
{users.error && <p>Error: {users.error.message}</p>}
{users.data && users.data.map(u => <p>{u.name}</p>)}
See query() for the full status surface.