leptos-rs/leptos · error

could not deserialize props

Error message

could not deserialize props

What it means

For islands with extra props, the macro generates hydration code that reads the serialized props from the element's data-props dataset attribute and deserializes it with serde_json::from_str::<PropsSerialized>(...).ok(), then .expect("could not deserialize props"). This panics on the client during hydration when the dataset attribute is missing or its JSON does not match the expected serialized props struct.

Source

Thrown at leptos_macro/src/component.rs:530

                quote! {{
                    #destructure
                    let mut props = #props_name::builder()
                        #prop_builders
                        #children
                        .build();

                    #optional_props

                    props
                }}
            } else {
                quote! {}
            };
            let deserialize_island_props = if is_island_with_other_props {
                quote! {
                    let props = el.dataset().get(::leptos::wasm_bindgen::intern("props"))
                        .and_then(|data| ::leptos::serde_json::from_str::<#props_serialized_name>(&data).ok())
                        .expect("could not deserialize props");
                }
            } else {
                quote! {}
            };

            let hydrate_fn_name = hydrate_fn_name.as_ref().unwrap();

            let hydrate_fn_inner = quote! {
                #deserialize_island_props
                let island = #name(#island_props);
                let state = island.hydrate_from_position::<true>(&el, ::leptos::tachys::view::Position::Current);
                // TODO better cleanup
                std::mem::forget(state);
            };
            if *is_lazy {
                let outer_name =
                    Ident::new(&format!("{name}_loader"), name.span());

View on GitHub (pinned to 32d20f6c9d)

Solutions

  1. Ensure server and client use the exact same build of the app/crate so the serialized props struct layout matches
  2. Verify the island element actually carries the data-props attribute in the served HTML
  3. Make prop types symmetric in Serialize/Deserialize (same fields, no skip asymmetry, string-serializable map keys)
  4. Upgrade or align leptos/leptos_macro versions between the SSR binary and the client WASM
  5. If hydration is optional, guard by re-creating the island client-side instead of panicking

Example fix

// before (server: old struct) / client expects new field
struct Props { a: u32 }          // server serialized only {"a":1}
struct Props { a: u32, b: String } // client deserialize fails -> panic
// after
// rebuild both server and client from the same commit so both use struct Props { a: u32, b: String }
Defensive patterns

Strategy: try-catch

Validate before calling

// Client-side pre-check before hydration:
let ok = el
    .dataset()
    .get(wasm_bindgen::intern("props"))
    .map(|d| serde_json::from_str::<PropsSerialized>(&d).is_ok())
    .unwrap_or(false);
if !ok { /* re-render island client-side instead of hydrating */ }

Type guard

fn has_valid_props(el: &web_sys::Element) -> bool {
    el.dataset()
        .get(wasm_bindgen::intern("props"))
        .and_then(|d| serde_json::from_str::<PropsSerialized>(&d).ok())
        .is_some()
}

Try / catch

// Hydration itself panics via expect; wrap the hydrate call:
let result = std::panic::catch_unwind(|| hydrate_island(el));
if result.is_err() {
    // fall back to client-side (CSR) mount of the island
}

Prevention

When it happens

Trigger: Hydrating an island whose data-props attribute was not emitted server-side; the server and client define the island with different props (version skew); the JSON was truncated/mutated; or the props type's Deserialize does not match its Serialize output (e.g. untagged enums, skipped fields).

Common situations: Server and client built from different crate versions so the props struct shape changed; hydration of islands rendered by a different framework path; WASI/CSR builds where dataset API returns None; manually edited DOM before hydrate.

Related errors


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