* Add beginner tip to ErrorBoundary
This might seem simple, but the nuances of types and traits confuse many people learning the language.
* edit
* Update error_boundary.rs
* edits
* ignore error block
* Added name span to .build in component_to_tokens
* Added #[allow(unreachable_code)] to leptos::component_view inside component_to_tokens
* Added span to name reference in component_to_tokens
* Added span to leptos::component_props_builder in component_to_tokens
* Added span to props in component_to_tokens
* Added span to "on" method in events component_to_tokens
* Added spans in directive_call_from_attribute_node
* Added spans in fragment_to_tokens and it's ssr version
* Added span to props in slot_to_tokens
* Added span to the whole slot quote
* Changed slots's name span to last slot node
* Added span to the slot vec
* Added #[allow(unreachable_code)] to `.into()` in slot_to_tokens
* Added span to `.build()` in slot_to_tokens
* Added span for the whole component
* Added span to "clone:" directive
* Added span to ".children()"
* Removed unused "_span" param from fragment_to_tokens and fragment_to_tokens_ssr
* Removed unnecessary parenthesis around values in `attribute_to_tokens`
* Removed unnecessary curly braces around value in `spread_attrs`
* Removed unnecessary parenthesis around children in `element_to_tokens`
* Added catch-all span to element_to_tokens
* Formatted `quote!` according to official guidelines
* Updated view/snapshots in leptos_macro
* Added span to spread props in element_to_tokens_ssr
* Removed unnecessary curly braces in element_to_token_ssr
* Updated view/snapshots in leptos_macro
* Added view macro tests to leptos_macro
* Fixed clippy warnings in view macro output
* Updated view snapshots in tests
* Fixed expected_one_let_bind_got_none test in leptos_macro
* Removed snapshot tests in leptos_macro/tests/ui/view
---------
Co-authored-by: Greg Johnston <greg.johnston@gmail.com>
Manually reviewed the changes. All look like reasonable nudges.
A summary :-
In one place removed a redundant call to .clone().
In two places, now using clone_from() which clippy says
**MAY** be an optimisation.
It's normal to have a `NotFound` page with a wildcard path like this
```
<Routes>
...
<Route path="*any" view=NotFound>
</Routes>
```
In `ssr` mode, most servers do a `first match win` approach, so we
should register server functions before view routes, or else a wildcard
route would block all api requests.
https://discord.com/channels/1031524867910148188/1218508054442545185
Signed-off-by: 司芳源 <sify21@163.com>
non-serializable struct.
This prevents it from being returned in the
get_user() API, and prevents it from being unintentionally returned on any
new API the end-user may create on top of this example code.
Delegated event listeners do not support adding more than one event listener of the same type. This can cause confusion if two listeners are added, as one is silently dropped.
Instead of using `Closure::once_into_js`, this uses `into_js_value`,
which uses weak references to clean up the closure when Javascript no
longer has need of it.
It would be nice to make this (and the similar interval function) drop
the callback promptly when cancelled, but I don't think that's
possible while keeping the handles Copy.
Fixes#2330
Co-authored-by: Robert Macomber <robertm@mox>
In client-side navigation we now handle redirects returned from
server functions by resolving the location against the current
origin as a base. The base is only relevant if the location
doesn't already include an origin. This fixes cross-origin
redirects.
Note: in order to handle redirects in the same way as the browser
would handle them, we need to use the server function's URL
(typically `<origin>/api/something`) as a base. I leave this as
a TODO for a future leptos version, because it probably
requires changing the signature of the `server_fn` redirect hook.
In order to not be affected by a future breaking change, users
should already start making sure that their redirect locations
either include an origin or at least start with a single slash
(e.g. `Location: /foo`).
Otherwise user gets:
```
use_head() is being called without a MetaContext being provided. We'll automatically create and provide one, but if this is being called in a child route it may cause bugs. To be safe, you should provide_meta_context() somewhere in the root of the app.
```
* fix(ci): address clippy issue
* fix(ci): add missing nightly specifications
* fix(ci): set all nightly references
* chore(ci): do not lint example crates
Note that this is a minimal implementation and will __not__ allow the
user to `expect_state` if they have external calls to rendering their
app (i.e. using `render_app_to_*` directly).
This warning appears in the browser's console log.
```
hackernews.js:933 At src/routes/stories.rs:39:17, you are reading a resource in `hydrate` mode outside a <Suspense/> or <Transition/>. This can cause hydration mismatch errors and loses out on a significant performance optimization. To fix this issue, you can either:
1. Wrap the place where you read the resource in a <Suspense/> or <Transition/> component, or
2. Switch to using create_local_resource(), which will wait to load the resource until the app is hydrated on the client side. (This will have worse performance in most cases.)
```
This warning is seen in the browsers console window.
```
counter_isomorphic.js:1068 At src/counters.rs:138:17, you are reading a resource in `hydrate` mode outside a <Suspense/> or <Transition/>. This can cause hydration mismatch errors and loses out on a significant performance optimization. To fix this issue, you can either:
1. Wrap the place where you read the resource in a <Suspense/> or <Transition/> component, or
2. Switch to using create_local_resource(), which will wait to load the resource until the app is hydrated on the client side. (This will have worse performance in most cases.)
```
Two derived signals "value" and "error_msg" need to be wrapped in a <Suspense> block.
"value" falls back to just the initial text.
"error" uses the default fallback.
* chore: update to axum 0.7
Removed http, since it's included in axum, and replaced hyper by http-body-util, which is a smaller.
* chore: update samples to work with nre axum
Missing sessions_axum_auth, pending PR merge.
* chore: all dependencies update to axum 0.7
* chore: cargo fmt
* chore: fix doctests
* chore: Fix example that in reality doesn't use axum.
Fixed anyway.
* chore: more examples support for axum 0.7
* Small tweak
* retain trailing slashes in paths but leave matching trail-slash-insensitive
* fix: Allow trailing slashes to remain in leptos_path.
* Better handling for trailing slashes. (#2154)
This adds a trailing_slash option to <Router> and <Route>.
By default, this option is backward compatible with current Leptos
behavior, but users can opt into two new modes for handling trailing
slashes.
* cargo fmt
* Fix redirect routes for wildcard patterns.
* Clippy fixies
* (Re)Reduce the scope of PossibleBranchContext's internals.
* Test real code, not copied code.
* Test TrailingSlash redirects.
* Fixes and more tests for matching "" && "/".
This path is the exception to the rule and *should* be treated
as equivalent regardless of its trailing slash.
* cargo fmt
---------
Co-authored-by: Tadas Dailyda <tadas@dailyda.com>
The root cause is the family of leptos modules requiring both versions 0.10.5 and 0.11.0
This PR will fix that. ( Also needs a bump to 0.12.0 )
```
warning: multiple versions for dependency `itertools`: 0.10.5, 0.11.0
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
note: the lint level is defined here
--> src/lib.rs:4:9
|
4 | #![warn(clippy::cargo)]
| ^^^^^^^^^^^^^
= note: `#[warn(clippy::multiple_crate_versions)]` implied by `#[warn(clippy::cargo)]`
```
I've always hated the get_todos function and I
wanted to change it badly. Added a .env file
containing the db url for sqlx-cli, and cleaned
up with leptosfmt
Co-authored-by: j0lol <me@j0.lol>
* fix: add support for placing attributes on server functions
Adding instrumentation to server functions is not straightforward (requires calling out to another ssr-only function which is instrumented). This commit adds all attributes on the server function to both the generated front end and back end functions. If those attributes are only desirable on the backend say, a user can always wrap their attribute in `#[cfg_attr(feature = "ssr", ..)]`.
* nit: formatting in example cargo
Update helix configuration for the newest version.
To be consistent, adding the `server` ignore entry to helix as well.
Also sorting the parameters alphabetically.
* added Callback to documentation and examples.
Also reduced code duplication in Callback implementation.
* added back the closure event callback example
This PR adds the ability to use `create_local_resource` instead of
`create_resource`. This will run the create resource locally on the
system and therefore its result type does not need to be `Seriaziable`.
Closes#1567
This allows form submission with checkbox inputs to work.
For example:
let doit = create_server_action::<DoItSFn>();
<ActionForm action=doit>
<input type="checkbox" name="is_good" value="true"/>
<input type="submit"/>
</ActionForm>
#[server(DoItSFn, "/api")]
pub async fn doit(#[server(default)] is_good: bool) -> Result<(), ServerFnError> {}
If is_good is absent in the request to the server API,
`Default::default()` is used instead.
warning: Rust code block is empty
--> leptos_reactive/src/memo.rs:209:9
|
209 | /// ```
| ^^^
|
= note: `#[warn(rustdoc::invalid_rust_codeblocks)]` on by default
help: mark blocks that do not contain Rust code as text
|
209 | /// ```text
| ++++
These lint warnings.
warning: /home/martin/build/leptos/leptos/Cargo.toml: dependency (serde) specified without providing a local path, Git repository, version, or workspace dependency to use. This will be considered an error in future versions
warning: /home/martin/build/leptos/leptos/Cargo.toml: dependency (serde_json) specified without providing a local path, Git repository, version, or workspace dependency to use. This will be considered an error in future versions
Handle all error codes 401-499 in addition to the
400 and 500-599 that were already handled.
In addition, handle them all in the same way
and improve the error message.
* Updated client reloading to use window.location.protocol/host to determine websocket connection. Added optional config reload_external_port to provide further control of the client websocket connection. These changes allow reloading while accessing the served site from outside of localhost.
On the latest lifetime we're getting the following warning in the server
macro:
warning: `&` without an explicit lifetime name cannot be used here
--> src/login.rs:19:1
|
19 | #[server(Login, "/api")]
| ^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
= note: this warning originates in the attribute macro `server` (in Nightly builds, run with -Z macro-backtrace for more info)
* fix: suppress warning about non-reactivity when calling `.refetch()` (occurs in e.g., async blocks in actions)
* fix: don't reenter reactivity if these are nested
* build(examples): pull up compile tasks
* build(examples): set toolchain for compiles tasks
* build(examples): set toolchain for build and check
* build(examples): set toolchain of other examples
* Update 23_ssr_modes.md
Fixed grammar, added the section anchor links
* Fixed a broken link
The github link doesn't get properly rendered in the Leptos book site. Make the book work, 'break' the github link.
* Update 26_extractors.md
Fixed broken Axum links. Added an Axum extract function doc link for consistency (had Actix, but not Axum before)
In order to facilitate its usage in other structs, which might derive
various traits, such as `serde::Serialize` or `PartialEq`, implement
them for the `leptos_config::Env` enum.
Signed-off-by: Filip Dutescu <filip@hucksy.dev>
Currently, `leptos::LeptosOptions` are deserialized (and serialized)
to `snake_case`, since, by default, `serde` uses the name of the field
as-is. The options given via `.toml` files are given using `kebab-case`.
This behaviour leads to problems if you wish to use
`leptos::LeptosOptions` as the type of a field in your own config,
which uses the `kebab-case` syntax, as it will not provide any values
to that field.
These changes switch out the previous mechanism of manually replacing
the `-` character to the `_` character in order for serialization to
work. In its place, it uses `serde`'s `#[serde(rename_all = ...)]`
macro to handle the naming convention.
In case other naming conventions are used, a workaround would be to
define a user-owned mirror struct of `leptos::LeptosOptions`, which
would contain the required fields, deserialize it as desired and'
convert it to the latter via, for example, the `From<T>` trait.
Closes: #1295
Signed-off-by: Filip Dutescu <filip@hucksy.dev>
* nix-flake: use follows for `rust-overlay` (..)
This removes the extra nixpkgs dependency by re-using the nixpkgs
already defined.
https://nixos.wiki/wiki/Flakeshttps://web.archive.org/web/20230621091703/https://nixos.wiki/wiki/Flakes
> # The `follows` keyword in inputs is used for inheritance.
> # Here, `inputs.nixpkgs` of sops-nix is kept consistent with the `inputs.nixpkgs` of
> # the current flake, to avoid problems caused by different versions of nixpkgs.
* nix-flake: use nix overlays for rustc/cargo (..)
This allows the same nightly rust version to be used across the
flake (vs. strictly in the `devShell`).
* nix-flake: add `mdbook` to the development shell (..)
bumped `nixpkgs` to the latest unstable to pull in mdbook version `0.4.30`.
* nix-flake: expose all rust tools in dev environment (..)
The `oxalica / rust-overlay` docs expose all tools in the development
environment, so we should do the same:
https://github.com/oxalica/rust-overlay#use-in-devshell-for-nix-develop
---------
Co-authored-by: Jay Querie <jay@querie.cc>
This requires state to be provided via context using a special handler, but allows for extractors that use this state, rather than only `()`, as previously.
The change in indentation makes the PR hard to review
so I will discuss the change in conversational language
Two "if"'s checks were merged into one "if"
this
- if let Some(expr) = node.value() {
- if let syn::Expr::Tuple(tuple) = expr {
becomes
+ if let Some(Tuple(tuple)) = node.value() {
This patch just clears the warnings listed below and ensures we get the benefits of a better package manger function.
```console
warning: some crates are on edition 2021 which defaults to `resolver = "2"`, but virtual workspaces default to `resolver = "1"`
note: to keep the current resolver, specify `workspace.resolver = "1"` in the workspace root's manifest
note: to use the edition 2021 resolver, specify `workspace.resolver = "2"` in the workspace root's manifest
```console
Rewrites the algorithm behind the `<For/>` component to create a more robust keyed list implementation, with the potential for future additional optimizations related to grouping moved ranges.
Closes#533.
* add "async routing" feature that waits for async resources to resolve before navigating
* add support for Outlet
* add `<RoutingProgress/>` component
* Feat: Upgrade to new local version of syn-rsx
* chore: Make macro more IDE friendly
1. Add quotation to RawText node.
2. Replace vec! macro with [].to_vec().
Cons:
1. Temporary remove allow(unused_braces) from expressions, to allow completion after dot in rust-analyzer.
* chore: Change dependency from syn-rsx to rstml
* chore: Fix value_to_string usage, pr comments, and fmt.
* chore: ignore playwright output
* fix: could not run playwright test
* test: should see the welcome message
* build: clean playwright output
* build: run playwright web tests
* build: setup e2e dependencies
This configures a hook to run the tailwindcss CLI when a build is triggered or retriggered via Trunk watch. It eliminates the need to run the tailwindcss manually.
* build: improve task names
* build: add clean-examples task
Make it easy to clean all the cargo and trunk files in the examples.
* build: clean after verify
Adjust the intro in the Dynamic Attributes page to include
the recent `dynamic style` feature. Also reorder a little given
the order of the page body that follows.
While the project offers a [Matrix][matrix] bridge, it is nowhere shown.
One would need to join the [Discord][discord] server and search through
it to find it.
To make it easier to join through [Matrix][matrix], add a badge in the
project `README.md`.
[matrix]: https://matrix.org/
[discord]: https://discord.com/Fixes: #893
Signed-off-by: Filip Dutescu <filip@hucksy.dev>
Most of the time when using use_context it would be a bug if the context
wasn't present and appropriate to panic. This is a convenience function
that has that behavior.
When running cargo clippy on server functions that use `cx: Scope` it
has an unused variable error.
It appears that the logic for adding an `#[allow(unused)]` notation is
inverted.
Due to no rust edition being present in a workspac's Cargo.toml, non
WASM compatible code can end up being built for a WASM target.
This commit documents this error and how to resolve it.
The modulo operator is less of a head-scratcher for folks coming through here. The bitwise & is equally correct (clearly) but is likely to cause confusion if folks don't immediately see what's going on.
// the `view` macro above expands to this builder syntax
div().child((
button().on(ev::click,clear).child("Clear"),
button().on(ev::click,decrement).child("-1"),
span().child(("Value: ",value,"!")),
button().on(ev::click,increment).child("+1")
))
}
// Easy to use with Trunk (trunkrs.dev) or with a simple wasm-bindgen setup
pubfnmain(){
mount_to_body(||view!{
<SimpleCounterinitial_value=3/>
})
}
```
## About the Framework
@@ -48,27 +73,27 @@ Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained re
## What does that mean?
- **Full-stack**: Leptos can be used to build apps that run in the browser (_client-side rendering_), on the server (_server-side rendering_), or by rendering HTML on the server and then adding interactivity in the browser (_hydration_). This includes support for _HTTP streaming_ of both data (`Resource`s) and HTML (out-of-order streaming of `<Suspense/>` components.)
- **Isomorphic**: Leptos provides primitives to write isomorphic server functions, i.e., functions that can be called with the “same shape” on the client or server, but only run on the server. This means you can write your server-only logic (database requests, authentication etc.) alongside the client-side components that will consume it, and call server functions as if they were running in the browser.
- **Web**: Leptos is built on the Web platform and Web standards. The router is designed to use Web fundamentals (like links and forms) and build on top of them rather than trying to replace them.
- **Full-stack**: Leptos can be used to build apps that run in the browser (client-side rendering), on the server (server-side rendering), or by rendering HTML on the server and then adding interactivity in the browser (server-side rendering with hydration). This includes support for HTTP streaming of both data ([`Resource`s](https://docs.rs/leptos/latest/leptos/struct.Resource.html)) and HTML (out-of-order or in-order streaming of [`<Suspense/>`](https://docs.rs/leptos/latest/leptos/fn.Suspense.html) components.)
- **Isomorphic**: Leptos provides primitives to write isomorphic [server functions](https://docs.rs/leptos_server/0.2.5/leptos_server/index.html), i.e., functions that can be called with the “same shape” on the client or server, but only run on the server. This means you can write your server-only logic (database requests, authentication etc.) alongside the client-side components that will consume it, and call server functions as if they were running in the browser, without needing to create and maintain a separate REST or other API.
- **Web**: Leptos is built on the Web platform and Web standards. The [router](https://docs.rs/leptos_router/latest/leptos_router/) is designed to use Web fundamentals (like links and forms) and build on top of them rather than trying to replace them.
- **Framework**: Leptos provides most of what you need to build a modern web app: a reactive system, templating library, and a router that works on both the server and client side.
- **Fine-grained reactivity**: The entire framework is built from reactive primitives. This allows for extremely performant code with minimal overhead: when a reactive signal’s value changes, it can update a single text node, toggle a single class, or remove an element from the DOM without any other code running. (_So, no virtual DOM!_)
- **Fine-grained reactivity**: The entire framework is built from reactive primitives. This allows for extremely performant code with minimal overhead: when a reactive signal’s value changes, it can update a single text node, toggle a single class, or remove an element from the DOM without any other code running. (So, no virtual DOM overhead!)
- **Declarative**: Tell Leptos how you want the page to look, and let the framework tell the browser how to do it.
## Learn more
Here are some resources for learning more about Leptos:
- [Book](https://leptos-rs.github.io/leptos/) (work in progress)
- [Common Bugs](https://github.com/leptos-rs/leptos/tree/main/docs/COMMON_BUGS.md) (and how to fix them!)
- Leptos Guide (in progress)
## `nightly` Note
Most of the examples assume you’re using `nightly`Rust.
Most of the examples assume you’re using `nightly`version of Rust and the `nightly` feature of Leptos. To use `nightly` Rust, you can either set your toolchain globally or on per-project basis.
To set up your Rust toolchain using `nightly` (and add the ability to compile Rust to WebAssembly, if you haven’t already)
To set `nightly` as a default toolchain for all projects (and add the ability to compile Rust to WebAssembly, if you haven’t already):
```
rustup toolchain install nightly
@@ -76,17 +101,19 @@ rustup default nightly
rustup target add wasm32-unknown-unknown
```
If you’re on `stable`, note the following:
If you'd like to use `nightly` only in your Leptos project however, add [`rust-toolchain.toml`](https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file) file with the following content:
1. You need to enable the `"stable"` flag in `Cargo.toml`: `leptos = { version = "0.1.0", features = ["stable"] }`
2.`nightly` enables the function call syntax for accessing and setting signals. If you’re using `stable`,
you’ll just call `.get()`, `.set()`, or `.update()` manually. Check out the
The `nightly` feature enables the function call syntax for accessing and setting signals, as opposed to `.get()` and `.set()`. This leads to a consistent mental model in which accessing a reactive value of any kind (a signal, memo, or derived signal) is always represented as a function call. This is only possible with nightly Rust and the `nightly` feature.
## `cargo-leptos`
[`cargo-leptos`](https://github.com/leptos-rs/cargo-leptos) is a build tool that's designed to make it easy to build apps that run on both the client and the server, with seamless integration. The best way to get started with a real Leptos project right now is to use `cargo-leptos` and our [starter template](https://github.com/leptos-rs/start).
[`cargo-leptos`](https://github.com/leptos-rs/cargo-leptos) is a build tool that's designed to make it easy to build apps that run on both the client and the server, with seamless integration. The best way to get started with a real Leptos project right now is to use `cargo-leptos` and our starter templates for [Actix](https://github.com/leptos-rs/start) or [Axum](https://github.com/leptos-rs/start-axum).
```bash
cargo install cargo-leptos
@@ -95,13 +122,13 @@ cd [your project name]
cargo leptos watch
```
Open browser on [http://localhost:3000/](http://localhost:3000/)
Open browser to [http://localhost:3000/](http://localhost:3000/).
## FAQs
### What’s up with the name?
*Leptos* (λεπτός) is an ancient Greek word meaning “thin, light, refine, fine-grained.” To me, a classicist and not a dog owner, it evokes the lightweight reactive system that powers the framework. I've since learned the same word is at the root of the medical term “leptospirosis,” a blood infection that affects humans and animals... My bad. No dogs were harmed in the creation of this framework.
_Leptos_ (λεπτός) is an ancient Greek word meaning “thin, light, refined, fine-grained.” To me, a classicist and not a dog owner, it evokes the lightweight reactive system that powers the framework. I've since learned the same word is at the root of the medical term “leptospirosis,” a blood infection that affects humans and animals... My bad. No dogs were harmed in the creation of this framework.
### Is it production ready?
@@ -109,7 +136,7 @@ People usually mean one of three things by this question.
1.**Are the APIs stable?** i.e., will I have to rewrite my whole app from Leptos 0.1 to 0.2 to 0.3 to 0.4, or can I write it now and benefit from new features and updates as new versions come?
With 0.1 the APIs are basically settled. We’re adding new features, but we’re very happy with where the type system and patterns have landed. I would not expect major breaking changes to your code to adapt to, for example, a 0.2.0 release.
The APIs are basically settled. We’re adding new features, but we’re very happy with where the type system and patterns have landed. I would not expect major breaking changes to your code to adapt to future releases, in terms of architecture.
2.**Are there bugs?**
@@ -119,11 +146,11 @@ Yes, I’m sure there are. You can see from the state of our issue tracker over
This may be the big one: “production ready” implies a certain orientation to a library: that you can basically use it, without any special knowledge of its internals or ability to contribute. Everyone has this at some level in their stack: for example I (@gbj) don’t have the capacity or knowledge to contribute to something like `wasm-bindgen` at this point: I simply rely on it to work.
There are several people in this community using Leptos right now for internal apps at work, who have also become significant contributors. I think this is the right level of production use for now. There may be missing features that you need, and you may end up building them! But for internal apps, if you’re willing to build and contribute missing pieces along the way, the framework is definitely usable right now.
There are several people in the community using Leptos right now for internal apps at work, who have also become significant contributors. I think this is the right level of production use for now. There may be missing features that you need, and you may end up building them! But for internal apps, if you’re willing to build and contribute missing pieces along the way, the framework is definitely usable right now.
### Can I use this for native GUI?
Sure! Obviously the `view` macro is for generating DOM nodes but you can use the reactive system to drive native any GUI toolkit that uses the same kind of object-oriented, event-callback-based framework as the DOM pretty easily. The principles are the same:
Sure! Obviously the `view` macro is for generating DOM nodes but you can use the reactive system to drive any native GUI toolkit that uses the same kind of object-oriented, event-callback-based framework as the DOM pretty easily. The principles are the same:
- Use signals, derived signals, and memos to create your reactive system
- Create GUI widgets
@@ -132,35 +159,27 @@ Sure! Obviously the `view` macro is for generating DOM nodes but you can use the
I've put together a [very simple GTK example](https://github.com/leptos-rs/leptos/blob/main/examples/gtk/src/main.rs) so you can see what I mean.
### How is this different from Yew/Dioxus?
The new rendering approach being developed for 0.7 supports “universal rendering,” i.e., it can use any rendering library that supports a small set of 6-8 functions. (This is intended as a layer over typical retained-mode, OOP-style GUI toolkits like the DOM, GTK, etc.) That future rendering work will allow creating native UI in a way that is much more similar to the declarative approach used by the web framework.
On the surface level, these libraries may seem similar. Yew is, of course, the most mature Rust library for web UI development and has a huge ecosystem. Dioxus is similar in many ways, being heavily inspired by React. Here are some conceptual differences between Leptos and these frameworks:
### How is this different from Yew?
Yew is the most-used library for Rust web UI development, but there are several differences between Yew and Leptos, in philosophy, approach, and performance.
- **VDOM vs. fine-grained:** Yew is built on the virtual DOM (VDOM) model: state changes cause components to re-render, generating a new virtual DOM tree. Yew diffs this against the previous VDOM, and applies those patches to the actual DOM. Component functions rerun whenever state changes. Leptos takes an entirely different approach. Components run once, creating (and returning) actual DOM nodes and setting up a reactive system to update those DOM nodes.
- **Performance:** This has huge performance implications: Leptos is simply _much_ faster at both creating and updating the UI than Yew is.
- **Mental model:** Adopting fine-grained reactivity also tends to simplify the mental model. There are no surprising component re-renders because there are no re-renders. Your app can be divided into components based on what makes sense for your app, because they have no performance implications.
- **Performance:** This has huge performance implications: Leptos is simply much faster at both creating and updating the UI than Yew is.
- **Server integration:** Yew was created in an erain which browser-rendered single-page apps (SPAs) were the dominant paradigm. While Leptos supports client-side rendering, it also focuses on integrating with the server side of your application via server functions and multiple modes of serving HTML, including out-of-order streaming.
### How is this different from Sycamore?
-### How is this different from Dioxus?
Conceptually, these two frameworks are very similar: because both are built on fine-grained reactivity, most apps will end up looking very similar between the two, and Sycamore or Leptos apps will both look a lot like SolidJS apps, in the same way that Yew or Dioxus can look a lot like React.
Like Leptos, Dioxus is a framework for building UIs using web technologies. However, there are significant differences in approach and features.
There are some practical differences that make a significant difference:
- **VDOM vs. fine-grained:** While Dioxus has a performant virtual DOM (VDOM), it still uses coarse-grained/component-scoped reactivity: changing a stateful value reruns the component function and diffs the old UI against the new one. Leptos components use a different mental model, creating (and returning) actual DOM nodes and setting up a reactive system to update those DOM nodes.
- **Web vs. desktop priorities:** Dioxus uses Leptos server functions in its fullstack mode, but does not have the same `<Suspense>`-based support for things like streaming HTML rendering, or share the same focus on holistic web performance. Leptos tends to prioritize holistic web performance (streaming HTML rendering, smaller WASM binary sizes, etc.), whereas Dioxus has an unparalleled experience when building desktop apps, because your application logic runs as a native Rust binary.
-**Maturity:** Sycamore is obviously a much more mature and stable library with a larger ecosystem.
- **Templating:** Leptos uses a JSX-like template format (built on [syn-rsx](https://github.com/stoically/syn-rsx)) for its `view` macro. Sycamore offers the choice of its own templating DSL or a builder syntax.
- **Read-write segregation:** Leptos, like Solid, encourages read-write segregation between signal getters and setters, so you end up accessing signals with tuples like `let (count, set_count) = create_signal(cx, 0);` _(If you prefer or if it's more convenient for your API, you can use `create_rw_signal` to give a unified read/write signal.)_
- **Signals are functions:** In Leptos, you can call a signal to access it rather than calling a specific method (so, `count()` instead of `count.get()`) This creates a more consistent mental model: accessing a reactive value is always a matter of calling a function. For example:
-### How is this different from Sycamore?
```rust
let (count, set_count) = create_signal(cx, 0); // a signal
let double_count = move || count() * 2; // a derived signal
let memoized_count = create_memo(cx, move |_| count() * 3); // a memo
Sycamore and Leptos are both heavily influenced by SolidJS. At this point, Leptos has a larger community and ecosystem and is more actively developed. Other differences:
- **Signals and scopes are `'static`:** Both Leptos and Sycamore ease the pain of moving signals in closures (in particular, event listeners) by making them `Copy`, to avoid the `{ let count = count.clone(); move |_| ... }` that's very familiar in Rust UI code. Sycamore does this by using bump allocation to tie the lifetimes of its signals to its scopes: since references are `Copy`, `&'a Signal<T>` can be moved into a closure. Leptos does this by using arena allocation and passing around indices: types like `ReadSignal<T>`, `WriteSignal<T>`, and `Memo<T>` are actually wrappers for indices into an arena. This means that both scopes and signals are both `Copy` and `'static` in Leptos, which means that they can be moved easily into closures without adding lifetime complexity.
- **Templating DSLs:** Sycamore uses a custom templating language for its views, while Leptos uses a JSX-like template format.
- **`'static` signals:** One of Leptos’s main innovations was the creation of `Copy + 'static` signals, which have excellent ergonomics. Sycamore is in the process of adopting the same pattern, but this is not yet released.
- **Perseus vs. server functions:** The Perseus metaframework provides an opinionated way to build Sycamore apps that include server functionality. Leptos instead provides primitives like server functions in the core of the framework.
Clicking the button twice will cause a panic, because of the nested signal _read_. Calling the `update` function on `resources` immediately takes out a mutable borrow on `resources`, then updates the `resource` signal—which re-runs the effect that reads from the signals, which tries to immutably access `resources` and panics. It's the nested update here which causes a problem, because the inner update triggers and effect that tries to read both signals while the outer is still updating.
You can fix this fairly easily by using the [`batch()`](https://docs.rs/leptos/latest/leptos/fn.batch.html) method:
```rust
letupdate=move|id: usize|{
batch(move||{
resources.update(|resources|{
resources
.entry(id)
.or_insert_with(||create_rw_signal(0))
.update(|amount|*amount+=1)
})
});
};
```
This delays running any effects until after both updates are made, preventing the conflict entirely without requiring any other restructuring.
## Templates and the DOM
### `<input value=...>` doesn't update or stops updating
@@ -37,11 +83,11 @@ Many DOM attributes can be updated either by setting an attribute on the DOM nod
This means that in practice, attributes like `value` or `checked` on an `<input/>` element only update the _default_ value for the `<input/>`. If you want to reactively update the value, you should use `prop:value` instead to set the `value` property.
// ✅ works as intended by setting the value *property*
<inputprop:value=aon:input=on_input/>
<inputprop:value=aon:input=on_input/>
}
```
## Build configuration
### Cargo feature resolution in workspaces
A new [version](https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions) of Cargo's feature resolver was introduced for the 2021 edition of Rust.
For single crate projects it will select a resolver version based on the Rust edition in `Cargo.toml`. As there is no Rust edition present for `Cargo.toml` in a workspace, Cargo will default to the pre 2021 edition resolver.
This can cause issues resulting in non WASM compatible code being built for a WASM target. Seeing `mio` failing to build is often a sign that none WASM compatible code is being included in the build.
The resolver version can be set in the workspace `Cargo.toml` to remedy this issue.
This project contains the core of a new introductory guide to Leptos.
The Leptos book is now available at [https://book.leptos.dev](https://book.leptos.dev).
It is built using `mdbook`. You can view a local copy by installing `mdbook`
```bash
cargo install mdbook
```
and run the book with
```
mdbook serve
```
It should be available at `http://localhost:3000`.
The source code for the book has moved to [https://github.com/leptos-rs/book](https://github.com/leptos-rs/book). Please open issues or make PRs in that repository.
One of our core contributors said to me recently: “I never used closures this often
until I started using Leptos.” And it’s true. Closures are at the heart of any Leptos
application. It sometimes looks a little silly:
```rust
// a signal holds a value, and can be updated
let(count,set_count)=create_signal(cx,0);
// a derived signal is a function that accesses other signals
letdouble_count=move||count()*2;
letcount_is_odd=move||count()&1==1;
lettext=move||ifcount_is_odd(){
"odd"
}else{
"even"
};
// an effect automatically tracks the signals it depends on
// and re-runs when they change
create_effect(cx,move|_|{
log!("text = {}",text());
});
view!{cx,
<p>{move||text().to_uppercase()}</p>
}
```
Closures, closures everywhere!
But why?
## Functions and UI Frameworks
Functions are at the heart of every UI framework. And this makes perfect sense. Creating a user interface is basically divided into two phases:
1. initial rendering
2. updates
In a web framework, the framework does some kind of initial rendering. Then it hands control back over to the browser. When certain events fire (like a mouse click) or asynchronous tasks finish (like an HTTP request finishing), the browser wakes the framework back up to update something. The framework runs some kind of code to update your user interface, and goes back asleep until the browser wakes it up again.
The key phrase here is “runs some kind of code.” The natural way to “run some kind of code” at an arbitrary point in time—in Rust or in any other programming language—is to call a function. And in fact every UI framework is based on rerunning some kind of function over and over:
1. virtual DOM (VDOM) frameworks like React, Yew, or Dioxus rerun a component or render function over and over, to generate a virtual DOM tree that can be reconciled with the previous result to patch the DOM
2. compiled frameworks like Angular and Svelte divide your component templates into “create” and “update” functions, rerunning the update function when they detect a change to the component’s state
3. in fine-grained reactive frameworks like SolidJS, Sycamore, or Leptos, _you_ define the functions that re-run
That’s what all our components are doing.
Take our typical `<SimpleCounter/>` example in its simplest form:
The `SimpleCounter` function itself runs once. The `value` signal is created once. The framework hands off the `increment` function to the browser as an event listener. When you click the button, the browser calls `increment`, which updates `value` via `set_value`. And that updates the single text node represented in our view by `{value}`.
Closures are key to reactivity. They provide the framework with the ability to re-run the smallest possible unit of your application in responsive to a change.
So remember two things:
1. Your component function is a setup function, not a render function: it only runs once.
2. For values in your view template to be reactive, they must be functions: either signals (which implement the `Fn` traits) or closures.
This is only a very limited introduction to testing. But I hope it’s useful as you begin to build applications.
> For more, see [the testing section of the `wasm-bindgen` guide](https://rustwasm.github.io/wasm-bindgen/wasm-bindgen-test/index.html#testing-on-wasm32-unknown-unknown-with-wasm-bindgen-test).
That “Hello, world!” was a *very* simple example. Let’s move on to something a
little more like an ordinary app.
First, let’s edit the `main` function so that, instead of rendering the whole
app, it just renders an `<App/>` component. Components are the basic unit of
composition and design in most web frameworks, and Leptos is no exception.
Conceptually, they are similar to HTML elements: they represent a section of the
DOM, with self-contained, defined behavior. Unlike HTML elements, they are in
`PascalCase`, so most Leptos applications will start with something like an
`<App/>` component.
```rust
fnmain(){
leptos::mount_to_body(|cx|view!{cx,<App/>})
}
```
Now let’s define our `<App/>` component itself. Because it’s relatively simple,
I’ll give you the whole thing up front, then walk through it line by line.
```rust
#[component]
fnApp(cx: Scope)-> implIntoView{
let(count,set_count)=create_signal(cx,0);
view!{cx,
<button
on:click=move|_|{
set_count.update(|n|*n+=1);
}
>
"Click me"
{move||count.get()}
</button>
}
}
```
## The Component Signature
```rust
#[component]
```
Like all component definitions, this begins with the [`#[component]`](https://docs.rs/leptos/latest/leptos/attr.component.html) macro. `#[component]` annotates a function so it can be
used as a component in your Leptos application. We’ll see some of the other features of
this macro in a couple chapters.
```rust
fnApp(cx: Scope)-> implIntoView
```
Every component is a function with the following characteristics
1. It takes a reactive [`Scope`](https://docs.rs/leptos/latest/leptos/struct.Scope.html)
as its first argument. This `Scope` is our entrypoint into the reactive system.
By convention, it’s usually named `cx`.
2. You can include other arguments, which will be available as component “props.”
3. Component functions return `impl IntoView`, which is an opaque type that includes
anything you could return from a Leptos `view`.
## The Component Body
The body of the component function is a set-up function that runs once, not a
render function that re-runs multiple times. You’ll typically use it to create a
few reactive variables, define any side effects that run in response to those values
creates a signal, the basic unit of reactive change and state management in Leptos.
This returns a `(getter, setter)` tuple. To access the current value, you’ll
use `count.get()` (or, on `nightly` Rust, the shorthand `count()`). To set the
current value, you’ll call `set_count.set(...)` (or `set_count(...)`).
> `.get()` clones the value and `.set()` overwrites it. In many cases, it’s more
efficient to use `.with()` or `.update()`; check out the docs for [`ReadSignal`](https://docs.rs/leptos/latest/leptos/struct.ReadSignal.html) and [`WriteSignal`](https://docs.rs/leptos/latest/leptos/struct.WriteSignal.html) if you’d like to learn more about those trade-offs at this point.
## The View
Leptos defines user interfaces using a JSX-like format via the [`view`](https://docs.rs/leptos/latest/leptos/macro.view.html) macro.
```rust
view!{cx,
<button
// define an event listener with on:
on:click=move|_|{
set_count.update(|n|*n+=1);
}
>
// text nodes are wrapped in quotation marks
"Click me: "
// blocks can include Rust code
{move||count.get()}
</button>
}
```
This should mostly be easy to understand: it looks like HTML, with a special
`on:click` to define a `click` event listener, a text node that’s formatted like
a Rust string, and then...
```rust
{move||count.get()}
```
whatever that is.
People sometimes joke that they use more closures in their first Leptos application
than they’ve ever used in their lives. And fair enough. Basically, passing a function
into the view tells the framework: “Hey, this is something that might change.”
When we click the button and call `set_count`, the `count` signal is updated. This
`move || count.get()` closure, whose value depends on the value of `count`, re-runs,
and the framework makes a targeted update to that one specific text node, touching
nothing else in your application. This is what allows for extremely efficient updates
to the DOM.
Now, if you have Clippy on—or if you have a particularly sharp eye—you might notice
that this closure is redundant, at least if you’re in `nightly` Rust. If you’re using
Leptos with `nightly` Rust, signals are already functions, so the closure is unnecessary.
As a result, you can write a simpler view:
```rust
view!{cx,
<button/* ... */>
"Click me: "
// identical to {move || count.get()}
{count}
</button>
}
```
Remember—and this is *very important*—only functions are reactive. This means that
`{count}` and `{count()}` do very different things in your view. `{count}` passes
in a function, telling the framework to update the view every time `count` changes.
`{count()}` access the value of `count` once, and passes an `i32` into the view,
rendering it once, unreactively. You can see the difference in the CodeSandbox below!
> Throughout this tutorial, we’ll use CodeSandbox to show interactive examples. To
show the browser in the sandbox, you may need to click `Add DevTools >
Other Previews > 8080.` Hover over any of the variables to show Rust-Analyzer details
and docs for what’s going on. Feel free to fork the examples to play with them yourself!
is a enumerated type that represents any kind of readable reactive signal. It can
be useful when defining APIs for components you’ll want to reuse while passing
different sorts of signals. The [`MaybeSignal`](https://docs.rs/leptos/latest/leptos/enum.MaybeSignal.html) type is useful when you want to be able to take either a static or
The examples in this directory are all built and tested against the current `main` branch.
To the extent that new features have been released or breaking changes have been made since the previous release, the examples are compatible with the `main` branch and not the current release.
## Getting Started
The simplest way to get started with any example is to use the “quick start” command found in the README for each example. Most of the examples use either [`trunk`](https://trunkrs.dev/) (a simple build system and dev server for client-side-rendered apps) or [`cargo-leptos`](https://github.com/leptos-rs/cargo-leptos) (a build system for server-rendered and client-hydrated apps).
## Using Cargo Make
You can also run any of the examples using [`cargo-make`](https://github.com/sagiegurari/cargo-make). Note that this is completely optional. We use it for CI, and it can be convenient for running the examples, but is not required.
Follow these steps to get any example up and running.
1.`cd` to the example you want to run
2. Make sure `cargo-make` is installed (for example by running `cargo install cargo-make`)
3. Make sure `rustup target add wasm32-unknown-unknown` was executed for the currently selected toolchain.
4. Run `cargo make ci` to setup and test the example
5. Run `cargo make start` to run the example
6. Open the client URL in the console output (<http://127.0.0.1:8080> or <http://127.0.0.1:3000> by default)
7. Run `cargo make stop` to end any processes started by `cargo make start`.
Here are a few additional notes:
- Extendable custom task files are located in the [cargo-make](./cargo-make/) directory
- Running a task will automatically install `cargo` dependencies
- Each `Makefile.toml` file must extend the [cargo-make/main.toml](./cargo-make/main.toml) file
- [cargo-make](./cargo-make/) files that end in `*-test.toml` configure web testing strategies
- Run `cargo make test-report` to learn which examples have web tests
## Prerequisites
Example projects depend on the following tools. Please install them as needed.
cargo-leptos is now the easiest and most featureful way to build server side rendered apps with hydration. It provides automatic recompilation of client and server code, wasm optimisation, CSS minification, and more! Check out more about it [here](https://github.com/akesson/cargo-leptos)
1. Install cargo-leptos
```bash
cargo install --locked cargo-leptos
```
2. Build the site in watch mode, recompiling on file changes
```bash
cargo leptos watch
```
Open browser on [http://localhost:3000/](http://localhost:3000/)
3. When ready to deploy, run
```bash
cargo leptos build --release
```
## WASM Pack
To run it as a server side app with hydration, you'll need to have wasm-pack installed.
0. Edit the `[package.metadata.leptos]` section and set `site-root` to `"."`. For examples with CSS you also want to change the path of the `<StyleSheet / >` component in the root component to point towards the CSS file in the root. This tells leptos that the WASM/JS files generated by wasm-pack are available at `./pkg` and that the CSS files are no longer processed by cargo-leptos. Building to alternative folders is not supported at this time. You'll also want to edit the call to `get_configuration()` to pass in `Some(Cargo.toml)`, so that Leptos will read the settings instead of cargo-leptos. If you do so, your file/folder names cannot include dashes.
1. Install wasm-pack
```bash
cargo install wasm-pack
```
2. Build the Webassembly used to hydrate the HTML from the server
# Defines a size-optimized profile for the WASM bundle in release mode
[profile.wasm-release]
inherits="release"
opt-level='z'
lto=true
codegen-units=1
panic="abort"
[package.metadata.leptos]
# The name used by wasm-bindgen/cargo-leptos for the JS/WASM bundle. Defaults to the crate name
output-name="leptos_start"
# The site root folder is where cargo-leptos generate all output. WARNING: all content of this folder will be erased on a rebuild. Use it in your server setup.
site-root="target/site"
# The site-root relative folder where all compiled output (JS, WASM and CSS) is written
# Defaults to pkg
site-pkg-dir="pkg"
# [Optional] The source CSS file. If it ends with .sass or .scss then it will be compiled by dart-sass into CSS. The CSS is optimized by Lightning CSS before being written to <site-root>/<site-pkg>/app.css
style-file="style/main.scss"
# The IP and port (ex: 127.0.0.1:3000) where the server serves the content. Use it in your server setup.
site-addr="127.0.0.1:3000"
# The port to use for automatic reload monitoring
reload-port=3001
# [Optional] Command to use when running end2end tests. It will run in the end2end dir.
# [Windows] for non-WSL use "npx.cmd playwright test"
# This binary name can be checked in Powershell with Get-Command npx
end2end-cmd="npx playwright test"
end2end-dir="end2end"
# The browserlist query used for optimizing the CSS.
browserquery="defaults"
# Set by cargo-leptos watch when building with that tool. Controls whether autoreload JS will be included in the head
watch=false
# The environment Leptos will run in, usually either "DEV" or "PROD"
env="DEV"
# The features to use when compiling the bin target
#
# Optional. Can be over-ridden with the command line parameter --bin-features
bin-features=["ssr"]
# If the --no-default-features flag should be used when compiling the bin target
#
# Optional. Defaults to false.
bin-default-features=false
# The features to use when compiling the lib target
#
# Optional. Can be over-ridden with the command line parameter --lib-features
lib-features=["hydrate"]
# If the --no-default-features flag should be used when compiling the lib target
#
# Optional. Defaults to false.
lib-default-features=false
# The profile to use for the lib target when compiling for release
#
# Optional. Defaults to "release".
lib-profile-release="wasm-release"
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.