yewstack/yew · error

To suspend rendering, a <Suspense /> component is required.

Error message

To suspend rendering, a <Suspense /> component is required.

What it means

When a component's render yields a suspension (use_prepared_state, use_transitive_state, or anything returning SuspensionResult), Yew walks up the scope tree via find_parent_scope::<BaseSuspense>() to register the suspension with the nearest Suspense boundary. This expect fires when no <Suspense> component exists above the suspending component: suspending is only legal inside a boundary that can display a fallback while the component waits.

Source

Thrown at packages/yew/src/html/component/lifecycle.rs:461

    fn suspend(&mut self, shared_state: &Shared<Option<ComponentState>>, suspension: Suspension) {
        // Currently suspended, we re-use previous root node and send
        // suspension to parent element.

        if suspension.resumed() {
            // schedule a render immediately if suspension is resumed.
            scheduler::push_component_render(
                self.comp_id,
                Box::new(RenderRunner {
                    state: shared_state.clone(),
                }),
            );
        } else {
            // We schedule a render after current suspension is resumed.
            let comp_scope = self.inner.any_scope();

            let suspense_scope = comp_scope
                .find_parent_scope::<BaseSuspense>()
                .expect("To suspend rendering, a <Suspense /> component is required.");

            let comp_id = self.comp_id;
            let shared_state = shared_state.clone();
            suspension.listen(Callback::from(move |_| {
                scheduler::push_component_render(
                    comp_id,
                    Box::new(RenderRunner {
                        state: shared_state.clone(),
                    }),
                );
                scheduler::start();
            }));

            if let Some(ref last_suspension) = self.suspension {
                if &suspension != last_suspension {
                    // We remove previous suspension from the suspense.
                    BaseSuspense::resume(&suspense_scope, last_suspension.clone());
                }

View on GitHub (pinned to 0e4a05472f)

Solutions

  1. Wrap the suspending component (usually the page/route component) in <Suspense fallback={...}>
  2. Ensure the boundary exists from the very first render, not added in a later pass
  3. If no fallback UI is wanted, replace the suspense-based hook with plain use_state plus an effect-driven fetch
  4. Check every consumer of the hook, including components rendered through routers or portals

Example fix

// before: use_prepared_state inside UserProfile, no boundary above it
html! { <UserProfile /> }

// after
html! {
    <Suspense fallback={html! { <p>{ "Loading..." }</p> }}>
        <UserProfile />
    </Suspense>
}
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: A component calling use_prepared_state/use_transitive_state (or otherwise producing Err(Suspension) from render) that is mounted without a <Suspense> ancestor - e.g. directly at the app root, on a router page that forgot the boundary, or where the Suspense is behind a flag that is false on the first render.

Common situations: Adding a data-fetching suspense hook during CSR-only development where no boundary was ever needed; restructuring the tree so the boundary ends up below the suspending component; conditionally rendering the <Suspense> itself.

Related errors


AI-assisted analysis of yewstack/yew@0e4a05472f (2026-08-22). Data as JSON: /api/errors/c5528f7cee49ef49. Report an issue: GitHub.