leptos-rs/leptos · error

You cannot use Suspend on an attribute outside Suspense

Error message

You cannot use Suspend on an attribute outside Suspense

What it means

Same family as the class variant: a Suspend-wrapped style attribute value is a future, and to_html can only render values available synchronously via now_or_never(). Outside a Suspense context there is nothing to await the future, so the style attribute panics during HTML string generation.

Source

Thrown at tachys/src/reactive_graph/style.rs:515

    style_reactive!(ArcStore, <V>, V, false, ArcStore<V>: Get<Value = V>);
    style_reactive!(ArcField, <V>, V, false, ArcField<V>: Get<Value = V>);
}
/*
impl<Fut> IntoStyle for Suspend<Fut>
where
    Fut: Clone + Future + Send + 'static,
    Fut::Output: IntoStyle,
{
    type AsyncOutput = Fut::Output;
    type State = Rc<RefCell<Option<<Fut::Output as IntoStyle>::State>>>;
    type Cloneable = Self;
    type CloneableOwned = Self;

    fn to_html(self, style: &mut String) {
        if let Some(inner) = self.inner.now_or_never() {
            inner.to_html(style);
        } else {
            panic!("You cannot use Suspend on an attribute outside Suspense");
        }
    }

    fn hydrate<const FROM_SERVER: bool>(
        self,
        el: &crate::renderer::types::Element,
    ) -> Self::State {
        let el = el.to_owned();
        let state = Rc::new(RefCell::new(None));
        reactive_graph::spawn_local_scoped({
            let state = Rc::clone(&state);
            async move {
                *state.borrow_mut() =
                    Some(self.inner.await.hydrate::<FROM_SERVER>(&el));
                self.subscriber.forward();
            }
        });
        state

View on GitHub (pinned to 32d20f6c9d)

Solutions

  1. Wrap the view containing the async style attribute in <Suspense>.
  2. Precompute the style value so it is synchronous at render time.
  3. Move the async fetch above the render (e.g. load data in a resource awaited within Suspense and pass the plain string down).

Example fix

// before
view! { <div style=Suspend(async move { theme_style().await }) /> }
// after
view! {
  <Suspense fallback=|| ()>
    <div style=Suspend(async move { theme_style().await }) />
  </Suspense>
}
Defensive patterns

Strategy: validation

Validate before calling

fn style_ready(s: &Suspend<impl Future>) -> bool { s.clone().inner_now_or_never().is_some() } // if false, you need Suspense

Type guard

fn is_plain_style(v: &str) -> bool { true } // favor &str/Read-derived styles over Suspend

Try / catch

std::panic::catch_unwind(|| to_html(&view)).map_err(|_| /* wrap in Suspense and retry */)

Prevention

When it happens

Trigger: Using Suspend(async expression) as a style attribute during SSR/dry rendering without a surrounding Suspense boundary.

Common situations: Async-computed inline styles (theme data fetched async) rendered on the server; converting a style: attr to an async source without adding Suspense.

Related errors


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