github leptos-rs/leptos v0.8.0

latest releases: v0.8.8, v0.8.6, v0.8.5...
4 months ago

*Changelog relative to 0.7.8. *

0.8 has been planned for a while, primarily to accommodate small changes that arose during the course of testing and adopting 0.7, most of which are technically semver-breaking but should not meaningfully affect user code. I think it's a significant QOL and user DX upgrade and I'm excited to properly release it.

Noteworthy features:

  • Axum 0.8 support. (This alone required a major version bump, as we reexport some Axum types.) (thanks to @sabify for the migration work here)
  • Significant improvements to compile times when using --cfg=erase_components, which is useful as a dev-mode optimization (thanks to @zakstucke) This is the default setting for cargo-leptos with its latest release, and can be set up manually for use with Trunk. (See docs here.)
  • Support for the new islands-router features that allow a client-side routing experience while using islands (see the islands_router example) (this one was me)
  • Improved server function error handling by allowing you to use any type that implements FromServerFnError rather than being constrained to use ServerFnError (see #3274). (Note: This will require changes if you're using a custom error type, but should be a better experience.) (thanks to @ryo33)
  • Support for creating WebSockets via server fns (thanks to @ealmloff)
  • Changes to make custom errors significantly more ergonomic when using server functions
  • LocalResource no longer exposes a SendWrapper in the API for the types it returns. (Breaking change: this will require removing some .as_deref() and so on when using LocalResource, but ends up with a much better API.)
  • Significantly improved DX/bugfixes for thread-local Actions.

As you can see this was a real team effort and, as always, I'm grateful for the contributions of everyone named above, and all those who made commits below.

WebSocket Example

The WebSocket support is particularly exciting, as it allows you to call server functions using the default Rust Stream trait from the futures crate, and have those streams send messages over websockets without you needing to know anything about that process. The API landed in a place that feels like a great extension of the "server function" abstraction in which you can make HTTP requests as if they were ordinary async calls. The websocket stuff doesn't integrate directly with Resources/SSR (which make more sense for one-shot things) but is really easy to use:

use server_fn::{codec::JsonEncoding, BoxedStream, ServerFnError, Websocket};

// The websocket protocol can be used on any server function that accepts and returns a [`BoxedStream`]
// with items that can be encoded by the input and output encoding generics.
//
// In this case, the input and output encodings are [`Json`] and [`Json`], respectively which requires
// the items to implement [`Serialize`] and [`Deserialize`].
#[server(protocol = Websocket<JsonEncoding, JsonEncoding>)]
async fn echo_websocket(
    input: BoxedStream<String, ServerFnError>,
) -> Result<BoxedStream<String, ServerFnError>, ServerFnError> {
    use futures::channel::mpsc;
    use futures::{SinkExt, StreamExt};
    let mut input = input; // FIXME :-) server fn fields should pass mut through to destructure

    // create a channel of outgoing websocket messages 
    // we'll return rx, so sending a message to tx will send a message to the client via the websocket
    let (mut tx, rx) = mpsc::channel(1);

    // spawn a task to listen to the input stream of messages coming in over the websocket 
    tokio::spawn(async move {
        while let Some(msg) = input.next().await {
            // do some work on each message, and then send our responses 
            tx.send(msg.map(|msg| msg.to_ascii_uppercase())).await;
        }
    });

    Ok(rx.into())
}

#[component]
pub fn App() -> impl IntoView {
    use futures::channel::mpsc;
    use futures::StreamExt;
    let (mut tx, rx) = mpsc::channel(1);
    let latest = RwSignal::new(None);

    // we'll only listen for websocket messages on the client
    if cfg!(feature = "hydrate") {
        spawn_local(async move {
            match echo_websocket(rx.into()).await {
                Ok(mut messages) => {
                    while let Some(msg) = messages.next().await {
                        latest.set(Some(msg));
                    }
                }
                Err(e) => leptos::logging::warn!("{e}"),
            }
        });
    }

    view! {
        <input type="text" on:input:target=move |ev| {
            tx.try_send(Ok(ev.target().value()));
        }/>
        <p>{latest}</p>
    }
}

What's Changed

  • Allow any type that implements FromServerFnError as a replacement of the ServerFnError in server_fn by @ryo33 in #3274
  • impl Dispose for Callback types and add try_run to the Callable trait by @basro in #3371
  • feat(breaking): allow make PossibleRouteMatch dyn-safe by @gbj in #3421
  • chore: upgrade axum to v0.8 by @sabify in #3439
  • feat: Add more options for generating server fn routes by @spencewenski in #3438
  • change: allow IntoFuture for Suspend::new() (closes #3509) by @gbj in #3532
  • fix: remove Default impl for LeptosOptions and ConfFile by @chrisp60 in #3522
  • Fixing closing brace by @thestarmaker in #3539
  • AddAnyAttr for AnyView for non-erased by @zakstucke in #3553
  • "Update axum paths to 0.8 syntax" by @zakstucke in #3555
  • Keep AddAnyAttr logic contained by @gbj in #3562
  • fix: Actix stream error handling with 0.8 error types by @gbj in #3574
  • RenderHtml::into_owned by @zakstucke in #3580
  • Binary size wins by @zakstucke in #3566
  • Internally erase html elements by @zakstucke in #3614
  • feat: support Option<_> in style: (closes #3568) by @gbj in #3618
  • Erased routing, codegen opts by @zakstucke in #3623
  • change: remove unused Result alias by @gbj in #3543
  • feat: support IntoSplitSignal for (Signal<T>, SignalSetter<T>) (closes #3634) by @gbj in #3643
  • fix: avoid hydration issues with HashedStylesheet (closes #3633) by @gbj in #3654
  • Islands router by @gbj in #3502
  • Erased mode in CI by @zakstucke in #3640
  • fix: tweak bounds on For for backwards-compat by @gbj in #3663
  • Implement several into traits for store fields (0.8) by @mahdi739 in #3658
  • Implement IntoClass for store fields by @mahdi739 in #3670
  • fix: Ensure reactive functions passed to TextProp are kept reactive (closes: #3689) by @mahdi739 in #3690
  • Add websocket support for server functions by @ealmloff in #3656
  • fix: broken type inference for Action::new_unsync (closes #3328) by @gbj in #3705
  • feat(reactive_stores): Replace AsRef bound of StoreFieldIterator blanket impl with Len bound by @DanikVitek in #3701
  • refactor: make shell parameter in file_and_error_handler* generic by @tversteeg in #3711
  • view!{} macro optimisation: don't wrap string types in closures when passing to ToChildren by @zakstucke in #3716
  • Remove SendWrapper from the external interface of LocalResource by @zakstucke in #3715
  • More flexible server fn macro api by @ealmloff in #3725
  • fix(CI): switch to stable in semver for most compatibility by @sabify in #3737
  • fix(CI): cancel in-group inflight and pending jobs on new pushes in pull requests by @sabify in #3739
  • fix(CI): free-up disk, properly gate nightly feature and pre-install deps by @sabify in #3735
  • ArcLocalResource fix (0.8) by @zakstucke in #3741
  • ArcLocalResource fix (0.7) by @zakstucke in #3740
  • fix(CI): cleanup the directory no matter of the results by @sabify in #3743
  • fix(CI): sermver job name by @sabify in #3748
  • chore: no need to filter out "nightly" feature as of #3735 by @sabify in #3747
  • fix: use signals rather than Action::new_local() (closes #3746) by @gbj in #3749
  • feat: switch extract() helper to use ServerFnErrorErr (closes #3745) by @ilyvion in #3750
  • docs(Effect::watch): refer to dependency_fn and handler args by @jmevel in #3731
  • Leptos 0.8 by @gbj in #3529
  • chore: ensure WASM target is installed for examples with provided rust-toolchain.toml (closes #3717) by @gbj in #3752
  • Make trailing comma optional for either macro by @NCura in #3736
  • chore: add SignalSetter to prelude (closes #3547) by @gbj in #3753
  • fix: properly feature gating ui macro tests (Closes #3742) by @sabify in #3756
  • fix(CI): optimize CI workflow by @sabify in #3758
  • fix(CI): remove duplicate semver ci, #3758 follow-up by @sabify in #3764
  • fix(CI): install deps only if needed, speeds up CI by @sabify in #3768
  • fix: support IntoFragment for single element (closes #3757) by @gbj in #3759
  • fix: clippy errors by @sabify in #3772
  • fix(CI): install deno only if needed, #3768 follow-up by @sabify in #3773
  • fix(CI): remove caching by @sabify in #3776
  • fix(CI): conditional executions of only changed examples by @sabify in #3777
  • Make docs match reality by @ilyvion in #3775
  • fix(CI): toolchain will be determined and test against by CI by @sabify in #3778
  • Reduce use local signals for Action::new_local and similar primitives by @gbj in #3762
  • Tweaks to MaybeSendWrapperOption<_> by @gbj in #3781
  • fix: correctly handle optional parameters in ParentRoute by @gbj in #3784
  • fix: router example build process by @sabify in #3779
  • fix(CI): run only the exact examples on the only examples change by @sabify in #3782
  • Re-export the codee crate by @zakstucke in #3761
  • fix: allow repeated class= for all tuples, not only static ones (closes #3794) by @gbj in #3801
  • Fix Store notification order for nested keyed fields by @gbj in #3799
  • Improved handling of <Title/> by @gbj in #3793
  • derive_local for ArcSignal<T, LocalStorage> by @zakstucke in #3798
  • fix: portal example by @sabify in #3785
  • feat: add support for more HTTP methods in server fn codecs by @ChosunOne in #3797
  • Store test fixes by @gbj in #3803
  • Add track_caller to store field methods by @jvdwrf in #3805
  • fix: allow custom status codes or redirects for route fallbacks by @gbj in #3808
  • fix: Move several Into* trait impls for store fields out of stable module for wider use by @mahdi739 in #3807
  • fix: remove SendOption from public API of actions by @gbj in #3812
  • feat: support aliased Result return types for server_fn by @ifiokjr in #3755
  • Migrate from Tailwind 3 to Tailwind 4 for the axum example. by @pico-bolero in #3804
  • pass key reference to Selector::selected by @flisky in #3694
  • fix: correctly establish root ownership for static site generation (closes #3822) by @gbj in #3824
  • fix: do not match static segment with last character missing before slash (closes #3817) by @gbj in #3823
  • fix: prevent race condition in executor initialization + docs, optimization and tests by @sabify in #3802
  • chore: missing Copy/Clone impls for MappedSignal by @gbj in #3827
  • Revert "Remove getrandom (#3589)" by @ilyvion in #3830
  • Introducing cargo all-features clippy|nextest part of build process by @sabify in #3767
  • feat: allow using different error types for req/resp with WebSockets, closes #3724 by @myypo in #3766
  • feat: enhancing server_fn errors by @sabify in #3811
  • fix: call additional_context after providing other server context in all cases by @gbj in #3841
  • fix: correctly decode base64-encoded server action error messages stored in URL by @gbj in #3842
  • fix: don't try to move keyed elements within the DOM if they're not yet mounted (closes #3844) by @gbj in #3846
  • chore(nightly): update proc-macro span file name method name by @gbj in #3852
  • fix: reactive_graph keymap impl and clippy warnings by @sabify in #3843
  • chore: ran cargo outdated. by @martinfrances107 in #3722
  • fix: close Actix websocket stream when browser disconnects (closes #3865) by @gbj in #3866
  • Error boundary fixes by @gbj in #3870
  • Forward lint attributes used with #[component] macro by @sathish-pv in #3864
  • Complete the migration of examples to Tailwind 4 by @nnmm in #3861
  • fix: Use stabilized ClipboardEvent by @feathecutie in #3849
  • Added header generation method to BrowserResponse by @rakshith-ravi in #3873
  • Prevent ScopedFuture stopping owner cleanup by @zakstucke in #3863
  • feat: enhancing ByteStream error handling by @sabify in #3869
  • fix: send/receive websocket data by @sabify in #3848
  • feat(examples): add WebSocket example by @sabify in #3853
  • chore: put TextProp in the prelude (closes #3877) by @huuff in #3879
  • fix(examples): websocket example tests fail on latency by @sabify in #3880
  • fix: correctly calculate starting index for first new key (closes #3828) by @gbj in #3878
  • fix: remove event listeners from Suspense fallback during SSR (closes #3871) by @gbj in #3882
  • fix(examples): broken favicons in hackernews examples (closes #3890) by @gbj in #3891
  • docs: add note about file hashing in Stylesheet docs by @gbj in #3898
  • fix(examples): incorrect routes in hackernews example (closes #3892) by @nickburlett in #3894
  • Fix some island-routing issues by @gbj in #3901
  • fix: prevent sibling context leakage in islands (closes #3902) by @gbj in #3903
  • fix: correct hydration for elements after island children (closes #3904) by @gbj in #3905
  • Fix leptos_debuginfo by @zakstucke in #3899
  • fix(examples): websocket tests fail (occasionally) second attemp by @sabify in #3910
  • feat: impl From<MappedSignal<T>> for Signal<T> (closes #3889) by @gbj in #3897

New Contributors

Full Changelog: v0.7.8...v0.8.0

Don't miss a new leptos release

NewReleases is sending notifications on new releases.