Skip to content

Releases: leptos-rs/leptos

v0.5.7

19 Jan 18:09
@gbj gbj
Compare
Choose a tag to compare

This has been a rocky week for releases. While finalizing the work on 0.6.0, I intended to publish a final 0.5.5 that would contain all the remaining changes to the 0.5 series. Unfortunately, this release contained two serious issues:

  1. breaking doc comments on server functions (fixed and rereleased in 0.5.6)
  2. The trailing_slash changes to the router in 0.5.5 broke the current routing behavior in several situations (see #2203 for details)

Because some (many?) existing 0.5 apps would break with this new feature under 0.5 β€” and worse, because they would compile and then either panic or display the wrong page β€” I decide to revert the feature immediately. This meant a semver-breaking change between 0.5.6 and 0.5.7, so I have yanked 0.5.5 and 0.5.6 of all the crates.

We've added some appropriate regression tests for those issues. Apologies to anyone whose work was affected by this!

v0.6.0-beta

17 Jan 13:10
@gbj gbj
Compare
Choose a tag to compare
v0.6.0-beta Pre-release
Pre-release

This is a beta release for our new server functions rewrite and Axum 0.7 support.

This should be a relatively feature-rich release, with limited breaking changes.

I'm interested in gathering feedback in the discussion associated with this release.

Migration

Actix

  • You can remove any explicit .handle_server_fns() call in your main.rs, as server functions are now handled in .leptos_routes()
  • The current extract function has been removed, and replaced with a new extract that has the same API as the current extractor. I think this API is strictly better, but please share feedback if you disagree.

Axum

  • This release supports Axum 0.7, so you'll need to migrate from Axum 0.6. The easiest way to do this is probably to consult the diff on one of the examples.
  • You can remove any explicit .handle_server_fns() call in your main.rs, as server functions are now handled in .leptos_routes()
  • The current extract function has been removed, and replaced with a new extract that has the same API as the current extractor. I think this API is strictly better, but please share feedback if you disagree.
  • RequestParts has been removed, as http::request::Parts now implements Clone: any use_context::<RequestParts>() should be updated to use Parts directly instead.

Features

A rewritten server function system that is backwards-compatible, but reduces binary size and increases flexibility, specifically by allowing

  • automatic setup of server fn handlers with .leptos_routes() from the integrations
  • a variety of additional built-in encodings (rkyv, multipart forms/file uploads) in addition to the current set (GET URL, POST URL, CBOR, Rkyv) (#1868, #1989)
  • support for streaming responses from server functions (#1284)
  • ability to create custom user encodings for input and output by deriving IntoReq, FromReq, IntoRes, and/or FromRes traits
  • ability to mix and match encodings easily: This server function should be a JSON POST request and a ByteStream response, this one should be GET URL request and an rkyv response, etc.; any combination of the encodings above is supported
  • custom error types (#1657)
  • a #[middleware] macro to add per-server-function middleware from the Tower or Actix ecosystems (#1461)

Note: The additional included encodings (serde_lite, rkyv, multipart form data) are all enabled by additive features on the server_fn crate. If you want to use them you can just add that crate as a dependency and enable the required features.

Example: You can find a comprehensive example of these new features in the new server_fns_axum example.

v0.5.6

17 Jan 01:06
Compare
Choose a tag to compare

Fix issue were doc comments on server functions caused an error. Sorry bout that!

What's Changed

  • fix: doc comments breaking server functions (closes #2190) by @gbj in #2191

Full Changelog: v0.5.5...v0.5.6

v0.5.5

15 Jan 23:35
041b86e
Compare
Choose a tag to compare

This is a (mostly) bug fix and documentation improvements release. If you're interested in the full list, check out the full What's Changed list below.

What's New

Trailing Slash Changes

@NfNitLoop discovered that by default the leptos_router strips trailing slashes from routes, which might not be what users expect. By default, leptos_router drops the trailing slash on /foo/. He's added a new optional setting on Router that could allow you to keep the slashes and have routes for both /foo and /foo/ or redirect from /foo/ to /foo. Check out the docs for details: https://docs.rs/leptos_router/0.5.5/leptos_router/enum.TrailingSlash.html
Thanks @NfNitLoop!

Thanks to all our new and existing contributors, the 0.6 alpha will be coming out soon with fancy new server functions, so don't go away!

What's Changed

New Contributors

Full Changelog: v0.5.4...v0.5.5

v0.5.4

28 Nov 23:55
@gbj gbj
Compare
Choose a tag to compare

v0.5.4

v0.5.3 inadvertently introduced a regression in the <A/> tag that broke [aria-current=page]. Here's 0.5.4 with a fix!

0.5.3 release notes below:


Along with the usual set of bugfixes and small improvements, there are two noteworthy additions in this release.

Improved rust-analyzer support in #[component] macro body

Good LSP support for proc macros is hard, because proc macros depend on parsing a stream of input into a valid Rust syntax tree, but while typing you are constantly creating a stream of new, invalid trees. This release tweaks how the #[component] macro emits code in order to enable better rust-analyzer support within the body of components.

If you've disabled rust-analyzer inside #[component] for better DX, try toggling that off and see if this is a better experience than it was before!

Apologies for any regressions this causes. Please report any issues that arise.

There is still additional work to be done to support rust-analyzer in the view! macro. The hoped-for improvements here are solely inside the #[component] body.

Optional Context <Provider/> Component

Since 0.5.0, there've been a couple instances of bugs or confusing behavior related to the fact that context now follows the reactive graph, not the component tree (see #1986, #2038).

This release includes a <Provider/> component that provides a certain value via context only to its children:

#[component]
pub fn App() -> impl IntoView {
    // each Provider will only provide the value to its children
    view! {
        <Provider value=1u8>
            // correctly gets 1 from context
            {use_context::<u8>().unwrap_or(0)}
        </Provider>
        <Provider value=2u8>
            // correctly gets 2 from context
            {use_context::<u8>().unwrap_or(0)}
        </Provider>
        // does not find any u8 in context
        {use_context::<u8>().unwrap_or(0)}
    }
}

provide_context continues working as it has since 0.5.0, and if you're using it without problems you can ignore this, or use it if you prefer to aesthetics. If you're in a situation where you need to provide multiple context values of the same type and ensure that they are scoped correctly and that siblings do not overwrite one another, use <Provider/>. If you have no idea what I mean, check the issues above for examples of the bugs this fixes.

What's Changed

Full Changelog: v0.5.3...v0.5.4

v0.5.3

28 Nov 00:58
@gbj gbj
Compare
Choose a tag to compare

v0.5.3

Along with the usual set of bugfixes and small improvements, there are two noteworthy additions in this release.

Improved rust-analyzer support in #[component] macro body

Good LSP support for proc macros is hard, because proc macros depend on parsing a stream of input into a valid Rust syntax tree, but while typing you are constantly creating a stream of new, invalid trees. This release tweaks how the #[component] macro emits code in order to enable better rust-analyzer support within the body of components.

If you've disabled rust-analyzer inside #[component] for better DX, try toggling that off and see if this is a better experience than it was before!

Apologies for any regressions this causes. Please report any issues that arise.

There is still additional work to be done to support rust-analyzer in the view! macro. The hoped-for improvements here are solely inside the #[component] body.

Optional Context <Provider/> Component

Since 0.5.0, there've been a couple instances of bugs or confusing behavior related to the fact that context now follows the reactive graph, not the component tree (see #1986, #2038).

This release includes a <Provider/> component that provides a certain value via context only to its children:

#[component]
pub fn App() -> impl IntoView {
    // each Provider will only provide the value to its children
    view! {
        <Provider value=1u8>
            // correctly gets 1 from context
            {use_context::<u8>().unwrap_or(0)}
        </Provider>
        <Provider value=2u8>
            // correctly gets 2 from context
            {use_context::<u8>().unwrap_or(0)}
        </Provider>
        // does not find any u8 in context
        {use_context::<u8>().unwrap_or(0)}
    }
}

provide_context continues working as it has since 0.5.0, and if you're using it without problems you can ignore this, or use it if you prefer to aesthetics. If you're in a situation where you need to provide multiple context values of the same type and ensure that they are scoped correctly and that siblings do not overwrite one another, use <Provider/>. If you have no idea what I mean, check the issues above for examples of the bugs this fixes.

Complete Changelog

  • fix: relax 'static bound on as_child_of_current_owner by @gbj in #1955
  • IntoView and IntoAttribute for std::fmt::Arguments improvements by @tqwewe in #1947
  • workflows: bump setup-node to version 4. by @martinfrances107 in #1944
  • Minor: bumped tj-actions/changed-files to @39. by @martinfrances107 in #1942
  • feat: add new method to Trigger by @tqwewe in #1935
  • feat: impl IntoAttribute for Cow<'static, str> by @tqwewe in #1945
  • chore: Add server to procMacro for helix as well by @kerkmann in #1951
  • Allow arbitrary attributes for anchor component by @koopa1338 in #1953
  • fix: add leptos_axum::build_static_routes (closes #1843) by @gbj in #1855
  • Implement IntoAttribute for TextProp by @SleeplessOne1917 in #1925
  • chore: fix clippy on login_with_token_csr_only by @gbj in #1965
  • fix: router docs feature stable to nightly by @chrisp60 in #1959
  • chore: typed-builder and typed-builder-macro - bumped version numbers. by @martinfrances107 in #1958
  • fix: leptos_router::params_map! by @chrisp60 in #1973
  • docs: Fix 08_parent_child.md callback example code. by @gibbz00 in #1976
  • Fix [] url in 12_transition.md by @gibbz00 in #1980
  • book: point leptos_server docs.rs url to latest version. by @gibbz00 in #1982
  • docs: clarify need to provide context in two places for server functions by @gbj in #1983
  • fix: treat Suspense as containing a Set of resources, not a counter by @gbj in #1985
  • examples: fix hackernews_js_fetch style.css path (closes #1992) by @gbj in #1994
  • fix: run <ErrorBoundary/> in a child so siblings don't collide (closes #1987) by @gbj in #1991
  • examples: remove incorrect CSR information for hackernews_js_fetch example by @gbj in #1997
  • fix: correctly reset island/not-island state of SSRed Suspense streaming into island (closes #1996) by @gbj in #2000
  • book: Fix link in metadata.md by @gibbz00 in #1999
  • docs: fix CodeSandbox for resources by @gbj in #2002
  • docs: remove outdated APP_ENVIRONMENT variable by @gbj in #2013
  • feat: Action::new and Action::server by @chrisp60 in #1998
  • chore: remove duplicate benchmarks in leptos_reactive by @gbj in #2014
  • docs: add note about context shadowing (closes #1986) by @gbj in #2015
  • re-export slice! by @blorbb in #2008
  • fix: allow nested functions in Attribute (closes #2023) by @gbj in #2027
  • fix: use create_effect for <Portal/> to avoid hydration issues (closes #2010) by @gbj in #2029
  • impl Default for TextProp by @blorbb in #2016
  • Docs: ActionForm examples for complex arguments by @chrisp60 in #2017
  • fix document book input name query by @datewu in #2024
  • Minor: stop using std::fmt, instead used core::fmt. by @martinfrances107 in #2033
  • Updates to Leptos Book by @diversable in #2036
  • examples: add CSR with server functions example (closes #1975) by @gbj in #2031
  • Have fetch example conform to docs guidance around using and in conjunction by @MoonKraken in #2035
  • feat: <Provider/> component to fix context shadowing (closes #2038) by @gbj in #2040
  • updated axum session to latest 0.9 in examples by @genusistimelord in #2049
  • ci(examples): fix portal test by @gbj in #2051
  • docs: add warning for nested Fn in attribute (see #2023) by @gbj in #2045
  • fix: correctly mark Trigger as clean when it is re-tracked (closes #1948, #2048) by @gbj in #2059
  • Add instruction to install trunk to examples/README.md by @hpepper in #2064
  • fix: dispose previous route or outlet before rendering new one (closes #2070) by @gbj in #2071
  • ci(leptos): run ci on change instead of check by @agilarity in #2061
  • fix: make prop serialization opt-in for devtools (closes #1952) by @gbj in #2081
  • fix: improved rust-analyzer support in #[component] macro? by @gbj in #2075

New Contributors

Full Changelog: v0.5.2...v0.5.3

v0.5.2

25 Oct 02:00
@gbj gbj
Compare
Choose a tag to compare

This has a bunch of bugfixes, small docs improvements, etc. but there are actually a bunch of cool new features, mostly from our growing body of contributors. See the full changelog below, but here are some highlights (apologies if I missed anything big)

Features

extractor() function with better API

The extract API is awkward due to closure. This adds an extractor function which is a little more ergonomic.

#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Search {
    q: String,
}

// Actix
#[server]
pub async fn query_extract() -> Result<Search, ServerFnError> {
    use actix_web::web::Query;
    use leptos_actix::extractor;

    let Query(query) = extractor().await?;
    Ok(query)
}

// Axum
#[server]
pub async fn data() -> Result<Search, ServerFnError> {
    use axum::extract::Query;
    use leptos_axum::extractor;
    
    let Query(query) = extractor().await?;
    Ok(query)
}

<Portal/>

Adds a portal component that lets you render some view mounted in a location other than where it appears in the view tree.

<Show when=show_overlay fallback=|| ()>
    <div>Show</div>
    <Portal mount=document().get_element_by_id("app").unwrap()>
        <div style="position: fixed; z-index: 10; width: 100vw; height: 100vh; top: 0; left: 0; background: rgba(0, 0, 0, 0.8); color: white;">
            <p>This is in the body element</p>
            <button id="btn-hide" on:click=move |_| set_show_overlay(false)>
                Close Overlay
            </button>
            <button id="btn-toggle" on:click=move |_| set_show_inside_overlay(!show_inside_overlay())>
                Toggle inner
            </button>

            <Show when=show_inside_overlay fallback=|| view! { "Hidden" }>
                Visible
            </Show>
        </div>
    </Portal>
</Show>

Server Function Named Arguments

Now that we've made all server function arguments optional, this adds in the ability to pass in one or more named arguments:

#[server(endpoint = "/path/to/my/endpoint")]
pub async fn my_server_action() -> Result<(), ServerFnError> {
    Ok(())
}

#[server(encoding = "GetJson")]
pub async fn my_server_action() -> Result<(), ServerFnError> {
    Ok(())
}

Directives

Adds support for directive functions, which can be used with use: in the view:

// This doesn't take an attribute value
fn my_directive(el: HtmlElement<AnyElement>) {
    // do sth
}

// This requires an attribute value
fn another_directive(el: HtmlElement<AnyElement>, params: i32) {
    // do sth
}

// ... in the view
view! {
  <div use:my_directive></div>
  <SomeComponent use:another_directive=5 />
}

slice!() macro

Makes it easier to define slices. Expands to the same output as create_slice:

#[derive(Default)]
pub struct State {
    count: i32,
    name: String,
}

let state = create_rw_signal(State::default());

let (count, set_count) = slice!(state.count);

What's Changed

New Contributors

Read more

v0.5.1

06 Oct 14:02
@gbj gbj
Compare
Choose a tag to compare

This is primarily a bugfix release, but it also includes a new feature: making the fallback prop on Show, Suspense, and Transition optional.

For 0.5 release notes in general, click here.

What's Changed

  • docs: fix For view prop name (closes #1813) by @gbj in #1814
  • Missing docs and Copy impl for Callback by @gbj in #1818
  • Fix template! cfg condition by @Dragonink in #1822
  • Docs: DX improvements: add section about jetbrains intellij-rust by @sebadob in #1804
  • made show fallback optional by @maccesch in #1817
  • Fixes #1828, making SSR behavior match the Hydrate behavior. by @dgsantana in #1829
  • fix: make explicit allowances for local-only suspense fragments in SSR/hydrate mode (closes #1823) by @gbj in #1824
  • fix: correctly quote spread attributes in {..attrs} syntax (fixes #1826) by @gbj in #1831
  • Allow disposing of Signal & StoredValue by @PaulWagener in #1849
  • Make Async Mode return Content-Type header in Response by @benwis in #1851
  • feat: support stored values in with! and update! by @blorbb in #1836
  • Removed warning in build artefacts. by @martinfrances107 in #1840
  • fix: update log debug to use get_untracked for logged in user to resolve client side console error by @kevinold in #1834
  • fix: panic during generate_route_list if you immediately dispatch an action (closes #1832) by @gbj in #1853
  • fix: clippy "needless lifetimes" warning (closes #1825) by @gbj in #1852
  • "#two" was being hidden. Added another # to unhide by @he00741098 in #1847

New Contributors

Full Changelog: v0.5.0...v0.5.1

v0.5.0

29 Sep 21:35
@gbj gbj
fa2be59
Compare
Choose a tag to compare

v0.5.0

Goodbye, Scope

This long-awaited release changes the nature of the reactive system by removing the entire concept of explicit Scope and the ubiquitous cx variable.

The primary impetus behind this change is that it increases the correctness of the behavior of the reactive system, and fixes several persistent issues.

Click here for more details. In order to provide signals that implement `Copy` are are `'static` and are therefore easy to use with closures and event listeners in 100% safe Rust, Leptos allocates memory for signals, memos, and effects in an arena. This raises the question: When is it safe to deallocate/dispose of these signals?

From 0.0 to 0.4, Leptos allocated signals in a dedicated Scope, which was ubiquitous in APIs. This had several drawbacks

  1. Ergonomics: It was annoying additional boilerplate to pass around.
  2. Trait implementations: Needing an additional Scope argument on many functions prevented us from implementing many traits that could not take an additional argument on signals, like From, Serialize/Deserialize.
  3. Correctness: Two characteristics made this system somewhat broken
  • The Scope was stored in a variable that was passed around, meaning that the β€œwrong” scope could be passed into functions (most frequently Resource::read()). If, for example, a derived signal or memo read from a resource in the component body, and was called under a Suspense lower in the tree, the Scope used would be from the parent component, not the Suspense. This was just wrong, but involved wrapping the function in another closure to pass in the correct Scope.
  • It was relatively easy to create situations, that could leak memory unless child Scopes were manually created and disposed, or in which on_cleanup was never called. (See #802 and #918 for more background.)

The solution to this problem was to do what I should have been doing a year ago, and merge the memory allocation function of Scope into the reactive graph itself, which already handles reactive unsubscriptions and cleanup. JavaScript doesn’t deal with memory management, but SolidJS handles its onCleanup through a concept of reactive ownership; disposing of memory for our signals is really just a case of cleanup on an effect or memo rerunning.

Essentially, rather than being owned by a Scope every signal, effect, or memo is now owned by its parent effect or memo. (If it’s in an untrack, there’s no reactive observer but the reactive owner remains.) Every time an effect or memo reruns, it disposes of everything β€œbeneath” it in the tree. This makes sense: for a signal to be owned by an effect/memo, it must have been created during the previous run, and will be recreated as needed during the next run, so this is the perfect time to dispose of it.

It also has the fairly large benefit of removing the need to pass cx or Scope variables around at all, and allowing the implementation of a bunch of different traits on the various signal types.

Now that we don't need an extra Scope argument to construct them, many of the signal types now implement Serialize/Deserialize directly, as well as From<T>. This should make it significantly easier to do things like "reactively serialize a nested data structure in a create_effect" β€” this removed literally dozens of lines of serialization/deserialization logic and a custom DTO from the todomvc example. Serializing a signal simply serializes its value, in a reactive way; deserializing into a signal creates a new signal containing that deserialized value.

Migration is fairly easy. 95% of apps will migrate completely by making the following string replacements:

  1. cx: Scope, => (empty string)
  2. cx: Scope => (empty string)
  3. cx, => (empty string)
  4. (cx) => ()
  5. |cx| => ||
  6. Scope, => (empty string)
  7. Scope => (empty string) as needed
  8. You may have some |_, _| that become |_| or |_| that become ||, particularly for the fallback props on <Show/> and <ErrorBoundary/>.

Basically, there is no longer a Scope type, and anything that used to take it can simply be deleted.

For the 5%: if you were doing tricky things like storing a Scope somewhere in a struct or variable and then reusing it, you should be able to achieve the same result by storing Owner::current() somewhere and then later using it in with_owner(owner, move || { /* ... */ }). If you have issues with this kind of migration, please let me know by opening an issue or discussion thread and we can work through the migration.

Islands

This release contains an initial, but very functional, implementation of the β€œislands architecture” for Leptos.

This adds an experimental-islands feature that opts you into a rendering mode that's different from the traditional server-rendered/client-hydrated model described here, in which the entire page requested is rendered to HTML on the server for the first request, and then runs on the client to hydrate this server-rendered HTML, and subsequent navigations take place by rendering pages in the browser.

The experimental- in experimental-islands means β€œparts of this API may need to change, and I won’t guarantee that its APIs won’t break during 0.5,” but I have no reasons to believe there are significant issues, and I have no planned API changes at present.

With this islands feature on, components are only rendered on the server, by default. Navigations follow the traditional/multi-page-app (MPA) style of navigating by fetching a new HTML page from the server. The name "islands" comes from the concept of the "islands architecture," in which you opt into small β€œislands” of interactivity in an β€œocean” of server-rendered, non-interactive HTML.

This allows reducing the WASM binary size by a lot (say ~80% for a typical demo app), making the time-to-interactive for a page load much faster. It also allows you to treat most components as β€œserver components” and use server-only APIs, because those plain components will never need to be rendered in the browser.

I still need to write some guide-style docs for the book, but I tried to put a pretty good amount of information in the PR, which you should read if you’re interested in this topic or in trying out islands.

There’s significant additional exploration that will take place in this direction. Expect a longer treatment in an upcoming updated roadmap posted as a pinned issue.

Additional Reading

Static Site Generation

This release includes some preliminary work on building static site rendering directly into the framework, mostly as part of leptos_router and the server integrations. Static site generation is built off of two key components, a new <StaticRoute /> component and the leptos_router::build_static_routes function.

StaticRoute defines a new static route. In order to be a static route, all parent routes must be static in order to ensure that complete URLs can be built as static pages. A β€œstatic route” means that a page can be rendered as a complete HTML page that is then cached and served on subsequent requests, rather than dynamically rendered, significantly reducing the server’s workload per request.

StaticRoute takes a path and view (like any Route) and a static_params prop. This is a function that returns a Future, which is used to provide a map of all the possible values for each route param. These static params are generated on the server. This means you can do something like call a #[server] function or access a database or the server filesystem. For example, if you have a /post/:id path, you might query the database for a list of all blog posts and use that to generate a set of possible params.

StaticRoute can be given an optional mode: StaticMode::Upfront (the default) or StaticMode::Incremental. Upfront means that all given routes will be generated when you call build_static_routes, and a 404 will be returned for any other pages. Incremental means that all the options you give in static_params will be generated up front, and additional pages will be generated when first requested and then cached.

Where our routes are defined, we can include a StaticRoute:

view! {
	<Routes>
		<StaticRoute
			mode=StaticMode::Incremental
			path="/incr/:id"
			view=StaticIdView
			static_params=move || Box::pin(async move {
				let mut map = StaticParamsMap::default();
				map.insert("id".to_string(), vec![(1).to_string(), (2).to_string()]);
				map
			})
		/>
	</Routes>
}

In main.rs, we build the static routes for the site on server startup:

let (routes, static_data_map) = generate_route_list_with_ssg(App);
build_static_routes(&conf.leptos_options, App, &routes, &static_data_map)
	.await
	.unwrap();

More to Come

There is additional work needed here as far as providing examples, and building out the story for things like invalidation (when to rebuild a page) and build hooks (when to build additional pages).

Other New Features in 0.5

attr: on components, and spreading attributes

Makes it much easier to pass some set of attributes to be given to a component (with attr: passed into a #[prop(attrs)] prop), and then to spread them onto an element with {..attrs} syntax.

#[...
Read more

v0.5.0-rc3

22 Sep 22:00
@gbj gbj
d99269a
Compare
Choose a tag to compare
v0.5.0-rc3 Pre-release
Pre-release

See here for more substance.

Most significant breaking change you might notice is that generate_route_list in Axum is no longer async and doesn't need .await, as is already true for Actix.

Big features including static site generation included here, but I will wait for an actual release to write up notes because it's Friday afternoon!

What's Changed (since v0.5.0-rc2)

New Contributors

Full Changelog: v0.5.0-rc2...v0.5.0-rc3