Website | Book | Docs.rs | Playground | Discord
You can find a list of useful libraries and example projects at awesome-leptos
.
use leptos::*;
#[component]
pub fn SimpleCounter(initial_value: i32) -> impl IntoView {
// create a reactive signal with the initial value
let (value, set_value) = create_signal(initial_value);
// create event handlers for our buttons
// note that `value` and `set_value` are `Copy`, so it's super easy to move them into closures
let clear = move |_| set_value(0);
let decrement = move |_| set_value.update(|value| *value -= 1);
let increment = move |_| set_value.update(|value| *value += 1);
// create user interfaces with the declarative `view!` macro
view! {
<div>
<button on:click=clear>Clear</button>
<button on:click=decrement>-1</button>
// text nodes can be quoted or unquoted
<span>"Value: " {value} "!"</span>
<button on:click=increment>+1</button>
</div>
}
}
// we also support a builder syntax rather than the JSX-like `view` macro
#[component]
pub fn SimpleCounterWithBuilder(initial_value: i32) -> impl IntoView {
use leptos::html::*;
let (value, set_value) = create_signal(initial_value);
let clear = move |_| set_value(0);
let decrement = move |_| set_value.update(|value| *value -= 1);
let increment = move |_| set_value.update(|value| *value += 1);
// 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
pub fn main() {
mount_to_body(|| view! {
<SimpleCounter initial_value=3 />
})
}
Leptos is a full-stack, isomorphic Rust web framework leveraging fine-grained reactivity to build declarative user interfaces.
- 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) and HTML (out-of-order or in-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, 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 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 overhead!)
- Declarative: Tell Leptos how you want the page to look, and let the framework tell the browser how to do it.
Here are some resources for learning more about Leptos:
- Book (work in progress)
- Examples
- API Documentation
- Common Bugs (and how to fix them!)
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 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
rustup default nightly
rustup target add wasm32-unknown-unknown
If you'd like to use nightly
only in your Leptos project however, add rust-toolchain.toml
file with the following content:
[toolchain]
channel = "nightly"
targets = ["wasm32-unknown-unknown"]
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
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 or Axum.
cargo install cargo-leptos
cargo leptos new --git https://github.com/leptos-rs/start
cd [your project name]
cargo leptos watch
Open browser to http://localhost:3000/.
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.
People usually mean one of three things by this question.
- 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?
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.
- Are there bugs?
Yes, I’m sure there are. You can see from the state of our issue tracker over time that there aren’t that many bugs and they’re usually resolved pretty quickly. But for sure, there may be moments where you encounter something that requires a fix at the framework level, which may not be immediately resolved.
- Am I a consumer or a contributor?
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 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.
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
- Use event listeners to update signals
- Create effects to update the UI
The 0.7 update originally set out to create a "generic rendering" approach that would allow us to reuse most of the same view logic to do all of the above. Unfortunately, this has had to be shelved for now due to difficulties encountered by the Rust compiler when building larger-scale applications with the number of generics spread throughout the codebase that this required. It's an approach I'm looking forward to exploring again in the future; feel free to reach out if you're interested in this kind of work.
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.
-
Server integration: Yew was created in an era in 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.
Like Leptos, Dioxus is a framework for building UIs using web technologies. However, there are significant differences in approach and features.
-
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.
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:
- 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 ofCopy + '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.
leptos's People
Forkers
linecode jthodge rustloverthecoder sirgallifrey ecklf danheuck virgiel endeva83 richardsonjf agent001 drruhe akesson oceantume fundon tanangular gmh5225 mrjoe7 mendeljacks lostinspiration dglsparsons benwis jquesada2016 ifiokjr jasonrhansen safx llblumire pravinshahi0007 uiforks lo-ferris saolsen ghassanachi chang-ching-chung ashsimmonds michaelowens fdiskas treysidechain sleepyweasel1 wychl gtm-nayan milanec sergeykadiyevskiy vivian2158 dwindleduck raonysolettialonso mu-l zoosky nanderoo xeronith bronifty hamdysaad20 dandasipuneet younss jedanny jrcribb emilpriver saberboy spid37 guohuiyuan gentle doomhammerhell 1nsane-dev gg-big-org nim65s lucasrodriguesus ultrasaurus kuroyuki thomasingalls snapbug luckynumberke7in tshepang aox0 tebellox gulbrand ragingwind vaniusrb icodein martinfrances107 vizidrix trueb2 pyjcode psarna nilsherzig tako8ki praveennanda124 slowtec placrosse web3buildor jaedukseo mirra-kro booker-d pinkdiamond1 sirzjp najidnadri spartan09 arcilli ekanna kustomzone jamiebshaw thearchiver honsaleptos's Issues
HackerNews demo: "Back" button from story to main page is broken
Clone bounds for signals/resources/memos are mostly not necessary
Discussed briefly in #34, but I'm opening a new issue since it's mostly unrelated.
I took a look at the usage of Clone in the signals API and found it could be removed fairly easily. Resources and Memos were slightly less simple:
- Resource sources still need to be Clone, though the Future's output does not need to be
- A
Resource::with()
was added, with similar behavior toSignal::with()
to allow access without cloning - Memo outputs do not need to be Clone, but it now takes
impl FnMut(Option<&T>) -> T
instead ofimpl FnMut(Option<T>) -> T
I have a branch for these changes, but it's built on top of my branch for #35, so I'll open a PR for it once that gets merged.
What are the fundamental conceptual difference to sycamore?
It's great to see that there is a new Rust web frameworks like the on of yours :)
I'm very curious about what the fundamental conceptual difference is from sycamore?
Reactive system panicking at `already borrowed: BorrowMutError`
I am working on a repro, but just wanted to raise the issue.
Here is a backtrace:
panicked at 'already borrowed: BorrowMutError', C:\Users\jose2\.cargo\git\checkouts\leptos-852582868f14a87e\21d7346\leptos_reactive\src\signal.rs:726:32
Stack:
Error
at http://localhost:5173/src/wasm/wasm.js:428:21
at logError (http://localhost:5173/src/wasm/wasm.js:237:18)
at imports.wbg.__wbg_new_abda76e883ba8a5f (http://localhost:5173/src/wasm/wasm.js:427:66)
at console_error_panic_hook::Error::new::hca9359d70b7a65b0 (http://localhost:5173/src/wasm/wasm_bg.wasm:wasm-function[7886]:0x2525df)
at console_error_panic_hook::hook_impl::hd6342f4a1d3ee1fa (http://localhost:5173/src/wasm/wasm_bg.wasm:wasm-function[1491]:0x151490)
at console_error_panic_hook::hook::h5583c33e69aaa70c (http://localhost:5173/src/wasm/wasm_bg.wasm:wasm-function[8484]:0x25aaca)
at core::ops::function::Fn::call::h4709acb035a3f1ae (http://localhost:5173/src/wasm/wasm_bg.wasm:wasm-function[7565]:0x24d60d)
at std::panicking::rust_panic_with_hook::h3534cfedda92faa6 (http://localhost:5173/src/wasm/wasm_bg.wasm:wasm-function[3320]:0x1d0181)
at std::panicking::begin_panic_handler::{{closure}}::h0b26f08fa80310e7 (http://localhost:5173/src/wasm/wasm_bg.wasm:wasm-function[4244]:0x1fa6ba)
at std::sys_common::backtrace::__rust_end_short_backtrace::hbf7b83761deb4f3e (http://localhost:5173/src/wasm/wasm_bg.wasm:wasm-function[9085]:0x2624d1)
Uncaught (in promise) RuntimeError: unreachable
at __rust_start_panic (wasm_bg.wasm:0x26966c)
at rust_panic (wasm_bg.wasm:0x264e18)
at std::panicking::rust_panic_with_hook::h3534cfedda92faa6 (wasm_bg.wasm:0x1d01ac)
at std::panicking::begin_panic_handler::{{closure}}::h0b26f08fa80310e7 (wasm_bg.wasm:0x1fa6ba)
at std::sys_common::backtrace::__rust_end_short_backtrace::hbf7b83761deb4f3e (wasm_bg.wasm:0x2624d1)
at rust_begin_unwind (wasm_bg.wasm:0x23a916)
at core::panicking::panic_fmt::h855c11889f2fb721 (wasm_bg.wasm:0x25989d)
at core::result::unwrap_failed::ha83ac9268ec678d9 (wasm_bg.wasm:0x212ae5)
at core::cell::RefCell<T>::borrow_mut::h38eb0755cd71904b (wasm_bg.wasm:0x198361)
at leptos_reactive::signal::SignalId::update::h68bb1fd62dd948e3 (wasm_bg.wasm:0x3a8e7)
Server functions create possible naming conflicts
At the moment these are all defined by urls on the pattern /function_name
This works but is obviously dumb.
- You should be able to prefix them with whatever you want, so for example, you as a developer should be able to specify "all my server functions should start with
/api
" while registering them - There are obvious conflicts here. It wouldn't be a great idea but I could easily create two different functions called
my_server_fn
in two different modules and they'd both try to be served at/my_server_fn
, with stupid consequences.
The macro should prepend the current module path to the URL, and the registration function should take an optional prefix, which should feed back into the server fn itself so it updates its URL to include that prefix.
Migrate server functions back to `serde_urlencoded`
JSON-encoding each field separately using your chosen library (serde
, serde-lite
, or miniserde
) sounds like a great idea, but actually works terrible for form inputs: the strings they send, because they're not wrapped in quotes, don't parse as a JSON string. Oops.
Audit use of nightly features and gate them behind a "nightly" feature
Classes with numbers are invalid
The following valid class name does not compile because of the 25
:
view! { cx,
<span class-opacity-25=true />
}
Set up some kind of CI pipeline
I'm really ignorant about CI setups so I'd love some help with this.
Various parts of the repo use ordinary cargo test
. I also have wasm-bindgen tests set up for some of the examples.
Router docs
Consider consolidating the `class:` notation
Through experimentation, I've noticed that using CSS minification tooling that analyzes the source code for classes, such as TailwindCSS, purge CSS, and Atomic fail to recognize classes which start with class-
, but do work with classes that start with class:
due to notations from JS frameworks, such as Svelte. It might be a good idea to consolidate class names to always use class:
, instead of having two seperate notations. Ideally, this should also be extended to on:
, but is outside the scope of this issue.
These tools can be made to recognize classes prefixed by class-
, but it might not be obvious to users.
Discord
Would it be possible to create a Discord for an informal way of communicating?
I'm possibly going to use Leptos for a project that needs to launch in some months and I'd like to know my degree of insanity for even considering it ;)
Bug: append_nodes Failed
`miniserde` and `serde-lite` implementations for `Result<T, ServerFnError>`
Forcing all resources to be serializable seems overly restrictive
It seems like resources are currently forced to be serializable to support server side rendering (and potentially resumability in the future). This makes sense, but it excludes some use cases that would otherwise make sense as resources. For example, I was experimenting with accessing a webcam in leptos (using nokhwa). This is an async operation and, reading the docs, it seemed like a resource was a perfect fit. However, a webcam handle isn't serializable so I ended up with this slightly more awkward implementation:
use leptos::*;
use nokhwa::{js_camera::JSCameraConstraintsBuilder, JSCamera};
use std::{cell::RefCell, rc::Rc};
async fn get_camera(set_camera: WriteSignal<Option<Rc<RefCell<JSCamera>>>>) {
let constraints = JSCameraConstraintsBuilder::new().build();
let camera = JSCamera::new(constraints).await;
match camera {
Ok(camera) => {
log::info!("Got camera");
set_camera(Some(Rc::new(RefCell::new(camera))));
}
Err(e) => {
log::error!("Failed to get camera: {}", e);
}
}
}
#[component]
pub fn UserWebcam(cx: scope) -> Element {
let (camera, set_camera) = create_signal::<Option<Rc<RefCell<JSCamera>>>>(cx, None);
spawn_local(get_camera(set_camera));
create_effect(cx, move |_| {
if let Some(camera) = camera.get() {
if let Err(e) = (*camera).borrow_mut().attach("camera_out", false) {
log::error!("Failed to attach camera: {}", e);
} else {
log::info!("Attached camera");
}
}
});
view! {cx,
<video id="camera_out" class="user_webcam"/>
}
}
It works, but it's not ideal and it can't integrate with functionality like suspense contexts (unless I'm missing something).
It feels like the most natural solution would be to split resources into serializable and non-serializable types. Serializable resources would keep the current behavior but non-serializable ones would be handled fully on the client side regardless of rendering configuration. Happy to take a stab at implementing this if you're open to a PR, but figured I'd open an issue first to at least make sure this functionality doesn't already exist somewhere.
Create contributors' guide
Per @jquesada2016 in #53 (comment) it would be a great idea to put together a short guide to make it easier for people to contribute.
I'm curious to hear from others what sort of stuff would be useful in there.
Outline of the various packages and what goes where? General approach of each? Normal flow of execution in the program?
What would be useful?
Cannot return `<template />` from a component
The following example results in a panic:
use leptos::*;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn start() {
console_error_panic_hook::set_once();
mount_to_body(view_fn);
}
fn view_fn(cx: Scope) -> impl Mountable {
view! { cx,
<TemplateComponent></TemplateComponent>
}
}
#[component]
fn TemplateComponent(cx: Scope) -> Element {
view! { cx,
<template>
<div>"Just doing the Lord's work."</div>
</template>
}
}
This only seems to happen if directly returning a <template />
from the top level of a component.
The full stack trace is:
Uncaught (in promise) Error: `unwrap_throw` failed
at imports.wbg.__wbindgen_throw (wasm.js?t=1667929395579:292:15)
at wasm_bindgen::throw_str::hafba1a1a4c068143 (wasm_bg.wasm:0x4508d)
at <core::option::Option<T> as wasm_bindgen::UnwrapThrowExt<T>>::expect_throw::h01d589852a07341a (wasm_bg.wasm:0x3ac67)
at wasm_bindgen::UnwrapThrowExt::unwrap_throw::h568eba629f01842e (wasm_bg.wasm:0x42212)
at wasm::TemplateComponent::hb3dcae73f423c169 (wasm_bg.wasm:0x3149c)
at wasm::view_fn::{{closure}}::h573caef1eccbc7ff (wasm_bg.wasm:0x3f78f)
at leptos_reactive::scope::Scope::untrack::hf1b86e9da7ad8dd0 (wasm_bg.wasm:0x320d5)
at leptos_dom::create_component::ha566be4d30a0487a (wasm_bg.wasm:0x3fae6)
at wasm::view_fn::he21f89cb96e65078 (wasm_bg.wasm:0x392b3)
at core::ops::function::Fn::call::ha4ca8e44e588d793
Also, reading the stack trace, just a heads up, using unwrap_throw
is unsound as it leads the stack in a potentially non-reentrant state, so if a user catches the JS exception, they will most likely experience UB.
Some events not firing
Given the following code:
use leptos::*;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn start() {
mount_to_body(view_fn);
}
fn view_fn(cx: Scope) -> impl Mountable {
let (focus, set_focus) = create_signal(cx, false);
let focused = move || {
if focus() {
view! { cx, <p></p> }
} else {
view! { cx, <span /> }
}
};
view! { cx,
<div>
<input on:focus=move |_| set_focus(true) on:blur=move |_| set_focus(false) />
{focused}
</div>
}
}
on:focus
and on:blur
never fire. on:click
does, however.
Check safety of lifetime transmute in add_event_listener
I'm pretty sure this is actually both unsound and unnecessary; we want to force people to move signals into the closure anyway.
Refactor HTML streaming from examples into a reusable form
Need to escape values before inserting them in HTML for SSR
`class:` and `class-` shorthands are not stripping `class` prefix
Writting the following:
view! { cx,
<div class:hidden=true />
}
results in
<div class="class:hidden" ></div>
Same for the class-
variant.
`view!` supporting `Option` on attributes, props, and event listeners
Often when receiving optional props, it would be really convenient to allow the macro to accept option types for event handlers and such. This would also allow conditionally attaching event listeners which can become performance hits on components which are heavily reused.
Add on_cleanup to new reactivity and document it
Add more helpful error messages in `leptos_reactive`
Much of the reactive system is built on RefCell
, which for some reason gives really unhelpful error messages in the browser, even in debug mode, because it doesn't give line numbers as it does in the console. (At least for me. Chrome tends to be better than Firefox with Wasm errors, but still not great.)
I usually don't see panic messages in Wasm error output either, so I have a debug_warn!
macro in leptos_reactive
that uses println!
syntax to log a warning to the console but is stripped out in release builds for bundle size.
It would be good to replace all the borrow_mut()
a in leptos_reactive
with try_borrow_mut()
s that catch and log an appropriate error with debug_warn!
Some of these don't even need to panic: for example if you're trying to run an effect or update a signal they can log the warning and then do nothing. Reading a signal that has been disposed does need to panic (unless someone has a better solution), because otherwise there's no value to return.
This helps with debugging issues like #25
Components are rendering out of order when mixed with HTML elements
Using version 0.0.15
, the following results in out of order rending:
use leptos::*;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn start() {
mount_to_body(view_fn);
}
fn view_fn(cx: Scope) -> impl Mountable {
view! { cx,
<form>
<TextInput />
<button>"Clcik me?"</button>
</form>
}
}
#[component]
fn TextInput(cx: Scope) -> Element {
view! {cx,
<input />
}
}
If I inline the component, i.e, <form><input /> ...
, then it works, if I remove the surrounding <form>
then it also renders correctly.
ActionForm needs to update input and value on its server fn action
Or just actually build the server fn type and dispatch the action.
Loaders for all routes reload on any navigation
See router example. Contacts list should never reload, but reloads when any navigation happens.
Examples are excluded from workspace
Examples are excluded from the workspace and this makes rust-analyzer skip them on my machine. Do you use a special setup to circumvent that, or is this an oversight? While I don't have much experience with setting up rust projects, I feel like examples should simply be members of the workspace.
Add `Resource::cancel` so we can abort running futures if we want
There's an on_cleanup
function that can be used to run arbitrary code before a scope is disposed, e.g., before a row is dropped in a <For/>
component or before a nested route is disposed in the router (because you're navigating away from it).
It would be awesome to have a Resource::cancel()
available so you can do things like abort in-flight fetch
requests, etc.
I've seen plenty of Future
s that have some kind of cancel/abort feature. For example, the oneshot
channel in the futures
crate returns Result<Option<T>, Canceled>
.
Does anyone know there's a common way to indicate a cancellable future? I'm imagining, for example, a Cancellable
trait so that we have something like
pub fn create_cancellable_resource<S, T, Fu>(
cx: Scope,
source: impl Fn() -> S + 'static,
fetcher: impl Fn(S) -> Fu + 'static,
) -> Resource<S, T>
where
S: PartialEq + Debug + Clone + 'static,
T: Debug + Clone + Serialize + DeserializeOwned + 'static,
Fu: ***Cancellable*** + Future<Output = T> + 'static,
{
}
I'm realizing as I write this that, because the Resource
struct isn't generic over the type of its Future
at the moment (only create_resource
is, then it stores it as a Pin<Box<dyn Future ...>>
or somesuch) this is a little cumbersome.
I guess I could just define my own Cancellable
trait for people to implement, but I'd love to have something from the wider Rust ecosystem if it exists.
`Element` mixed with `Vec<Element>` causes nothing to render
The following example renders nothing, I assume this is related to #53.
use leptos::*;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn start() {
mount_to_body(view_fn);
}
fn view_fn(cx: Scope) -> impl Mountable {
let (sig, _) = create_signal(cx, view! { cx, <span /> });
let data = || {
view! { cx,
<>
<h1>"1"</h1>
<h1>"2"</h1>
<h1>"3"</h1>
</>
}
};
view! { cx,
<span>
{data} // <-- wrapping this in a <span /> fixes rendering
<h1>"4"</h1>
</span>
}
}
Test bundle size effects of using serde_lite instead of serde for Resources
Deal with class: attributes in SSR rather than ignoring them
lazy loading and resumability ala qwik
consider adopting ideas from qwik framework.
leverage seralization on the client to enable:
- lazy loading components - makes the app a lot faster to start
- resumability between sessions - better user experience
i believe qwik is bringing some inovative ideas into the web app world and since this project is still in early development you can still possibly incorporate them.
Loader API
Loaders should probably be something more like the following. This allows them to do fine-grained reactive reloads only in response to particular params or queries on which they actually depend. Currently, they do a Remix-style “reload whenever query or params change,” which is not great. The main issue here is establishing a stable URL for the create_server_resource
(or whatever) so that the loader runs on either client or server, but the server resource runs only on server and can be accessed using a JSON endpoint.
fn contact_data(cx: Scope) -> Resource<String, Contact>{
let params = use_params_map(cx);
let id = create_memo(cx, |_| params.get("id"));
create_server_resource(cx, move || id(), |id: String| fetch_contact_data_for_user(&id))
}
Only elements are allowed at the top level of `view! {}` macro
The following code is allowed:
use leptos::*;
use wasm_bindgen::prelude::*;
#[wasm_bindgen(start)]
pub fn start() {
mount_to_body(view_fn);
}
fn view_fn(cx: Scope) -> impl Mountable {
let (focus, set_focus) = create_signal(cx, false);
let focused = move || {
if focus() {
view! { cx, <p></p> }
} else {
view! { cx, <span /> }
}
};
view! { cx,
<div>
<input on:focus=move |_| set_focus(true) on:blur=move |_| set_focus(false) />
{focused}
</div>
}
}
but the following is not:
view! { cx,
<input on:focus=move |_| set_focus(true) on:blur=move |_| set_focus(false) />
{focused}
}
I do understand why, because the macro expects these items to be Element
rather than impl IntoElement
, but thought it was worth mentioning in case this was not intentional.
Document syntax differences between stable and nightly
Update to `syn-rsx` 0.9 to allow things like `on:my-event`
Currently the syn-rsx
crate doesn't allow mixing colons and dashes in attribute names, which breaks the on:
syntax in custom event names, which are encouraged to have a dash like my-event
.
I had a PR merged to syn-rsx
that would allow this, but there are also some other breaking changes in their 0.9 release, so I'll need to make sure we're up to date.
Use `rustversion` crate instead of `stable` flag
We can enable use on either stable or nightly Rust by detecting version with the rustversion
crate instead of using the stable
flag. This should be fairly simple... only leptos_reactive
and leptos_dom
need to change at all, and then have the feature removed in the main leptos
package.
"Action" on a Form component should be dynamic
Just like the href property on an A component, Form action should take an IntoHref type, so it can update dynamically. This is important for making sure relative routes work if params change, because the form is not rerendered.
Migrate examples to single-folder structure
Some of the examples (that use SSR + hydration) are divided into three directories: one for the server, one for the client, and one for the app. This used to be necessary to allow you to compile the hydration code to Wasm and the SSR code for the server without causing feature conflicts.
This problem no longer exists because the feature flags are no longer contradictory. So the hackernews
and counter_isomorphic
examples can be rewritten to use a single-create/single-directory structure instead.
See the model here.
Bug: Leptos_DOM Child.rs IntoChild Trait Build Error
Tried to simply build everything with cargo build
from root directory:
It builds by itself:
cd leptos_dom
cargo build
Seems to be the hackernews and todomvc-ssr examples.
Edit: ^ nvm, i just wasn't building hackernews and todomvc-ssr subdirectories individually
`Form::from_action`
Implement a helper method on the <Form/>
component to derive it automatically from an action, using its (server function) URL as the form
's action, updating the action's version
when the form
submits, and sending errors to the action's error field.
Remove transitions and broken transition example
SSR benchmarks
I'd love to see some simple benchmarks of server-side rendering performance across different Rust frameworks (Leptos, Sycamore, Yew, Dioxus?) using cargo bench
This could be something fairly simple: as long as it involves two or three components, maybe a <For/>
component or other relevant keyed list. If it's too simple (like just rendering a simple large static template) it will be very unfair, since Leptos would probably compile it to a single string already.
Is it possible to condense the three-directory server/client/app model into one?
We've been talking on Discord a little about this.
I think it should be possible, now that the csr
/ssr
/hydrate
features aren't mutually exclusive, to write a complete server-rendered-and-browser-hydrated app in a single directory.
Browser/specific code can compile on the server, but not run. Server code usually can't compile for Wasm.
You'd need to use #[wasm_bindgen]
to annotate the function you are using to hydrate the app (i.e., basically the few lines of code that are in hackernews-client
), and call that function from the HTML you're generating on the server.
Then put the actual server code behind a #[cfg(feature = "ssr")]
flag so that it doesn't try to compile actix-web
or whatever to Wasm.
Bonus points if it's also Trunk-compatible for an easy dev-mode client-side-rendered version.
If someone wants to try it out in the hackernews
directory and submit a PR I'd give them major kudos. @akesson it seems like this may be up your alley as you're working on cargo-leptos
too... Let me know if you're interested in giving it a try.
Docs for Resource, including streaming behavior
Fix hackernews example to use new router APIs
Route actions (i.e., POST)
Currently these are just a stub... I'd like to figure them out so the router is usable for both circumstances, but I want to take some time to make sure the API is right.
`<Form method="POST"/>`
Implement progressively-enhanced POST
for forms in leptos_router
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.