mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-27 14:40:44 +00:00
Merge pull request #163 from DioxusLabs/jk/release-02
Release post for Dioxus v0.2
This commit is contained in:
commit
ad5c627ee9
7 changed files with 1408 additions and 0 deletions
3
.vscode/spellright.dict
vendored
3
.vscode/spellright.dict
vendored
|
@ -80,3 +80,6 @@ oninput
|
|||
Webview
|
||||
idanarye
|
||||
Katex
|
||||
mrxiaozhuox
|
||||
Fruitie
|
||||
Vercel
|
||||
|
|
3
docs/posts/allocators.md
Normal file
3
docs/posts/allocators.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# WASM Performance
|
||||
|
||||
This article is about WASM performance.
|
0
docs/posts/design_in_depth.md
Normal file
0
docs/posts/design_in_depth.md
Normal file
306
docs/posts/release-0-2-0.md
Normal file
306
docs/posts/release-0-2-0.md
Normal file
|
@ -0,0 +1,306 @@
|
|||
# Dioxus v0.2 Release: Router, State Management, and Tooling
|
||||
|
||||
> Jan 26, 2022
|
||||
|
||||
> [@jkelleyrtp](https://github.com/jkelleyrtp)
|
||||
|
||||
Thanks to these amazing folks for their financial support on OpenCollective:
|
||||
|
||||
- [@t1m0t](https://github.com/t1m0t)
|
||||
- [@alexkirsz](https://github.com/t1m0t)
|
||||
- [@freopen](https://github.com/freopen)
|
||||
- [@DannyMichaels](https://github.com/DannyMichaels)
|
||||
- [@SweetLittleMUV](https://github.com/Fatcat560)
|
||||
|
||||
|
||||
Thanks to these amazing folks for their code contributions:
|
||||
|
||||
[@mrxiaozhuox](https://github.com/mrxiaozhuox)
|
||||
[@autarch](https://github.com/autarch)
|
||||
[@FruitieX](https://github.com/FruitieX)
|
||||
[@t1m0t](https://github.com/t1m0t)
|
||||
[@Demonthos](https://github.com/Demonthos)
|
||||
[@oovm](https://github.com/oovm)
|
||||
[@6asaaki](https://github.com/6asaaki)
|
||||
|
||||
|
||||
Just over two months in, and we already a ton of awesome changes to Dioxus!
|
||||
|
||||
Dioxus is a recently-released library for building interactive user interfaces (GUI) with Rust. It is built around a Virtual DOM, making it portable for the web, desktop, server, mobile, and more. Dioxus looks and feels just like React, so if you know React, then you'll feel right at home.
|
||||
|
||||
```rust
|
||||
fn app(cx: Scope) -> Element {
|
||||
let mut count = use_state(&cx, || 0);
|
||||
|
||||
cx.render(rsx! {
|
||||
h1 { "Count: {count}" }
|
||||
button { onclick: move |_| count += 1, "+" }
|
||||
button { onclick: move |_| count -= 1, "-" }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
# What's new?
|
||||
|
||||
A *ton* of stuff happened in this release; 109 commits, 10 contributors, 2 minor releases, and 1 backer on Open Collective (!!!).
|
||||
|
||||
The TLDR of the major features:
|
||||
|
||||
- We now can render into the terminal, similar to Ink.JS - a huge thanks to [@Demonthos](https://github.com/Demonthos)
|
||||
- We have a new router in the spirit of React-Router [@autarch](https://github.com/autarch)
|
||||
- We now have Fermi for global state management in the spirit of [Recoil.JS](https://recoiljs.org)
|
||||
- Our desktop platform got major upgrades, getting closer to parity with Electron [@mrxiaozhuox](https://github.com/mrxiaozhuox)
|
||||
- Our CLI tools now support HTML-to-RSX translation for converting 3rd party HTML into Dioxus [@mrxiaozhuox](https://github.com/mrxiaozhuox)
|
||||
- Dioxus-Web is sped up by 2.5x with JS-based DOM manipulation (3x faster than React)
|
||||
|
||||
We also fixed and improved a bunch of stuff - check out the full list down below.
|
||||
|
||||
|
||||
## A New Renderer: Your terminal!
|
||||
|
||||
When Dioxus was initially released, we had very simple support for logging Dioxus elements out as TUI elements. In the past month or so, [@Demonthos](https://github.com/Demonthos) really stepped up and made the new crate a reality.
|
||||
|
||||
|
||||
[Imgur](https://i.imgur.com/GL7uu3r.png)
|
||||
|
||||
The new TUI renderer even supports mouse movements, keyboard input, async tasks, borders, and a ton more.
|
||||
|
||||
<blockquote class="imgur-embed-pub" lang="en" data-id="a/49n7Hj2" data-context="false" ><a href="//imgur.com/a/49n7Hj2"></a></blockquote><script async src="//s.imgur.com/min/embed.js" charset="utf-8"></script>
|
||||
|
||||
|
||||
|
||||
## New Router
|
||||
|
||||
We totally revamped the router, switching away from the old yew-router approach to the more familiar [React-Router](http://reactrouter.com). It's less type-safe but provides more flexibility and support for beautiful URLs.
|
||||
|
||||
Apps with routers are *really* simple now. It's easy to compose the "Router", a "Route", and "Links" to define how your app is laid out:
|
||||
|
||||
```rust
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
Router {
|
||||
onchange: move |_| log::info!("Route changed!"),
|
||||
ul {
|
||||
Link { to: "/", li { "Go home!" } }
|
||||
Link { to: "users", li { "List all users" } }
|
||||
Link { to: "blog", li { "Blog posts" } }
|
||||
}
|
||||
Route { to: "/", "Home" }
|
||||
Route { to: "/users", "User list" }
|
||||
Route { to: "/users/:name", User {} }
|
||||
Route { to: "/blog", "Blog list" }
|
||||
Route { to: "/blog/:post", BlogPost {} }
|
||||
Route { to: "", "Err 404 Route Not Found" }
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
We're also using hooks to parse the URL parameters and segments so you can interact with the router from anywhere deeply nested in your app.
|
||||
|
||||
```rust
|
||||
#[derive(Deserialize)]
|
||||
struct Query { name: String }
|
||||
|
||||
fn BlogPost(cx: Scope) -> Element {
|
||||
let post = use_route(&cx).segment("post")?;
|
||||
let query = use_route(&cx).query::<Query>()?;
|
||||
|
||||
cx.render(rsx!{
|
||||
"Viewing post {post}"
|
||||
"Name selected: {query}"
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Give a big thanks to [@autarch](https://github.com/autarch) for putting in all the hard work to make this new router a reality.
|
||||
|
||||
The Router guide is [available here](https://dioxuslabs.com/nightly/router/) - thanks to [@dogedark](https://github.com/dogedark).
|
||||
|
||||
## Fermi for Global State Management
|
||||
|
||||
Managing state in your app can be challenging. Building global state management solutions can be even more challenging. For the first big attempt at building a global state management solution for Dioxus, we chose to keep it simple and follow in the footsteps of the [Recoil.JS](http://recoiljs.org) project.
|
||||
|
||||
Fermi uses the concept of "Atoms" for global state. These individual values can be get/set from anywhere in your app. Using state with Fermi is basically as simple as `use_state`.
|
||||
|
||||
```rust
|
||||
// Create a single value in an "Atom"
|
||||
static TITLE: Atom<&str> = |_| "Hello";
|
||||
|
||||
// Read the value from anywhere in the app, subscribing to any changes
|
||||
fn app(cx: Scope) -> Element {
|
||||
let title = use_read(&cx, TITLE);
|
||||
cx.render(rsx!{
|
||||
h1 { "{title}" }
|
||||
Child {}
|
||||
})
|
||||
}
|
||||
|
||||
// Set the value from anywhere in the app
|
||||
fn Child(cx: Scope) -> Element {
|
||||
let set_title = use_set(&cx, TITLE);
|
||||
cx.render(rsx!{
|
||||
button {
|
||||
onclick: move |_| set_title("goodbye"),
|
||||
"Say goodbye"
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Inline Props Macro
|
||||
|
||||
For internal components, explicitly declaring props structs can become tedious. That's why we've built the new `inline_props` macro. This macro lets you inline your props definition right into your component function arguments.
|
||||
|
||||
Simply add the `inline_props` macro to your component:
|
||||
```rust
|
||||
#[inline_props]
|
||||
fn Child<'a>(
|
||||
cx: Scope,
|
||||
name: String,
|
||||
age: String,
|
||||
onclick: EventHandler<'a, ClickEvent>
|
||||
) -> Element {
|
||||
cx.render(rsx!{
|
||||
button {
|
||||
"Hello, {name}"
|
||||
"You are {age} years old"
|
||||
onclick: move |evt| onclick.call(evt)
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
You won't be able to document each field or attach attributes so you should refrain from using it in libraries.
|
||||
|
||||
## Props optional fields
|
||||
|
||||
Sometimes you don't want to specify *every* value in a component's props, since there might a lot. That's why the `Props` macro now supports optional fields. You can use a combination of `default`, `strip_option`, and `optional` to tune the exact behavior of properties fields.
|
||||
|
||||
```rust
|
||||
#[derive(Props, PartialEq)]
|
||||
struct ChildProps {
|
||||
#[props(default = "client")]
|
||||
name: String,
|
||||
|
||||
#[props(default)]
|
||||
age: Option<u32>,
|
||||
|
||||
#[props(optional)]
|
||||
age: Option<u32>,
|
||||
}
|
||||
|
||||
// then to use the accompanying component
|
||||
rsx!{
|
||||
Child {
|
||||
name: "asd",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Dioxus Web Speed Boost
|
||||
|
||||
We've changed how DOM patching works in Dioxus-Web; now, all of the DOM manipulation code is written in TypeScript and shared between our web, desktop, and mobile runtimes.
|
||||
|
||||
On an M1-max, the "create-rows" operation used to take 45ms. Now, it takes a mere 17ms - 3x faster than React. We expect an upcoming optimization to bring this number as low as 3ms.
|
||||
|
||||
Under the hood, we have a new string interning engine to cache commonly used tags and values on the Rust <-> JS boundary, resulting in significant performance improvements.
|
||||
|
||||
Overall, Dioxus apps are even more snappy than before.
|
||||
|
||||
|
||||
## Dioxus Desktop Window Context
|
||||
|
||||
A very welcome change, thanks AGAIN to [@mrxiaozhuox](https://github.com/mrxiaozhuox) is support for imperatively controlling the desktop window from your Dioxus code.
|
||||
|
||||
A bunch of new methods were added:
|
||||
- Minimize and maximize window
|
||||
- Close window
|
||||
- Focus window
|
||||
- Enable devtools on the fly
|
||||
|
||||
And more!
|
||||
|
||||
In addition, Dioxus Desktop now autoresolves asset locations, so you can easily add local images, JS, CSS, and then bundle it into an .app without hassle.
|
||||
|
||||
You can now build entirely borderless desktop apps:
|
||||
|
||||
[img](https://i.imgur.com/97zsVS1.png)
|
||||
|
||||
<!-- ## VSCode Extension
|
||||
|
||||
To make life easier and improve your development experience, we've launched the first iteration of the official Dioxus VSCode extension. If you're not using VSCode, you can still take advantage of these new features through the CLI tool.
|
||||
|
||||
Included in the new extension is:
|
||||
|
||||
- Auto-formatting of `rsx!` blocks
|
||||
- Convert selection of HTML to RSX
|
||||
- Extract RSX as component
|
||||
|
||||
[To install the extension, go here](https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer).
|
||||
|
||||
The VSCode extension is really easy to contribute to and has tons of potential. This is a great place to start contributing to the Dioxus project *and* improve your development experience. -->
|
||||
|
||||
## CLI Tool
|
||||
|
||||
Thanks to the amazing work by [@mrxiaozhuox](https://github.com/mrxiaozhuox), our CLI tool is fixed and working better than ever. The Dioxus-CLI sports a new development server, an HTML to RSX translation engine, a `cargo fmt`-style command, a configuration scheme, and much more.
|
||||
|
||||
Unlike its counterpart, `Trunk.rs`, the dioxus-cli supports running examples and tests, making it easier to test web-based projects and showcase web-focused libraries.
|
||||
|
||||
## All New Features
|
||||
|
||||
- [x] A new router @autarch
|
||||
- [x] Fermi for global state management
|
||||
- [x] Translation of docs and Readme into Chinese @mrxiaozhuox
|
||||
- [x] 2.5x speedup by using JS-based DOM manipulation (3x faster than React)
|
||||
- [x] Beautiful documentation overhaul
|
||||
- [x] InlineProps macro allows definition of props within a component's function arguments
|
||||
- [x] Improved dev server, hot reloading for desktop and web apps [@mrxiaozhuox](https://github.com/mrxiaozhuox)
|
||||
- [x] Templates: desktop, web, web/hydration, Axum + SSR, and more [@mrxiaozhuox](https://github.com/mrxiaozhuox)
|
||||
- [x] Web apps ship with console_error_panic_hook enabled, so you always get tracebacks
|
||||
- [x] Enhanced Hydration and server-side-rendering
|
||||
- [x] Optional fields for component properties
|
||||
- [x] Introduction of the `EventHandler` type
|
||||
- [x] Improved use_state hook to be closer to react
|
||||
- [x] Improved use_ref hook to be easier to use in async contexts
|
||||
- [x] New use_coroutine hook for carefully controlling long-running async tasks
|
||||
- [x] Prevent Default attribute
|
||||
- [x] Provide Default Context allows injection of global contexts to the top of the app
|
||||
- [x] push_future now has a spawn counterpart to be more consistent with rust
|
||||
- [x] Add gap and gap_row attributes [@FruitieX](https://github.com/FruitieX)
|
||||
- [x] File Drag n Drop support for Desktop
|
||||
- [x] Custom handler support for desktop
|
||||
- [x] Forms now collect all their values in oninput/onsubmit
|
||||
- [x] Async tasks now are dropped when components unmount
|
||||
- [x] Right-click menus are now disabled by default
|
||||
|
||||
## Fixes
|
||||
- [x] Windows support improved across the board
|
||||
- [x] Linux support improved across the board
|
||||
- [x] Bug in Calculator example
|
||||
- [x] Improved example running support
|
||||
|
||||
A ton more! Dioxus is now much more stable than it was at release!
|
||||
|
||||
## Community Additions
|
||||
- [Styled Components macro](https://github.com/Zomatree/Revolt-Client/blob/master/src/utils.rs#14-27) [@Zomatree](https://github.com/Zomatree)
|
||||
- [Dioxus-Websocket hook](https://github.com/FruitieX/dioxus-websocket-hooks) [@FruitieX](https://github.com/FruitieX)
|
||||
- [Home automation server app](https://github.com/FruitieX/homectl) [@FruitieX](https://github.com/FruitieX)
|
||||
- [Video Recording app](https://github.com/rustkid/recorder)
|
||||
- [Music streaming app](https://github.com/autarch/Crumb/tree/master/web-frontend) [@autarch](https://github.com/autarch)
|
||||
- [NixOS dependency installation](https://gist.github.com/FruitieX/73afe3eb15da45e0e05d5c9cf5d318fc) [@FruitieX](https://github.com/FruitieX)
|
||||
- [Vercel Deploy Template](https://github.com/lucifer1004/dioxus-vercel-demo) [@lucifer1004](https://github.com/lucifer1004)
|
||||
- [Render Katex in Dioxus](https://github.com/oovm/katex-wasm)
|
||||
- [Render PrismJS in Dioxus](https://github.com/oovm/prism-wasm)
|
||||
- [Compile-time correct TailwindCSS](https://github.com/houseabsolute/tailwindcss-to-rust)
|
||||
- [Autogenerate tailwind CSS](https://github.com/oovm/tailwind-rs)
|
||||
- [Heroicons library](https://github.com/houseabsolute/dioxus-heroicons)
|
||||
- [RSX -> HTML translator app](https://dioxus-convert.netlify.app)
|
||||
- [Toast Support](https://github.com/mrxiaozhuox/dioxus-toast)
|
||||
- New Examples: forms, routers, linking, tui, and more!
|
||||
|
||||
Looking Forward
|
||||
---
|
||||
|
||||
Contributors
|
||||
---
|
408
docs/posts/release.md
Normal file
408
docs/posts/release.md
Normal file
|
@ -0,0 +1,408 @@
|
|||
# Introducing Dioxus v0.1 ✨
|
||||
|
||||
> Jan 3, 2022
|
||||
|
||||
> [@jkelleyrtp](https://github.com/jkelleyrtp), thanks [@alexkirsz](https://github.com/alexkirsz)
|
||||
|
||||
After many months of work, we're very excited to release the first version of Dioxus!
|
||||
|
||||
Dioxus is a new library for building interactive user interfaces (GUI) with Rust. It is built around a Virtual DOM, making it portable for the web, desktop, server, mobile, and more.
|
||||
|
||||
Dioxus has the following design goals:
|
||||
|
||||
- **Familiar**: Offer a React-like mental model and API surface
|
||||
- **Robust**: Avoid runtime bugs by moving rules and error handling into the type system
|
||||
- **Performant**: Scale to the largest apps and the largest teams
|
||||
- **Productive**: Comprehensive inline documentation, fast recompiles, and deeply integrated tooling
|
||||
- **Extensible**: Reusable hooks and components that work on every platform
|
||||
|
||||
Dioxus is designed to be familiar for developers already comfortable with React paradigms. Our goal is to ensure a smooth transition from TypeScript/React without having to learn any major new concepts.
|
||||
|
||||
To give you an idea of what Dioxus looks like, here's a simple counter app:
|
||||
|
||||
```rust
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus::desktop::launch(app)
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let mut count = use_state(&cx, || 0);
|
||||
|
||||
cx.render(rsx! {
|
||||
h1 { "Count: {count}" }
|
||||
button { onclick: move |_| count += 1, "+" }
|
||||
button { onclick: move |_| count -= 1, "-" }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
This simple counter is a complete desktop application, running at native speeds on a native thread. Dioxus automatically shuttles all events from the WebView runtime into the application code. In our app, we can interact natively with system APIs, run multi-threaded code, and do anything a regular native Rust application might do. Running `cargo build --release` will compile a portable binary that looks and feels the same on Windows, macOS, and Linux. We can then use `cargo-bundle` to bundle our binary into a native `.app`/`.exe`/`.deb`.
|
||||
|
||||
Dioxus supports many of the same features React does including:
|
||||
|
||||
- Server-side-rendering, pre-rendering, and hydration
|
||||
- Mobile, desktop, and web support
|
||||
- Suspense, fibers, coroutines, and error handling
|
||||
- Hooks, first-class state management, components
|
||||
- Fragments, conditional rendering, and custom elements
|
||||
|
||||
However, some things are different in Dioxus:
|
||||
|
||||
- Automatic memoization (opt-out rather than opt-in)
|
||||
- No effects - effectual code can only originate from actions or coroutines
|
||||
- Suspense is implemented as hooks - _not_ deeply ingrained within Dioxus Core
|
||||
- Async code is _explicit_ with a preference for _coroutines_ instead
|
||||
|
||||
As a demo, here's our teaser example running on all our current supported platforms:
|
||||
|
||||
![Teaser Example](/static/Untitled.png)
|
||||
|
||||
This very site is built with Dioxus, and the source code is available [here](https://github.com/dioxuslabs/docsite).
|
||||
|
||||
To get started with Dioxus, check out any of the "Getting Started" guides for your platform of choice, or check out the GitHub Repository for more details.
|
||||
|
||||
- [Getting Started with Dioxus](https://dioxuslabs.com/guide)
|
||||
- [Getting Started with Web](https://dioxuslabs.com/reference/web)
|
||||
- [Getting Started with Desktop](https://dioxuslabs.com/reference/desktop)
|
||||
- [Getting Started with Mobile](https://dioxuslabs.com/reference/mobile)
|
||||
- [Getting Started with SSR](https://dioxuslabs.com/reference/ssr)
|
||||
|
||||
## Show me some examples of what can be built!
|
||||
|
||||
- [File explorer desktop app](https://github.com/dioxuslabs/example-projects)
|
||||
- [WiFi scanner desktop app](https://github.com/dioxuslabs/example-projects)
|
||||
- [Dog CEO API Search](https://github.com/dioxuslabs/example-projects)
|
||||
- [TodoMVC Mobile App](https://github.com/dioxuslabs/example-projects)
|
||||
- [E-Commerce Liveview App](https://github.com/dioxuslabs/example-projects)
|
||||
|
||||
## Why should I use Rust and Dioxus for frontend?
|
||||
|
||||
We believe that Rust's ability to write high-level and statically typed code should make it easier for frontend teams to take on even the most ambitious of projects. Rust projects can be refactored fearlessly: the powerful type system prevents an entire class of bugs at compile-time. No more `cannot read property of undefined` ever again! With Rust, all errors must be accounted for at compile time. You cannot ship an app that does not — in some way — handle its errors.
|
||||
|
||||
### Difference from TypeScript/React:
|
||||
|
||||
TypeScript is still fundamentally JavaScript. If you've written enough TypeScript, you might be bogged down with lots of configuration options, lack of proper support for "go-to-source," or incorrect ad-hoc typing. With Rust, strong types are built-in, saving tons of headache like `cannot read property of undefined`.
|
||||
|
||||
By using Rust, we gain:
|
||||
|
||||
- Strong types for every library
|
||||
- Immutability by default
|
||||
- A simple and intuitive module system
|
||||
- Integrated documentation (go to source actually goes to source instead of the `.d.ts` file)
|
||||
- Advanced pattern matching
|
||||
- Clean, efficient, composable iterators
|
||||
- Inline built-in unit/integration testing
|
||||
- High quality error handling
|
||||
- Flexible standard library and traits
|
||||
- Powerful macro system
|
||||
- Access to the [crates.io](https://crates.io) ecosystem
|
||||
|
||||
Dioxus itself leverages this platform to provide the following guarantees:
|
||||
|
||||
- Correct use of immutable data structures
|
||||
- Guaranteed handling of errors and null-values in components
|
||||
- Native performance on mobile
|
||||
- Direct access to system IO
|
||||
|
||||
And much more. Dioxus makes Rust apps just as fast to write as React apps, but affords more robustness, giving your frontend team greater confidence in making big changes in shorter timespans.
|
||||
|
||||
Semantically, TypeScript-React and Rust-Dioxus are very similar. In TypeScript, we would declare a simple component as:
|
||||
|
||||
```tsx
|
||||
type CardProps = {
|
||||
title: string,
|
||||
paragraph: string,
|
||||
};
|
||||
|
||||
const Card: FunctionComponent<CardProps> = (props) => {
|
||||
let [count, set_count] = use_state(0);
|
||||
return (
|
||||
<aside>
|
||||
<h2>{props.title}</h2>
|
||||
<p> {props.paragraph} </p>
|
||||
<button onclick={() => set_count(count + 1)}> Count {count} </button>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
In Dioxus, we would define the same component in a similar fashion:
|
||||
|
||||
```rust
|
||||
#[derive(Props, PartialEq)]
|
||||
struct CardProps {
|
||||
title: String,
|
||||
paragraph: String
|
||||
}
|
||||
|
||||
static Card: Component<CardProps> = |cx| {
|
||||
let mut count = use_state(&cx, || 0);
|
||||
cx.render(rsx!(
|
||||
aside {
|
||||
h2 { "{cx.props.title}" }
|
||||
p { "{cx.props.paragraph}" }
|
||||
button { onclick: move |_| count+=1, "Count: {count}" }
|
||||
}
|
||||
))
|
||||
};
|
||||
```
|
||||
|
||||
However, we recognize that not every project needs Rust - many are fine with JavaScript! We also acknowledge that Rust/Wasm/Dioxus does not fix "everything that is wrong with frontend development." There are always going to be new patterns, frameworks, and languages that solve these problems better than Rust and Dioxus.
|
||||
|
||||
As a general rule of thumb, Dioxus is for you if:
|
||||
|
||||
- your app will become very large
|
||||
- you need to share code across many platforms
|
||||
- you want a fast way to build for desktop
|
||||
- you want to avoid electron or need direct access to hardware
|
||||
- you're tired of JavaScript tooling
|
||||
|
||||
Today, to publish a Dioxus app, you don't need NPM/WebPack/Parcel/etc. Dioxus simply builds with cargo, and for web builds, Dioxus happily works with the popular [trunk](http://trunkrs.dev) project.
|
||||
|
||||
## Show me more
|
||||
|
||||
Here, we'll dive into some features of Dioxus and why it's so fun to use. The [guide](https://dioxuslabs.com/guide/) serves as a deeper and more comprehensive look at what Dioxus can do.
|
||||
|
||||
## Building a new project is simple
|
||||
|
||||
To start a new project, all you need is Cargo, which comes with Rust. For a simple desktop app, all we'll need is the `dioxus` crate with the appropriate `desktop` feature. We start by initializing a new binary crate:
|
||||
|
||||
```shell
|
||||
$ cargo init dioxus_example
|
||||
$ cd dioxus_example
|
||||
```
|
||||
|
||||
We then add a dependency on Dioxus to the `Cargo.toml` file, with the "desktop" feature enabled:
|
||||
|
||||
```rust
|
||||
[dependencies]
|
||||
dioxus = { version = "*", features = ["desktop"] }
|
||||
```
|
||||
|
||||
We can add our counter from above.
|
||||
|
||||
```rust
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus::desktop::launch(app)
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
let mut count = use_state(&cx, || 0);
|
||||
|
||||
cx.render(rsx! {
|
||||
h1 { "Count: {count}" }
|
||||
button { onclick: move |_| count += 1, "+" }
|
||||
button { onclick: move |_| count -= 1, "-" }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
And voilà! We can `cargo run` our app
|
||||
|
||||
|
||||
![Simple Counter Desktop App](/static/counter.png)
|
||||
|
||||
## Support for JSX-style templating
|
||||
|
||||
Dioxus ships with a templating macro called RSX, a spin on React's JSX. RSX is very similar to regular struct syntax for Rust so it integrates well with your IDE. If used with [Rust-Analyzer](https://github.com/rust-analyzer/rust-analyzer) (not tested anywhere else) RSX supports code-folding, block selection, bracket pair colorizing, autocompletion, symbol renaming — pretty much anything you would expect from writing regular struct-style code.
|
||||
|
||||
```rust
|
||||
rsx! {
|
||||
div { "Hello world" }
|
||||
button {
|
||||
onclick: move |_| log::info!("button pressed"),
|
||||
"Press me"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If macros aren't your style, you can always drop down to the factory API:
|
||||
|
||||
```rust
|
||||
LazyNodes::new(|f| {
|
||||
f.fragment([
|
||||
f.element(div, [f.text("hello world")], [], None, None)
|
||||
f.element(
|
||||
button,
|
||||
[f.text("Press Me")],
|
||||
[on::click(move |_| log::info!("button pressed"))],
|
||||
None,
|
||||
None
|
||||
)
|
||||
])
|
||||
})
|
||||
```
|
||||
|
||||
The `rsx!` macro generates idiomatic Rust code that uses the factory API — no different than what you'd write by hand yourself.
|
||||
|
||||
To make it easier to work with RSX, we've built a small [VSCode extension](https://github.com/DioxusLabs/studio) with useful utilities. This extension provides a command that converts a selected block of HTML into RSX so you can easily reuse existing web templates.
|
||||
|
||||
## Dioxus prioritizes developer experience
|
||||
|
||||
Many of the Rust UI frameworks are particularly difficult to work with. Even the ones branded as "ergonomic" are quite challenging to in comparison to TSX/JSX. With Dioxus, we've innovated on a number of Rust patterns to deliver a framework that is actually enjoyable to develop in.
|
||||
|
||||
For example, many Rust frameworks require you to clone your data in for *every* closure and handler you use. This can get really clumsy for large apps.
|
||||
|
||||
```rust
|
||||
div()
|
||||
.children([
|
||||
button().onclick(cloned!(name, date, age, description => move |evt| { /* */ })
|
||||
button().onclick(cloned!(name, date, age, description => move |evt| { /* */ })
|
||||
button().onclick(cloned!(name, date, age, description => move |evt| { /* */ })
|
||||
])
|
||||
```
|
||||
|
||||
Dioxus understands the lifetimes of data borrowed from `Scope`, so you can safely return any borrowed data without declaring explicit captures. Hook handles all implement `Copy` so they can be shared between listeners without any ceremony.
|
||||
|
||||
|
||||
```rust
|
||||
let name = use_state(&cx, || "asd");
|
||||
rsx! {
|
||||
div {
|
||||
button { onclick: move |_| name.set("abc") }
|
||||
button { onclick: move |_| name.set("def") }
|
||||
button { onclick: move |_| name.set("ghi") }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Because we know the lifetime of your handlers, we can also expose this to children. No other Rust frameworks let us share borrowed state through the tree, forcing use of Rc/Arc everywhere. With Dioxus, all the Rc/Arc magic is tucked away in hooks, and just beautiful borrowed interfaces are exposed to your code. You don't need to know how Rc/RefCell work to build a competent Dioxus app.
|
||||
|
||||
```rust
|
||||
fn app(cx: Scope) -> Element {
|
||||
let name = use_state(&cx, || "asd");
|
||||
cx.render(rsx!{
|
||||
Button { name: name }
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Props)]
|
||||
struct ButtonProps<'a> {
|
||||
name: UseState<'a, &'static str>
|
||||
}
|
||||
|
||||
fn Button<'a>(cx: Scope<'a, Childprops<'a>>) -> Element {
|
||||
cx.render(rsx!{
|
||||
button {
|
||||
onclick: move |_| cx.props.name.set("bob")
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
There's *way* more to this story, but hopefully we've convinced you that Dioxus' DX somewhat approximates JSX/React.
|
||||
|
||||
|
||||
## Dioxus is perfected for the IDE
|
||||
|
||||
Note: all IDE-related features have only been tested with [Rust-Analyzer](https://github.com/rust-analyzer/rust-analyzer).
|
||||
|
||||
Dioxus code operates pleasantly with your IDE. For starters, most elements are documented through the Rustdoc system. A quick summary of the MDN docs is always under your finger tips:
|
||||
|
||||
![Elements have hover context](/static/ide_hover.png)
|
||||
|
||||
Dioxus also wraps platform-specific events with a custom synthetic event system. This means events enjoy proper autocomplete and documentation, unlike [Yew](https://yew.rs/) which currently relies on [web-sys](https://crates.io/crates/web-sys) with incomplete IDE support:
|
||||
|
||||
![Events are strongly typed](/static/ide_autocomplete.png)
|
||||
|
||||
Even element attributes and event handlers have top-notch documentation!
|
||||
|
||||
![Element attributes and listeners have hover context](/static/ide_listener.png)
|
||||
|
||||
The `rsx!` macro also benefits from code folding, batch renaming, and block selection, making most basic code navigation and completion tasks a breeze.
|
||||
|
||||
![Element blocks can be folded and renamed](/static/ide_selection.png)
|
||||
|
||||
Furthermore, the `rsx!` macro itself is documented, so if you ever forget how to use a certain feature, the documentation remains close at hand:
|
||||
|
||||
![The RSX documentation is provided on hover](/static/ide_rsx.png)
|
||||
|
||||
We spent a ton of time on this and we hope you enjoy it!
|
||||
|
||||
## Dioxus is extremely fast
|
||||
|
||||
We take the performance of Dioxus seriously. Instead of resolving to "good enough," Dioxus is designed to push the limits of what a declarative React-like framework can achieve. Dioxus is designed with multi-tenancy in mind: a single machine should be able to run thousands of simultaneous low-latency LiveView apps without skipping a beat. To accomplish this goal we've implemented a large number of optimizations:
|
||||
|
||||
- Usage of bump memory allocators and double-buffering
|
||||
- Compile-time hashing of templates
|
||||
- Automatic component memoization
|
||||
- Fiber-like scheduler
|
||||
- DOM Patch Batching
|
||||
|
||||
Dioxus is humbly built off the work done by [Dodrio](https://github.com/fitzgen/dodrio), a now-archived research project by fitzgen exploring the use of bump allocators in UI frameworks.
|
||||
|
||||
Dioxus is *substantially* more performant than many of the other Rust DOM-based UI libraries (Yew/Percy) and is *significantly* more performant than React - roughly competitive with InfernoJS. While not as performant as libraries like SolidJS/Sycamore, Dioxus imposes roughly a ~3% overhead over DOM patching, so it's *plenty* fast.
|
||||
|
||||
## Works on Desktop and Mobile
|
||||
We’ve mentioned before that Dioxus works practically anywhere that Rust does. When running natively as a desktop or mobile app, your Dioxus code will run on its own thread, not inside of a web runtime. This means you can access hardware, file system, and platform APIs directly without needing to go through a shim layer. In our examples, we feature a [file explorer app](https://github.com/DioxusLabs/example-projects/tree/master/file-explorer) and [WiFi scanner app](https://github.com/DioxusLabs/example-projects/tree/master/wifi-scanner) where platform access occurs inside an asynchronous multithreaded coroutine. This solves the problem faced by React Native and other cross-platform toolkits where JavaScript apps incur a massive performance penalty with substantial maintenance overhead associated with platform API shims.
|
||||
|
||||
A desktop app:
|
||||
|
||||
[![Example Dioxus desktop app](https://github.com/DioxusLabs/example-projects/raw/master/file-explorer/image.png)](https://github.com/DioxusLabs/example-projects/blob/master/file-explorer)
|
||||
|
||||
A mobile app:
|
||||
|
||||
[![Example Dioxus mobile app](https://github.com/DioxusLabs/example-projects/raw/master/ios_demo/assets/screenshot_smaller.jpeg)](https://github.com/DioxusLabs/example-projects/blob/master/ios_demo)
|
||||
|
||||
However, be warned that mobile is currently considered very experimental and there will likely be quirks. Dioxus is leveraging the work done by the [Tauri](https://github.com/tauri-apps/tauri) team to enable mobile support, and mobile support isn't technically complete in Tauri yet.
|
||||
|
||||
iOS should be supported out of the box, but Android support will take custom some boilerplate that hasn't been completely figured out. If you're interested in contributing to Dioxus, improving mobile support would be extremely helpful.
|
||||
|
||||
### Did someone say TUI support?
|
||||
|
||||
Yes, you can even build terminal user interfaces with Dioxus. Full support is still a work in progress, but the foundation is there.
|
||||
|
||||
[![TUI Support](https://github.com/DioxusLabs/rink/raw/master/examples/example.png)](https://github.com/dioxusLabs/rink)
|
||||
|
||||
### Things we didn't cover:
|
||||
|
||||
There are a bunch of things we didn't talk about here. Check out the guide for more information, or peruse the examples and reference for more context.
|
||||
|
||||
- Jank-free rendering with fiber scheduler
|
||||
- [Support for borrowed props]()
|
||||
- [Conditional rendering]()
|
||||
- [CSS/Styling/Inline style support]()
|
||||
- [Support for inline Context Providing/Consuming]()
|
||||
- [First-class global state management]()
|
||||
|
||||
For a quick glance at party with React, check out the [Readme on Github](https://github.com/DioxusLabs/dioxus#parity-with-react).
|
||||
|
||||
## What's on the roadmap?
|
||||
|
||||
The world of Rust on the frontend is barely explored. Given the performance, ergonomics, and portability of Rust/Dioxus, we expect there to be a ton of different applications where having a React-like toolkit running natively can enable things previously considered impossible.
|
||||
|
||||
In the coming weeks, our plan is to finish the remaining outstanding features where Dioxus is lacking in comparison to React:
|
||||
|
||||
- Transition effects for Suspense
|
||||
- Micro-optimizations and better cross-platform/browser bug mitigations
|
||||
- Heuristics to guide the diffing algorithm
|
||||
- Better support for subtree memoization (signals, etc.)
|
||||
- More thorough documentation, fleshing out sore spots
|
||||
|
||||
We also need some help in important crates currently missing:
|
||||
|
||||
- First class cross-platform router (currently in progress)
|
||||
- An extension to DioxusStudio that enables lazy bundling of static assets
|
||||
- Animation library (see [React Spring](https://react-spring.io/), [Framer Motion](https://www.framer.com/motion/))
|
||||
- A [TUI renderer for Dioxus](https://github.com/dioxuslabs/rink) (see [Ink](https://github.com/vadimdemedes/ink))
|
||||
|
||||
And finally, some bigger, forward-thinking projects that are too big for a one-person team:
|
||||
|
||||
- Completely native renderer for the Dioxus Virtual DOM (see [Flutter](https://flutter.dev/))
|
||||
- Better support for LiveView
|
||||
- Code-splitting
|
||||
- 3D renderer (see [react-three-fiber](https://github.com/pmndrs/react-three-fiber))
|
||||
|
||||
Stay tuned for our next article, which will go over some of the optimization techniques that went into making Dioxus blazing fast.
|
||||
|
||||
## Community
|
||||
|
||||
The future is bright for Rust frontends! If you'd like to get involved, we have a [Discord server](https://discord.gg/XgGxMSkvUM), [a subreddit](http://reddit.com/r/dioxus), and [GitHub discussion pages](https://github.com/DioxusLabs/dioxus/discussions).
|
||||
|
||||
Let us know what you build!
|
||||
|
||||
Check out the original `/r/rust` thread here.
|
145
docs/posts/release_0_1_7.md
Normal file
145
docs/posts/release_0_1_7.md
Normal file
|
@ -0,0 +1,145 @@
|
|||
# Dioxus Release Notes: v0.1.7 🏗
|
||||
|
||||
> Jan 7, 2022
|
||||
|
||||
> [@jkelleyrtp](https://github.com/jkelleyrtp)
|
||||
> Thanks to [@mrxiaozhuox](https://github.com/mrxiaozhuox) [@JtotheThree](https://github.com/JtotheThree) [@chris-morgan](https://github.com/chris-morgan) [@higumachan](https://github.com/higumachan)
|
||||
|
||||
TLDR Major features in this update:
|
||||
- The `Props` macro now allows optional/default attributes
|
||||
- InlineProps macro allows definition of props within a component's function arguments
|
||||
- New router in the spirit of React Router
|
||||
- `Attribute` Syntax for spreading arbitrary attributes into components
|
||||
- Rehydration example, improved implementation, tests, and documentation
|
||||
- File Drag n Drop support for Desktop
|
||||
- PreventDefault attribute and method on events
|
||||
|
||||
TLDR Major fixes:
|
||||
- Windows/GTK delayed loading bug fixed
|
||||
- Windows ICE fixed
|
||||
- Studio/CLI compiles properly
|
||||
|
||||
TLDR Community Contributions:
|
||||
- Form Example
|
||||
- Improved Calculator example
|
||||
- Improved example running support
|
||||
|
||||
# Highlighted Features
|
||||
|
||||
## The `Props` macro now allows optional/default attributes
|
||||
|
||||
While the `Props` macro has always supported optional/default attributes, it is now documented! Props can be configured to work just like how [Typed-Builder](https://github.com/idanarye/rust-typed-builder) works:
|
||||
|
||||
```rust
|
||||
#[derive(Props)]
|
||||
struct CheckboxProps {
|
||||
#[props(default)]
|
||||
enabled: bool,
|
||||
|
||||
#[props(default = "jane")]
|
||||
name: &'static str,
|
||||
|
||||
#[props(auto_into)] // will always coerce Into<String>
|
||||
description: String,
|
||||
|
||||
#[props(default, strip_option)]
|
||||
age: Option<usize>
|
||||
}
|
||||
```
|
||||
|
||||
## The inline props macro
|
||||
|
||||
In the spirit of improving props declaration, we've released the `inline_props` macro. This makes it faster to build components without needing to explicitly declare a props struct.
|
||||
|
||||
```rust
|
||||
#[inline_props]
|
||||
fn Checkbox(cx: Scope, enabled: bool, name: &'static str) -> Element {
|
||||
cx.render(rsx!{
|
||||
h1 { "Hello, {name}" }
|
||||
p { "Are you enabled?, {enabled}" }
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## New router in the spirit of React Router
|
||||
|
||||
We've added a new router in the spirit of [React Router](http://reactrouter.com). The React ecosystem has lots of experience and battle-tested solutions, so adopting React Router's architecture was easy for us.
|
||||
|
||||
Routes are declared
|
||||
|
||||
```rust
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
Router {
|
||||
ul {
|
||||
Link { to: "/", li { "Go home!" } }
|
||||
Link { to: "users", li { "List all users" } }
|
||||
Link { to: "blog", li { "Blog posts" } }
|
||||
}
|
||||
Route { to: "/", "Home" }
|
||||
Route { to: "users",
|
||||
Route { to: "/", "User list" }
|
||||
Route { to: ":name", BlogPost {} }
|
||||
}
|
||||
Route { to: "blog"
|
||||
Route { to: "/", "Blog list" }
|
||||
Route { to: ":post", BlogPost {} }
|
||||
}
|
||||
Route { to: "", "Err 404 Route Not Found" }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn BlogPost(cx: Scope) -> Element {
|
||||
let post = dioxus::router::use_route(&cx).last_segment()?;
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "Reading blog post: {post}" }
|
||||
p { "example blog post" }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn User(cx: Scope) -> Element {
|
||||
let post = dioxus::router::use_route(&cx).last_segment()?;
|
||||
let bold = dioxus::router::use_route(&cx).param::<bool>("bold");
|
||||
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "Reading blog post: {post}" }
|
||||
p { "example blog post" }
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## `Attribute` Syntax for spreading arbitrary attributes into components
|
||||
|
||||
|
||||
## Rehydration example, improved implementation, tests, and documentation
|
||||
|
||||
|
||||
## File Drag n Drop support for Desktop
|
||||
|
||||
|
||||
## PreventDefault attribute and method on events
|
||||
|
||||
|
||||
|
||||
# Highlighted Fixes
|
||||
|
||||
## Windows/GTK delayed loading bug fixed
|
||||
|
||||
|
||||
## Windows ICE fixed
|
||||
|
||||
|
||||
## Studio/CLI compiles properly
|
||||
|
||||
|
||||
# Highlighted Community Contributions
|
||||
|
||||
## Form Example
|
||||
|
||||
## Improved Calculator example
|
543
docs/posts/release_draft.md
Normal file
543
docs/posts/release_draft.md
Normal file
|
@ -0,0 +1,543 @@
|
|||
# Introducing Dioxus 0.1
|
||||
|
||||
After months of work, we're very excited to release the first version of Dioxus!
|
||||
|
||||
Dioxus is a new library for building interactive user interfaces with Rust. It is built around a VirtualDOM, making it portable for the web, desktop, server, mobile, and more.
|
||||
|
||||
Dioxus has the following design goals:
|
||||
|
||||
- **Familiar**: offer a React-like mental model and API surface
|
||||
- **Correct**: Avoid runtime bugs by moving rules and error handling into the type system
|
||||
- **Performant**: Scale to the largest of apps for the largest teams
|
||||
- **Productive:** Comprehensive inline documentation, fast recompiles, and deeply integrated tooling
|
||||
- **Extensible:** Reusable hooks and components that work on every platform
|
||||
|
||||
Dioxus is designed to be familiar for developers comfortable with React paradigms. Our goal is to ensure a smooth transition from TypeScript to Rust without having to learn any major new concepts. In practice, Rust-Dioxus code looks and feels very similar to TypeScript-React code.
|
||||
|
||||
To give you a taste of what Dioxus is all about, here's a simple counter app:
|
||||
|
||||
```rust
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus::desktop::launch(App, |cfg| cfg)
|
||||
}
|
||||
|
||||
const App: Component<()> = |cx| {
|
||||
let mut count = use_state(&cx, || 0);
|
||||
cx.render(rsx! {
|
||||
h1 { "Count: {count}" }
|
||||
button { onclick: move |_| count += 1, "+" }
|
||||
button { onclick: move |_| count -= 1, "-" }
|
||||
})
|
||||
};
|
||||
```
|
||||
|
||||
This simple counter is a fully-fledged desktop app, running at native speeds on a native thread. In this particular configuration, Dioxus is using the system's built-in WebView renderer as a "LiveView target." Dioxus automatically shuttles all events from the WebView runtime into the application code. In our app, we can interact natively with system APIs, run multi-threaded code, and do anything a regular native Rust application might do. To publish our app, we simply need to run `cargo build` to compile a portable binary that looks and feels the same on Windows, Mac, and Linux. In fact, our `App` function works exactly the same on desktop, mobile, and the web too.
|
||||
|
||||
Dioxus supports everything React does, and more, including
|
||||
|
||||
- Server-side-rendering, pre-rendering, and hydration
|
||||
- Mobile, desktop, and web support
|
||||
- Suspense, fibers, coroutines, and customizable error handling
|
||||
- Hooks, first-class state management, components
|
||||
- Fragments, conditional rendering, and custom elements
|
||||
- and more!
|
||||
|
||||
As a demo, here's a Dioxus app running on all our current supported platforms:
|
||||
|
||||
![Untitled](static/Untitled.png)
|
||||
|
||||
This very site is built with dioxus, and the source code is available here.
|
||||
|
||||
To get started with Dioxus, check out any of the "Getting Started" guides for your platform of choice, or check out the GitHub Repository for more details.
|
||||
|
||||
- Getting Started with Dioxus
|
||||
- Getting Started with Web
|
||||
- Getting Started with Desktop
|
||||
- Getting Started with Mobile
|
||||
- Getting Started with SSR
|
||||
|
||||
|
||||
## Show me some examples of what can be built!
|
||||
|
||||
|
||||
- File explorer desktop app
|
||||
- Bluetooth scanner desktop app
|
||||
- IoT management web app
|
||||
- Chat mobile app
|
||||
- Hackernews LiveView app
|
||||
|
||||
|
||||
## Why should I use Rust and Dioxus for frontend?
|
||||
|
||||
|
||||
Modern applications are scaling way beyond what our tools originally intended. Unfortunately, these tools make it too easy to fall into a "pit of despair" of buggy, unmaintainable, and fragile code. Frontend teams are constantly battling technical debt to maintain their velocity; and, while our web tools make it easy and fast to write code, they don't push us to write *better* code.
|
||||
|
||||
We believe that Rust's ability to write high-level, statically typed, and efficient code should make it easier for frontend teams to take on even the most ambitious of projects. Rust projects can be refactored fearlessly: the powerful type system prevents an entire class of bugs at compile time. No more `cannot read property of undefined` ever again! With Rust, all errors must be accounted for at compile time. You cannot ship an app that does not - in some way - handle its errors.
|
||||
|
||||
And while TypeScript is a great addition to JavaScript, it comes with a lot of tweaking flags, a slight performance hit, and an uneven ecosystem where some of the most important packages are not properly typed. TypeScript provides tremendous benefit to JS projects, but comes with its own "TypeScript Tax" that can impede development. Using Rust can be seen as a step up from TypeScript, supporting:
|
||||
|
||||
- static types for *all* libraries
|
||||
- advanced pattern matching
|
||||
- true immutability by default
|
||||
- clean, composable iterators
|
||||
- a consistent module system
|
||||
- integrated documentation
|
||||
- inline built-in unit/integration testing
|
||||
- best-in-class error handling
|
||||
- simple and fast build system (compared to webpack!)
|
||||
- powerful standard library and extensive library ecosystem
|
||||
- various macros (`html!`, `rsx!`) for fast template iteration
|
||||
- Excellent IDE support for documentation and jump-to-source support
|
||||
|
||||
Our goal with Dioxus is to give frontend teams greater confidence in their work. We believe that Dioxus apps are just as ergonomic to write as their React counterparts and may be fearlessly iterated in less time.
|
||||
|
||||
However, we do recognize that these benefits are not for everyone, nor do they completely fix "everything that is wrong with frontend development." There are always going to be new patterns, frameworks, and languages that solve these problems better than Rust and Dioxus. We hope that Dioxus serves as a competent companion for developers looking to build reliable and efficient software that extends into the world of user interfaces.
|
||||
|
||||
## Show me more:
|
||||
|
||||
|
||||
Here, we'll dive into some features of Dioxus and why it's so fun to use. The API reference serves as a deeper and more comprehensive look at what Dioxus can do.
|
||||
|
||||
|
||||
### **Building a new Project is simple!**
|
||||
|
||||
|
||||
To start a new project, all you need is Cargo (comes with Rust). For a simple desktop app, all we'll need is the `dioxus` create with the appropriate `desktop` feature. In a new crate, we'll just add this to our `Cargo.toml`.
|
||||
|
||||
```rust
|
||||
[dependencies]
|
||||
dioxus = { version = "*", features = ["desktop"] }
|
||||
```
|
||||
|
||||
Because it's so simple to get started, you probably won't need to reach for a prebuilt template, though we have pre-configured a few templates with suggested project layout.
|
||||
|
||||
For web development, you'll want to install the Dioxus CLI to run a local development server and run some basic WASM optimization tools. This can be done with a simple `cargo install dioxus-cli`. The `dioxus-cli` tool will handle building, bundling, development, and optimization for the web and mobile.
|
||||
|
||||
|
||||
### **Multiple flavors of templating: `rsx!`, `html!`, and `factory`, oh my!**
|
||||
|
||||
|
||||
You can use three flavors of templating to declare your UI structure. With the `html!` macro, you get JSX-like functionality. You can copy-paste *most* HTML snippets and expect them to work without modification.
|
||||
|
||||
```rust
|
||||
html! {
|
||||
<div> "Hello, world!" </div>
|
||||
<button onclick={|_| log::info!("button pressed")}>
|
||||
"Press me"
|
||||
</button>
|
||||
}
|
||||
```
|
||||
|
||||
We also have our own flavor of templating called RSX (a spin on JSX). RSX is very similar to regular struct syntax for Rust so it integrates well with your IDE. RSX supports code-folding, block selection, bracket pair colorizing, autocompletion, symbol renaming - pretty much anything you would expect from writing just regular struct-style code.
|
||||
|
||||
```rust
|
||||
rsx! {
|
||||
div { "Hello world" }
|
||||
button { onclick: |_| log::info!("button pressed"),
|
||||
"Press me"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If macros aren't your style, then you can always just use the factory API directly:
|
||||
|
||||
```rust
|
||||
LazyNodes(|f| {
|
||||
f.fragment([
|
||||
f.element(div, [f.text("hello world")], [], None, None)
|
||||
f.element(
|
||||
button,
|
||||
[f.text("Press Me")],
|
||||
[on::click(move |_| log::info!("button pressed"))],
|
||||
None,
|
||||
None
|
||||
)
|
||||
])
|
||||
})
|
||||
```
|
||||
|
||||
The `rsx!` and `html!` macros generate idiomatic Rust code that uses the factory API - no different than what you'd write by hand, yourself. Feel free to try it out with `cargo expand` .
|
||||
|
||||
To make it easier to work with RSX, we've built a small VSCode extension with useful utilities. This extension provides a command that converts a selected block of HTML into RSX so you can use any web template instantly.
|
||||
|
||||
|
||||
### **Dioxus is perfected for the IDE.**
|
||||
|
||||
|
||||
All Dioxus code operates pleasantly with your IDE. If you really need to write HTML templates, you can use the `html!` macro, but if you're willing to depart from traditional syntax, the `rsx!` macro provides everything you need, and more.
|
||||
|
||||
For starters, all elements are documented through the Rustdoc system - a quick summary of the MDN docs is always under your finger tips:
|
||||
|
||||
![static/Screen_Shot_2021-07-06_at_9.42.08_PM.png](static/Screen_Shot_2021-07-06_at_9.42.08_PM.png)
|
||||
|
||||
Dioxus also wraps platform-specific events with a custom synthetic event system. This means events enjoy proper autocomplete and documentation, unlike Yew which currently relies WebSys (which is not IDE supported):
|
||||
|
||||
![static/Screen_Shot_2021-07-06_at_10.24.03_PM.png](static/Screen_Shot_2021-07-06_at_10.24.03_PM.png)
|
||||
|
||||
Even element attributes and event handlers have top-notch documentation!
|
||||
|
||||
![static/Screen_Shot_2021-07-07_at_1.21.31_AM.png](static/Screen_Shot_2021-07-07_at_1.21.31_AM.png)
|
||||
|
||||
The `rsx!` macro also enjoys code folding, batch renaming, block selection, making most basic code navigation and completion tasks a breeze.
|
||||
|
||||
![static/Screen_Shot_2021-07-06_at_10.16.46_PM.png](static/Screen_Shot_2021-07-06_at_10.16.46_PM.png)
|
||||
|
||||
Plus, the `rsx!` macro itself is documented, so if you ever forget how to use a certain feature, the documentation is literally right under your cursor:
|
||||
|
||||
![static/Screen_Shot_2021-07-07_at_1.28.24_AM.png](static/Screen_Shot_2021-07-07_at_1.28.24_AM.png)
|
||||
|
||||
We spent a ton of time on this - we hope you enjoy it!
|
||||
|
||||
|
||||
## **Dioxus is hyperoptimized 🚀🚀🚀**
|
||||
|
||||
|
||||
We take the performance of Dioxus seriously. Instead of resolving to "good enough," Dioxus is designed to push the limits of what a declarative React-like framework can achieve. Dioxus is designed with multi-tenancy in mind: a single machine should be able to run thousands of simultaneous low-latency LiveView apps without skipping a beat. To accomplish this goal we've implemented a large number of optimizations:
|
||||
|
||||
- Specialized memory allocators
|
||||
- Compile-time hashing and diffing hints
|
||||
- Automatic component memoization
|
||||
- Cooperative fiber-like scheduling
|
||||
- DOM Patch Batching
|
||||
|
||||
|
||||
### Bump allocator
|
||||
|
||||
|
||||
Dioxus is incredibly optimized, both for performance and memory efficiency. Due to Rust's type system and low-level abilities, we can precisely control the performance in ways that JavaScript simply cannot. In some aspects, using Rust with Dioxus is faster than plain JavaScript. With direct access over memory allocation and efficient reuse of strings, we can bypass dynamic memory allocation entirely for components after their initial render.
|
||||
|
||||
All `Factory` calls use a bump memory allocator to allocate new objects like Elements, Listeners, Attributes, and even strings. Bump memory allocators are the fastest possible memory allocators - significantly faster than the default allocator used in JavaScript runtimes. The original research in bump allocators for Rust-based UI comes from Dodrio (@fitzgen), a now-archived project that demonstrated the insane speeds of low-level memory control.
|
||||
|
||||
![static/Screen_Shot_2021-08-17_at_2.24.39_AM.png](static/Screen_Shot_2021-08-17_at_2.24.39_AM.png)
|
||||
|
||||
|
||||
### Static subtree optimization
|
||||
|
||||
|
||||
Because the rsx! macro is deeply integrated with Dioxus, we can apply extremely aggressive optimizations, pushing performance to the absolute maximum. Every rsx! call will generate a static hash, informing the diffing algorithm if the element needs to be checked after it's been mounted. This means that static substructures in complex components will never need to be diffed, saving many precious clock cycles at runtime. For instance, the "div" element in the below example will never be checked once the component has rendered for the first time. Dioxus will only bother computing the differences of attributes and children that it knows *can* change, like the text contents of the paragraph.
|
||||
|
||||
```rust
|
||||
let val = 10;
|
||||
rsx!{
|
||||
div {
|
||||
style: { background_color: "red" }
|
||||
"This is a static subtree"
|
||||
h1 {"These elements will not be diffed"}
|
||||
}
|
||||
p { "This content _can_ change: {val}" }
|
||||
}
|
||||
```
|
||||
|
||||
### Automatic Memoization and Managed Lifetimes
|
||||
|
||||
|
||||
Dioxus provides a very simple form of memoization for components: if a component's props borrow directly from its parent, it is not memoized. This means that Dioxus is the only UI framework in Rust that lets components borrow data from their parents.
|
||||
|
||||
A memoized component is a component that does not borrow any data from its parent. Either it "copies" or "clones" data from the parent into its own props, or generates new data on the fly. These Props must implement `PartialEq` - where you can safely implement your own memoization strategy.
|
||||
|
||||
```rust
|
||||
static App: FC<()> = |cx| rsx!(in cx, Child { name: format!("world") });
|
||||
|
||||
#[derive(Props, PartialEq)]
|
||||
struct ChildProps {
|
||||
name: String,
|
||||
}
|
||||
static Child: FC<MyProps> = |cx| rsx!(in cx, div {"Hello, {cx.name}"});
|
||||
```
|
||||
|
||||
For components that are not valid for the `'static` lifetime, they do not need to implement `PartialEq` . This lets you choose between dynamic memory allocation or longer diffs. Just like in React, it's better to avoid memory allocation if you know that a child will always change when its parent changes.
|
||||
|
||||
To make components with props that borrow data, we need to use the regular function syntax, and specify the appropriate lifetime:
|
||||
|
||||
```rust
|
||||
struct ChildProps<'a> {
|
||||
name: &'a str
|
||||
}
|
||||
fn Child<'a>(cx: Scope<'a, ChildProps>) -> Element<'a> {
|
||||
rsx!(cx, div {"Hello, {cx.name}"})
|
||||
}
|
||||
```
|
||||
|
||||
### Cooperative Fiber-Like Scheduling
|
||||
|
||||
|
||||
Rendering with Dioxus is entirely asynchronous and supports the same pause/resume functionality that the new React Fiber rewrite introduced. Internally, Dioxus uses a priority-based scheduler to pause long-running diffs to handle higher-priority work if new events are ready. With the priority scheduler, Dioxus will never block the main thread long enough to cause dropped frames or "jank": event handling is scheduled during idle times and DOM modification is scheduled during animation periods.
|
||||
|
||||
A cool note: Pause/resume uses Rust's efficient Future machinery under the hood, accomplishing the exact same functionality as React fiber with no additional code overhead.
|
||||
|
||||
On top of Pause/Resume, asynchronous rendering enables Suspense, fetch-as-you-render, and even Signals/Streams support.
|
||||
|
||||
|
||||
### Listener Multiplexing / Event delegation
|
||||
|
||||
|
||||
On the web, event delegation is a technique that makes highly-interactive web pages more performant by batching together listeners of the same type. For instance, a single app with more than 100 "onclick" listeners will only have a single listener mounted to the root node. Whenever the listener is triggered, the listener multiplexer will analyze the event and pass it off to the correct listener inside the VirtualDOM. This leads to more performant apps. Dioxus uses an event delegation system, but does not currently have support for proper bubbling or the ability to capture events. Dioxus will always bubble your events (manually) but does not (yet) provide any mechanism to prevent bubbling.
|
||||
|
||||
|
||||
### Patch Batching
|
||||
|
||||
|
||||
When updating the DOM to match your Dioxus component declaration, Dioxus separates its work into two separate phases: diff and commit. During the diff phase, Dioxus will compare an old version of your UI against a new version and figure out exactly which changes need to be made. Unlike other frameworks, Dioxus will not actually modify the DOM during this phase - modifying the DOM is an expensive operation that causes considerable context switching out of "hot codepaths."
|
||||
|
||||
Instead, Dioxus returns a "Mutations" object to the renderer to handle:
|
||||
|
||||
```rust
|
||||
#[derive(Debug)]
|
||||
pub struct Mutations<'a> {
|
||||
pub edits: Vec<DomEdit<'a>>,
|
||||
pub noderefs: Vec<NodeRefMutation<'a>>,
|
||||
}
|
||||
```
|
||||
|
||||
These modifications give the renderer a list of changes that need to be made to modify the real DOM to match the Virtual DOM. Our "DomEdit" type is just a simple enum that can be serialized and sent across the network - making it possible for us to support Liveview and remote clients.
|
||||
|
||||
```rust
|
||||
pub enum DomEdit<'bump> {
|
||||
PushRoot { id: u64 },
|
||||
PopRoot,
|
||||
AppendChildren { many: u32 },
|
||||
ReplaceWith { root: u64, m: u32 },
|
||||
InsertAfter { root: u64, n: u32 },
|
||||
// ....more variants
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Support for pre-rendering and hydration
|
||||
|
||||
|
||||
As part of the mutation paradigm, Dioxus also supports pre-rendering and hydration of pages - so you can easily generate your site statically and hydrate it with interactivity once it gets to the client. Rust already runs smoothly on the server, so you can easily build isomorphic apps that achieve amazing Lighthouse scores. Enabling hydration is as simple as:
|
||||
|
||||
```rust
|
||||
// On the server
|
||||
let pre_rendered = dioxus::ssr::render(App, |cfg| cfg.pre_render(true));
|
||||
|
||||
// On the client bundle
|
||||
dioxus::web::launch(App, |cfg| cfg.hydrate(true));
|
||||
```
|
||||
|
||||
|
||||
### Support for global shared state
|
||||
|
||||
|
||||
With Dioxus, shared state (React.Context) is simple. We don't need providers, consumers, wrapping components, or anything special. Creating a shared state is as simple as:
|
||||
|
||||
```rust
|
||||
struct SharedState(String);
|
||||
static App: FC<()> = |cx| {
|
||||
// Define it with the dedicated API on Context
|
||||
cx.use_create_shared_state(|| SharedState("Hello".to_string()));
|
||||
|
||||
// We can even immediately consume it in the same component
|
||||
let my_state = cx.use_shared_state::<SharedState>();
|
||||
}
|
||||
```
|
||||
|
||||
With Rust's memory safety guarantees and smart pointers, we can share state across the component tree without worrying about accidental mutations or usage errors.
|
||||
|
||||
In fact, Dioxus is shipping with a 1st-class state management solution modeled after Recoil.JS. The name is still in flux, but we're going with `Recoil` for now until we find something better.
|
||||
|
||||
In Recoil, shared state is declared with an "Atom" or "AtomFamily". From there, "Selectors" and "SelectorFamilies" make it possible to select, combine, and memoize computations across your app. The two fundamental hooks `use_read` and `use_write` provide an API that matches `use_state` and work across your whole app. The documentation on Recoil is currently very thin, but the API is simple to grok.
|
||||
|
||||
```rust
|
||||
static COUNT: Atom<u32> = |_| 0;
|
||||
|
||||
static Incr: FC<()> = |cx| {
|
||||
let mut count = use_write(cx, COUNT);
|
||||
rsx!(in cx, button { onclick: move |_| count += 1, "increment" })
|
||||
};
|
||||
|
||||
static Decr: FC<()> = |cx| {
|
||||
let mut count = use_write(cx, COUNT);
|
||||
rsx!(in cx, button { onclick: move |_| count -= 1, "decrement" })
|
||||
};
|
||||
|
||||
static App: FC<()> = |cx| {
|
||||
let count = use_read(cx, COUNT);
|
||||
rsx!(in cx, "Count is {count}", Incr {}, Decr {})
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### Support for Suspense
|
||||
|
||||
|
||||
Dioxus makes it dead-easy to work with asynchronous values. Simply provide a future to `cx.suspend`, and Dioxus will schedule that future as a task which occurs outside of the regular diffing process. Once the future is complete, that subtree will be rendered. This is a different, more "Rusty" approach to React's "Suspense" mechanism. Because Dioxus uses an asynchronous diffing algorithm, you can easily "fetch as you render." Right now, suspense is very low-level and there aren't a ton of ergonomic hooks built around it. Feel free to make your own!
|
||||
|
||||
```rust
|
||||
#[derive(serde::Deserialize)]
|
||||
struct DogApi {
|
||||
message: String,
|
||||
}
|
||||
const ENDPOINT: &str = "https://dog.ceo/api/breeds/image/random";
|
||||
|
||||
const App: FC<()> = |cx| {
|
||||
let req = use_ref(cx, surf::get(ENDPOINT).recv_json::<DogApi>());
|
||||
|
||||
let doggo = cx.suspend(req, |cx, res| match res {
|
||||
Ok(res) => rsx!(in cx, img { src: "{res.message}" }),
|
||||
Err(_) => rsx!(in cx, div { "No doggos for you :(" }),
|
||||
});
|
||||
|
||||
cx.render(rsx!(
|
||||
h1 {"Waiting for a doggo..."}
|
||||
{doggo}
|
||||
))
|
||||
};
|
||||
```
|
||||
|
||||
|
||||
### Built-in coroutines make apps easier to scale
|
||||
|
||||
|
||||
Rust’s async story, while young, does have several highlights. Every future in Rust essentially has a built-in zero-cost AbortHandler - allowing us to pause, start, and restart asynchronous tasks without any additional logic. Dioxus gives full control over these tasks with the use_task hook, making it easy to spawn long-running event loops - essentially coroutines. Coroutines are very popular in game development for their ease of development and high performance even in the largest of apps. In Dioxus, a coroutine would look something like:
|
||||
|
||||
```rust
|
||||
static App: FC<()> = |cx| {
|
||||
let websocket_coroutine = use_task(cx, async move {
|
||||
let mut socket = connect_to_websocket().await.unwrap();
|
||||
while let Some(msg) = socket.recv().await {
|
||||
// update our global state
|
||||
}
|
||||
});
|
||||
// render code
|
||||
};
|
||||
```
|
||||
|
||||
In fact, coroutines allow you to implement true multithreading from within your UI. It's possible to spin up a Tokio/Async_std task or interact with a threadpool from a `use_task` handler.
|
||||
|
||||
|
||||
### Custom hooks
|
||||
|
||||
|
||||
Just like React, Dioxus supports custom hooks. With the `use_hook` method on `Context` , it's easy to write any new hook. However, unlike React, Dioxus manages the mutability of hook data for you, automatically. Calling `use_hook` is basically just adding a field to a managed "bag of state." This lets you obtain an `&mut T` to any data in use-hook - just like if you were writing a regular struct-based component in other frameworks:
|
||||
|
||||
```rust
|
||||
let counter: &mut u32 = cx.use_hook(|_| 0, |val| val, |_| {});
|
||||
```
|
||||
|
||||
|
||||
### Inline styles
|
||||
|
||||
|
||||
A small feature - but welcome one: Dioxus supports inline styles! This gives you 3 ways of styling components: dedicated CSS files, through `style` tags, and inline styles. Here:
|
||||
|
||||
```rust
|
||||
// dedicated file
|
||||
link { rel: "stylesheet", href: "style.css" }
|
||||
|
||||
// style tags
|
||||
let style = include_str!("style.css");
|
||||
rsx!(style { "{style}" });
|
||||
|
||||
// inline styles
|
||||
rsx!(div { background_color: "red" });
|
||||
```
|
||||
|
||||
Right now, a dedicated "Style" object is not supported for style merging, but we plan to add it in the future.
|
||||
|
||||
|
||||
### Works on mobile and desktop
|
||||
|
||||
|
||||
We’ve mentioned before that Dioxus works practically anywhere that Rust works. When running natively as a desktop or mobile app, your Dioxus code will run on its own thread: not inside of a web runtime. This means you can access hardware, file system, and platform APIs directly without needing to go through a shim layer. In our examples, we feature a file explorer app and Bluetooth scanner app where platform access occurs inside an asynchronous multithreaded coroutine. This solves the problem faced by React Native and other cross-platform toolkits where JavaScript apps occur a massive performance penalty with substantial maintenance overhead associated with platform API shims.
|
||||
|
||||
Using Dioxus on mobile is easy:
|
||||
|
||||
```rust
|
||||
dioxus::mobile::launch(App, |cfg| cfg);
|
||||
```
|
||||
|
||||
However, be warned that mobile is considered very experimental and there will likely be quirks. Dioxus is leveraging work done by the Tauri team to enable mobile support, and mobile support isn't technically complete in Tauri - yet. iOS should be supported out of the box, but Android support will take custom some boilerplate that hasn't been figured out completey.
|
||||
|
||||
|
||||
## FAQ:
|
||||
|
||||
|
||||
*"I thought the overhead of Rust to JS makes Rust apps slow?"*
|
||||
|
||||
Wasm-bindgen is *just* as fast Vanilla JS, beating out nearly every JS framework in the [framework benchmark](https://krausest.github.io/js-framework-benchmark/2021/table_chrome_91.0.4472.77.html). The biggest bottleneck of Rust interacting with JS is the overhead of translating Rust's UTF-8 strings into JS's UTF-16 strings. Dioxus uses string-interning (caching) and Bloomfilters to effectively circumvent this overhead, meaning even this bottleneck is basically invisible. In fact, Dioxus actually beats the wasm-bindgen benchmark, approaching near vanilla-JS speeds on the JS framework benchmark.
|
||||
|
||||
*"Isn't it much more difficult to write Rust than JS?"*
|
||||
|
||||
Frankly, the type of code used to write UI is not that complex. When dealing with complex problems, Rust code may end up more complex. React, Redux, and Immer all struggle with mutability issues that Rust naturally prevents. With Rust, it's impossible to accidentally mutate a field, saving developers not only from memory safety bugs, but logic bugs. Plus, we truly believe that Dioxus code will be even easier to write and maintain than its JS counterpart:
|
||||
|
||||
```rust
|
||||
// The Rust-Dioxus version
|
||||
const App: FC<()> = |cx| {
|
||||
let mut count = use_state(&cx, || 0);
|
||||
cx.render(rsx!{
|
||||
h1 { "Count: {count}" }
|
||||
button { onclick: move |_| count += 1, "+" }
|
||||
button { onclick: move |_| count -= 1, "-" }
|
||||
})
|
||||
}
|
||||
|
||||
// The TypeScript-React version:
|
||||
const App: FC<()> = (props) => {
|
||||
let [count, set_count] = use_state(0);
|
||||
return (
|
||||
<>
|
||||
<h1> Count: {count} </h1>
|
||||
<button onclick={() => set_count(count + 1)}> "+" </button>
|
||||
<button onclick={() => set_count(count - 1)}> "-" </button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
"Doesn't Rust take forever to compile?"
|
||||
|
||||
Have you ever used Webpack? 🙂 It's not uncommon for a large Webpack builds to push 3-5 minutes with hot iterations in 20-30 seconds. We've found that Rust's compiler has gotten much faster than it once was, WASM projects have fewer big dependencies, and the new Cranelift backend generates WASM code at blindingly-fast speeds. Smaller WASM projects will compile nearly instantly and the bigger projects might take 5-10 seconds for a hot-reload.
|
||||
|
||||
"Aren't Rust binaries too big for the web?"
|
||||
|
||||
Dioxus' gzipped "Hello world" clocks in at around 50 kB - more than Preact/Inferno but on par with React. However, WASM code is compiled as it is downloaded (in parallel) - and it compiles incredibly quickly! By the time the 50 kB is downloaded, the app is already running at full speed. In this way, it's hard to compare WASM and JS; JS needs time to be JIT-ed and cannot be JIT-ed until after it's downloaded completely. Typically, the JIT process takes much longer than the WASM compile process, so it's hard compare kilobytes to kilobytes.
|
||||
|
||||
In short - WASM apps launch just as fast, if not faster, than JS apps - which is typically the main concern around code size.
|
||||
|
||||
|
||||
## What's on the Roadmap?
|
||||
|
||||
|
||||
The world of Rust on the frontend is barely explored. Given the performance, ergonomics, and portability of Dioxus, we expect there to be a ton of different applications where having a React-like toolkit running natively can enable things previously impossible.
|
||||
|
||||
In the coming weeks, the plan is to finish the final outstanding features where Dioxus is lacking in comparison to React:
|
||||
|
||||
- Synchronous and asynchronous layout-related effects like `useEffect`
|
||||
- Support for event "capturing" and real event bubbling
|
||||
- Transition Effects for suspense
|
||||
- Micro-optimizations and better cross-platform/browser bug mitigations
|
||||
- Hooks to guide the diffing algorithm
|
||||
- Better support for subtree memoization
|
||||
- More thorough documentation, fleshing out sore spots
|
||||
|
||||
We also need some help in important crates currently missing:
|
||||
|
||||
- 1st class cross-platform router
|
||||
- An extension to DioxusStudio that enables lazy bundling of static assets
|
||||
- Animation library (like React Spring)
|
||||
- Better support for Dioxus as a TUI framework
|
||||
|
||||
And finally, some bigger, forward-thinking projects that are too big for one person:
|
||||
|
||||
- Completely native renderer for the Dioxus VirtualDOM (like Flutter)
|
||||
- Better support for LiveView
|
||||
- Code-splitting
|
||||
- 3D renderer like React-three-fiber
|
||||
|
||||
|
||||
## Community
|
||||
|
||||
|
||||
The future is bright for Rust frontends! If you'd like to get involved, we have a
|
||||
|
||||
- Discord
|
||||
- Subreddit
|
||||
- Github Discussions page
|
||||
|
||||
Check out the original `r/rust` thread here.
|
||||
|
||||
Let us know what you build!
|
Loading…
Reference in a new issue