yewstack/yew · critical

use_linked_state requires a LinkProvider

Error message

use_linked_state requires a LinkProvider

What it means

use_linked_state<T> (packages/yew-link/src/lib.rs:512) reads LinkContextInner via use_context and panics with .expect when no ancestor rendered <LinkProvider>. The hook needs the provider's cache, endpoint and (on SSR) resolver to fetch or resolve linked state, so a missing provider is always a programming error, not a recoverable condition — the doc comment explicitly states it panics.

Source

Thrown at packages/yew-link/src/lib.rs:512

/// state is fetched from the provider's `endpoint` URL.
///
/// The hook suspends while the state is being resolved/fetched for the first
/// time. On [`refresh`](LinkedStateHandle::refresh), the previous value is
/// kept visible (stale-while-revalidate) and
/// [`is_refreshing`](LinkedStateHandle::is_refreshing) returns `true` until
/// the fresh data arrives.
///
/// Multiple components requesting the same `(T, Input)` concurrently share a
/// single in-flight request.
///
/// # Panics
///
/// Panics if there is no ancestor [`LinkProvider`] in the component tree.
#[hook]
pub fn use_linked_state<T: LinkedState>(input: T::Input) -> SuspensionResult<LinkedStateHandle<T>> {
    #[cfg(any(feature = "ssr", target_arch = "wasm32"))]
    let link_ctx =
        use_context::<LinkContextInner>().expect("use_linked_state requires a LinkProvider");

    #[cfg(any(feature = "ssr", target_arch = "wasm32"))]
    type Prepared<T, E> = Result<T, LinkError<E>>;

    #[cfg(feature = "ssr")]
    {
        let prepared = {
            let link_ctx = link_ctx.clone();
            yew::functional::use_prepared_state_with_suspension(
                input,
                move |input: Rc<T::Input>| {
                    let link_ctx = link_ctx.clone();
                    async move { link_ctx.resolve_local::<T>(&input).await }
                },
            )
        }?;

        let result: Rc<Prepared<T, T::Error>> =

View on GitHub (pinned to 0e4a05472f)

Solutions

  1. Mount <LinkProvider> above every component that calls use_linked_state — app root is the safest place: html! { <LinkProvider endpoint={AttrValue::from("/api/link")}> <App /> </LinkProvider> }
  2. In tests, wrap the component under test with a minimal LinkProvider
  3. On SSR additionally pass a resolver prop (see the resolve_local panic) so the provider is fully configured

Example fix

// before
html! { <ProfilePage /> }  // ProfilePage calls use_linked_state

// after
html! {
    <LinkProvider endpoint={AttrValue::from("/api/link")}>
        <ProfilePage />
    </LinkProvider>
}
Defensive patterns

Strategy: validation

Validate before calling

// render every use_linked_state consumer inside a LinkProvider
// html! { <LinkProvider endpoint={AttrValue::from("/api/link")}> <App /> </LinkProvider> }

Type guard

// debug-only check you can drop into a component before calling the hook
// #[cfg(debug_assertions)]
// if yew::functional::use_context::<crate::LinkContextInner>().is_none() {
//     panic!("use_linked_state requires a LinkProvider ancestor");
// }

Prevention

When it happens

Trigger: Calling use_linked_state in a component mounted outside a <LinkProvider> subtree; putting the provider next to instead of above the consuming component; rendering the hook during SSR with no provider at all in the server tree.

Common situations: Adding a linked-state hook to a new page while the provider only wraps other routes; reorganizing the app layout so the root provider is dropped; testing a component in isolation without wrapping it in a provider.

Related errors


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