leptos-rs/leptos · error

Reading from a LocalResource outside Suspense in `ssr` mode

Error message

Reading from a LocalResource outside Suspense in `ssr` mode will cause the response to hang, because LocalResources are always pending on the server.

What it means

In ssr mode a LocalResource never resolves on the server (it is always pending, since its data is client-local). Reading it via into_future outside a Suspense context means there is no LocalResourceNotifier to defer serialization, so leptos panics instead of hanging the HTTP response forever.

Source

Thrown at leptos_server/src/local_resource.rs:147

    /// left to handle the `None` and `Err(_)` states.
    #[track_caller]
    pub fn and_then<U>(&self, f: impl FnOnce(&T) -> U) -> Option<Result<U, E>> {
        self.map(|data| data.as_ref().map(f).map_err(|e| e.clone()))
    }
}

impl<T> IntoFuture for ArcLocalResource<T>
where
    T: Clone + 'static,
{
    type Output = T;
    type IntoFuture = AsyncDerivedFuture<T>;

    fn into_future(self) -> Self::IntoFuture {
        if let Some(mut notifier) = use_context::<LocalResourceNotifier>() {
            notifier.notify();
        } else if cfg!(feature = "ssr") {
            panic!(
                "Reading from a LocalResource outside Suspense in `ssr` mode \
                 will cause the response to hang, because LocalResources are \
                 always pending on the server."
            );
        }
        self.data.into_future()
    }
}

impl<T> DefinedAt for ArcLocalResource<T> {
    fn defined_at(&self) -> Option<&'static Location<'static>> {
        #[cfg(any(debug_assertions, leptos_debuginfo))]
        {
            Some(self.defined_at)
        }
        #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
        {
            None

View on GitHub (pinned to 32d20f6c9d)

Solutions

  1. Wrap the reading component/view in <Suspense> so the LocalResource can register its notifier.
  2. If the data must be available during SSR, use a regular Resource (server-fn backed) instead of LocalResource.
  3. Only read LocalResources in client/hydration-only code paths.

Example fix

// before
let res = use_local_resource(...);
view! { <p>{move || res.get().map(|d| d.text())}</p> } // panics on ssr
// after
view! {
    <Suspense fallback=|| view! { <p>Loading...</p> }>
        <p>{move || res.get().map(|d| d.text())}</p>
    </Suspense>
}
Defensive patterns

Strategy: try-catch

Validate before calling

#[cfg(feature = "ssr")]
fn local_resource_ssr_safe() -> bool {
    use leptos::reactive_graph::LocalResourceNotifier;
    // true only when rendering inside Suspense (notifier present)
    leptos::prelude::use_context::<LocalResourceNotifier>().is_some()
}

Try / catch

// Panics cannot be caught idiomatically here; restructure instead.
// catch_unwind is a last resort around the whole render:
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| render_app()));

Prevention

When it happens

Trigger: Calling `.get()`/`.await` (into_future) on a LocalResource during SSR when the read happens outside any <Suspense> component, so use_context::<LocalResourceNotifier>() returns None and the ssr feature is enabled.

Common situations: Awaiting a LocalResource in a component rendered on the server without wrapping it in <Suspense>; refactoring a Resource (server-friendly) into a LocalResource (client-only) and forgetting the SSR implications; reading the resource at the top level of a view during server render.

Related errors


AI-assisted analysis of leptos-rs/leptos@32d20f6c9d (2026-09-01). Data as JSON: /api/errors/84b8b29d40f2901e. Report an issue: GitHub.