diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 968d5f103..f69053742 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -23,7 +23,7 @@ jobs:
# NOTE: Delete when the previous one is enabled
- name: Setup mdBook
run: |
- cargo install mdbook --git https://github.com/Ruin0x11/mdBook.git --branch localization --rev e74fdb1
+ cargo install mdbook --git https://github.com/Demonthos/mdBook.git --branch master
- uses: actions/checkout@v3
- name: Build
diff --git a/docs/guide/Cargo.toml b/docs/guide/Cargo.toml
index 0f544e071..eb8060552 100644
--- a/docs/guide/Cargo.toml
+++ b/docs/guide/Cargo.toml
@@ -11,6 +11,7 @@ dioxus-desktop = { path = "../../packages/desktop" }
dioxus-web = { path = "../../packages/web" }
dioxus-ssr = { path = "../../packages/ssr" }
dioxus-router = { path = "../../packages/router" }
+dioxus-liveview = { path = "../../packages/liveview", features = ["axum"] }
dioxus-tui = { path = "../../packages/tui" }
fermi = { path = "../../packages/fermi" }
@@ -19,3 +20,4 @@ fermi = { path = "../../packages/fermi" }
serde = { version = "1.0.138", features=["derive"] }
reqwest = { version = "0.11.11", features = ["json"] }
tokio = { version = "1.19.2" , features=[]}
+axum = { version = "0.6.1", features = ["ws"] }
\ No newline at end of file
diff --git a/docs/guide/book.toml b/docs/guide/book.toml
index b0bae8d52..1f926256e 100644
--- a/docs/guide/book.toml
+++ b/docs/guide/book.toml
@@ -24,6 +24,8 @@ edit-url-template = "https://github.com/DioxusLabs/dioxus/edit/master/docs/guide
[output.html.playground]
editable = true
line-numbers = true
+# running examples will not work because dioxus is not a included in the playground
+runnable = false
[output.html.search]
limit-results = 20
diff --git a/docs/guide/examples/boolean_attribute.rs b/docs/guide/examples/boolean_attribute.rs
index 14a824b69..685103ccb 100644
--- a/docs/guide/examples/boolean_attribute.rs
+++ b/docs/guide/examples/boolean_attribute.rs
@@ -5,13 +5,14 @@ fn main() {
dioxus_desktop::launch(App);
}
+#[rustfmt::skip]
fn App(cx: Scope) -> Element {
// ANCHOR: boolean_attribute
- cx.render(rsx! {
- div {
- hidden: "false",
- "hello"
- }
- })
+cx.render(rsx! {
+ div {
+ hidden: "false",
+ "hello"
+ }
+})
// ANCHOR_END: boolean_attribute
}
diff --git a/docs/guide/examples/component_props_options.rs b/docs/guide/examples/component_props_options.rs
index 8963f6374..df2ebf1db 100644
--- a/docs/guide/examples/component_props_options.rs
+++ b/docs/guide/examples/component_props_options.rs
@@ -5,49 +5,50 @@ fn main() {
dioxus_desktop::launch(App);
}
+#[rustfmt::skip]
fn App(cx: Scope) -> Element {
cx.render(rsx! {
// ANCHOR: OptionalProps_usage
- Title {
- title: "Some Title",
- },
- Title {
- title: "Some Title",
- subtitle: "Some Subtitle",
- },
- // Providing an Option explicitly won't compile though:
- // Title {
- // title: "Some Title",
- // subtitle: None,
- // },
+Title {
+ title: "Some Title",
+},
+Title {
+ title: "Some Title",
+ subtitle: "Some Subtitle",
+},
+// Providing an Option explicitly won't compile though:
+// Title {
+// title: "Some Title",
+// subtitle: None,
+// },
// ANCHOR_END: OptionalProps_usage
// ANCHOR: ExplicitOption_usage
- ExplicitOption {
- title: "Some Title",
- subtitle: None,
- },
- ExplicitOption {
- title: "Some Title",
- subtitle: Some("Some Title"),
- },
- // This won't compile:
- // ExplicitOption {
- // title: "Some Title",
- // },
+ExplicitOption {
+ title: "Some Title",
+ subtitle: None,
+},
+ExplicitOption {
+ title: "Some Title",
+ subtitle: Some("Some Title"),
+},
+// This won't compile:
+// ExplicitOption {
+// title: "Some Title",
+// },
// ANCHOR_END: ExplicitOption_usage
// ANCHOR: DefaultComponent_usage
- DefaultComponent {
- number: 5,
- },
- DefaultComponent {},
+DefaultComponent {
+ number: 5,
+},
+DefaultComponent {},
// ANCHOR_END: DefaultComponent_usage
// ANCHOR: IntoComponent_usage
- IntoComponent {
- string: "some &str",
- },
+IntoComponent {
+ string: "some &str",
+},
// ANCHOR_END: IntoComponent_usage
})
}
diff --git a/docs/guide/examples/conditional_rendering.rs b/docs/guide/examples/conditional_rendering.rs
index 0b3ebfbfe..c6095df98 100644
--- a/docs/guide/examples/conditional_rendering.rs
+++ b/docs/guide/examples/conditional_rendering.rs
@@ -1,3 +1,4 @@
+#![allow(unused)]
#![allow(non_snake_case)]
use dioxus::prelude::*;
@@ -16,6 +17,7 @@ pub fn App(cx: Scope) -> Element {
}
#[inline_props]
+#[rustfmt::skip]
fn LogIn<'a>(
cx: Scope<'a>,
is_logged_in: bool,
@@ -23,38 +25,73 @@ fn LogIn<'a>(
on_log_out: EventHandler<'a>,
) -> Element<'a> {
// ANCHOR: if_else
- if *is_logged_in {
- cx.render(rsx! {
- div {
- "Welcome!",
- button {
- onclick: move |_| on_log_out.call(()),
- "Log Out",
- }
- }
- })
- } else {
- cx.render(rsx! {
- button {
- onclick: move |_| on_log_in.call(()),
- "Log In",
- }
- })
- }
+if *is_logged_in {
+ cx.render(rsx! {
+ "Welcome!"
+ button {
+ onclick: move |_| on_log_out.call(()),
+ "Log Out",
+ }
+ })
+} else {
+ cx.render(rsx! {
+ button {
+ onclick: move |_| on_log_in.call(()),
+ "Log In",
+ }
+ })
+}
// ANCHOR_END: if_else
}
#[inline_props]
+#[rustfmt::skip]
+fn LogInImproved<'a>(
+ cx: Scope<'a>,
+ is_logged_in: bool,
+ on_log_in: EventHandler<'a>,
+ on_log_out: EventHandler<'a>,
+) -> Element<'a> {
+ // ANCHOR: if_else_improved
+cx.render(rsx! {
+ // We only render the welcome message if we are logged in
+ // You can use if statements in the middle of a render block to conditionally render elements
+ if *is_logged_in {
+ // Notice the body of this if statment is rsx code, not an expression
+ "Welcome!"
+ }
+ button {
+ // depending on the value of `is_logged_in`, we will call a different event handler
+ onclick: move |_| if *is_logged_in {
+ on_log_in.call(())
+ }
+ else{
+ on_log_out.call(())
+ },
+ if *is_logged_in {
+ // if we are logged in, the button should say "Log Out"
+ "Log Out"
+ } else {
+ // if we are not logged in, the button should say "Log In"
+ "Log In"
+ }
+ }
+})
+ // ANCHOR_END: if_else_improved
+}
+
+#[inline_props]
+#[rustfmt::skip]
fn LogInWarning(cx: Scope, is_logged_in: bool) -> Element {
// ANCHOR: conditional_none
- if *is_logged_in {
- return cx.render(rsx!(()));
- }
+if *is_logged_in {
+ return None;
+}
- cx.render(rsx! {
- a {
- "You must be logged in to comment"
- }
- })
+cx.render(rsx! {
+ a {
+ "You must be logged in to comment"
+ }
+})
// ANCHOR_END: conditional_none
}
diff --git a/docs/guide/examples/dangerous_inner_html.rs b/docs/guide/examples/dangerous_inner_html.rs
index f5f976e59..fe8a52663 100644
--- a/docs/guide/examples/dangerous_inner_html.rs
+++ b/docs/guide/examples/dangerous_inner_html.rs
@@ -5,15 +5,16 @@ fn main() {
dioxus_desktop::launch(App);
}
+#[rustfmt::skip]
fn App(cx: Scope) -> Element {
// ANCHOR: dangerous_inner_html
- // this should come from a trusted source
- let contents = "live dangerously";
+// this should come from a trusted source
+let contents = "live dangerously";
- cx.render(rsx! {
- div {
- dangerous_inner_html: "{contents}",
- }
- })
+cx.render(rsx! {
+ div {
+ dangerous_inner_html: "{contents}",
+ }
+})
// ANCHOR_END: dangerous_inner_html
}
diff --git a/docs/guide/examples/event_click.rs b/docs/guide/examples/event_click.rs
index d6ccc0682..bd25c5f36 100644
--- a/docs/guide/examples/event_click.rs
+++ b/docs/guide/examples/event_click.rs
@@ -5,13 +5,14 @@ fn main() {
dioxus_desktop::launch(App);
}
+#[rustfmt::skip]
fn App(cx: Scope) -> Element {
// ANCHOR: rsx
- cx.render(rsx! {
- button {
- onclick: move |event| println!("Clicked! Event: {event:?}"),
- "click me!"
- }
- })
+cx.render(rsx! {
+ button {
+ onclick: move |event| println!("Clicked! Event: {event:?}"),
+ "click me!"
+ }
+})
// ANCHOR_END: rsx
}
diff --git a/docs/guide/examples/event_nested.rs b/docs/guide/examples/event_nested.rs
index 4372869e3..b31e340f1 100644
--- a/docs/guide/examples/event_nested.rs
+++ b/docs/guide/examples/event_nested.rs
@@ -5,20 +5,21 @@ fn main() {
dioxus_desktop::launch(App);
}
+#[rustfmt::skip]
fn App(cx: Scope) -> Element {
// ANCHOR: rsx
- cx.render(rsx! {
- div {
- onclick: move |_event| {},
- "outer",
- button {
- onclick: move |event| {
- // now, outer won't be triggered
- event.stop_propagation();
- },
- "inner"
- }
+cx.render(rsx! {
+ div {
+ onclick: move |_event| {},
+ "outer",
+ button {
+ onclick: move |event| {
+ // now, outer won't be triggered
+ event.stop_propagation();
+ },
+ "inner"
}
- })
+ }
+})
// ANCHOR_END: rsx
}
diff --git a/docs/guide/examples/event_prevent_default.rs b/docs/guide/examples/event_prevent_default.rs
index 120f74157..b109700bf 100644
--- a/docs/guide/examples/event_prevent_default.rs
+++ b/docs/guide/examples/event_prevent_default.rs
@@ -5,13 +5,14 @@ fn main() {
dioxus_desktop::launch(App);
}
+#[rustfmt::skip]
fn App(cx: Scope) -> Element {
// ANCHOR: prevent_default
- cx.render(rsx! {
- input {
- prevent_default: "oninput",
- prevent_default: "onclick",
- }
- })
+cx.render(rsx! {
+ input {
+ prevent_default: "oninput",
+ prevent_default: "onclick",
+ }
+})
// ANCHOR_END: prevent_default
}
diff --git a/docs/guide/examples/hello_world_desktop.rs b/docs/guide/examples/hello_world_desktop.rs
index 91f0db03f..9d5a02051 100644
--- a/docs/guide/examples/hello_world_desktop.rs
+++ b/docs/guide/examples/hello_world_desktop.rs
@@ -1,12 +1,15 @@
// ANCHOR: all
#![allow(non_snake_case)]
+// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
use dioxus::prelude::*;
fn main() {
+ // launch the dioxus app in a webview
dioxus_desktop::launch(App);
}
// ANCHOR: component
+// define a component that renders a div with the text "Hello, world!"
fn App(cx: Scope) -> Element {
cx.render(rsx! {
div {
diff --git a/docs/guide/examples/hello_world_liveview.rs b/docs/guide/examples/hello_world_liveview.rs
new file mode 100644
index 000000000..e9170cf62
--- /dev/null
+++ b/docs/guide/examples/hello_world_liveview.rs
@@ -0,0 +1,60 @@
+// ANCHOR: all
+use axum::{extract::ws::WebSocketUpgrade, response::Html, routing::get, Router};
+use dioxus::prelude::*;
+
+// ANCHOR: glue
+#[tokio::main]
+async fn main() {
+ let addr: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into();
+
+ let view = dioxus_liveview::LiveViewPool::new();
+
+ let app = Router::new()
+ // The root route contains the glue code to connect to the WebSocket
+ .route(
+ "/",
+ get(move || async move {
+ Html(format!(
+ r#"
+
+
+
Dioxus LiveView with Axum
+
+ {glue}
+
+ "#,
+ // Create the glue code to connect to the WebSocket on the "/ws" route
+ glue = dioxus_liveview::interpreter_glue(&format!("ws://{addr}/ws"))
+ ))
+ }),
+ )
+ // The WebSocket route is what Dioxus uses to communicate with the browser
+ .route(
+ "/ws",
+ get(move |ws: WebSocketUpgrade| async move {
+ ws.on_upgrade(move |socket| async move {
+ // When the WebSocket is upgraded, launch the LiveView with the app component
+ _ = view.launch(dioxus_liveview::axum_socket(socket), app).await;
+ })
+ }),
+ );
+
+ println!("Listening on http://{}", addr);
+
+ axum::Server::bind(&addr.to_string().parse().unwrap())
+ .serve(app.into_make_service())
+ .await
+ .unwrap();
+}
+// ANCHOR_END: glue
+
+// ANCHOR: app
+fn app(cx: Scope) -> Element {
+ cx.render(rsx! {
+ div {
+ "Hello, world!"
+ }
+ })
+}
+// ANCHOR_END: app
+// ANCHOR_END: all
diff --git a/docs/guide/examples/hello_world_tui.rs b/docs/guide/examples/hello_world_tui.rs
index ef9b7ec47..dc4f0ee71 100644
--- a/docs/guide/examples/hello_world_tui.rs
+++ b/docs/guide/examples/hello_world_tui.rs
@@ -1,20 +1,17 @@
#![allow(non_snake_case)]
+// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
use dioxus::prelude::*;
fn main() {
+ // launch the app in the terminal
dioxus_tui::launch(App);
}
+// create a component that renders a div with the text "Hello, world!"
fn App(cx: Scope) -> Element {
cx.render(rsx! {
div {
- width: "100%",
- height: "10px",
- background_color: "red",
- justify_content: "center",
- align_items: "center",
-
- "Hello world!"
+ "Hello, world!"
}
})
}
diff --git a/docs/guide/examples/hello_world_web.rs b/docs/guide/examples/hello_world_web.rs
index 5f813bbe1..7b013bc3d 100644
--- a/docs/guide/examples/hello_world_web.rs
+++ b/docs/guide/examples/hello_world_web.rs
@@ -1,10 +1,13 @@
#![allow(non_snake_case)]
+// import the prelude to get access to the `rsx!` macro and the `Scope` and `Element` types
use dioxus::prelude::*;
fn main() {
+ // launch the web app
dioxus_web::launch(App);
}
+// create a component that renders a div with the text "Hello, world!"
fn App(cx: Scope) -> Element {
cx.render(rsx! {
div {
diff --git a/docs/guide/examples/hooks_bad.rs b/docs/guide/examples/hooks_bad.rs
index ff9d3417a..cbaa54e90 100644
--- a/docs/guide/examples/hooks_bad.rs
+++ b/docs/guide/examples/hooks_bad.rs
@@ -7,60 +7,61 @@ fn main() {
dioxus_desktop::launch(App);
}
+#[rustfmt::skip]
fn App(cx: Scope) -> Element {
let you_are_happy = true;
let you_know_it = false;
// ANCHOR: conditional
- // ❌ don't call hooks in conditionals!
- // We must ensure that the same hooks will be called every time
- // But `if` statements only run if the conditional is true!
- // So we might violate rule 2.
- if you_are_happy && you_know_it {
- let something = use_state(cx, || "hands");
- println!("clap your {something}")
- }
-
- // ✅ instead, *always* call use_state
- // You can put other stuff in the conditional though
+// ❌ don't call hooks in conditionals!
+// We must ensure that the same hooks will be called every time
+// But `if` statements only run if the conditional is true!
+// So we might violate rule 2.
+if you_are_happy && you_know_it {
let something = use_state(cx, || "hands");
- if you_are_happy && you_know_it {
- println!("clap your {something}")
- }
+ println!("clap your {something}")
+}
+
+// ✅ instead, *always* call use_state
+// You can put other stuff in the conditional though
+let something = use_state(cx, || "hands");
+if you_are_happy && you_know_it {
+ println!("clap your {something}")
+}
// ANCHOR_END: conditional
// ANCHOR: closure
- // ❌ don't call hooks inside closures!
- // We can't guarantee that the closure, if used, will be called at the same time every time
- let _a = || {
- let b = use_state(cx, || 0);
- b.get()
- };
-
- // ✅ instead, move hook `b` outside
+// ❌ don't call hooks inside closures!
+// We can't guarantee that the closure, if used, will be called in the same order every time
+let _a = || {
let b = use_state(cx, || 0);
- let _a = || b.get();
+ b.get()
+};
+
+// ✅ instead, move hook `b` outside
+let b = use_state(cx, || 0);
+let _a = || b.get();
// ANCHOR_END: closure
let names: Vec<&str> = vec![];
// ANCHOR: loop
- // `names` is a Vec<&str>
+// `names` is a Vec<&str>
- // ❌ Do not use hooks in loops!
- // In this case, if the length of the Vec changes, we break rule 2
- for _name in &names {
- let is_selected = use_state(cx, || false);
- println!("selected: {is_selected}");
- }
+// ❌ Do not use hooks in loops!
+// In this case, if the length of the Vec changes, we break rule 2
+for _name in &names {
+ let is_selected = use_state(cx, || false);
+ println!("selected: {is_selected}");
+}
- // ✅ Instead, use a hashmap with use_ref
- let selection_map = use_ref(cx, HashMap::<&str, bool>::new);
+// ✅ Instead, use a hashmap with use_ref
+let selection_map = use_ref(cx, HashMap::<&str, bool>::new);
- for name in &names {
- let is_selected = selection_map.read()[name];
- println!("selected: {is_selected}");
- }
+for name in &names {
+ let is_selected = selection_map.read()[name];
+ println!("selected: {is_selected}");
+}
// ANCHOR_END: loop
cx.render(rsx!(()))
diff --git a/docs/guide/examples/hooks_counter.rs b/docs/guide/examples/hooks_counter.rs
index 1fb2a0bd7..86705f4af 100644
--- a/docs/guide/examples/hooks_counter.rs
+++ b/docs/guide/examples/hooks_counter.rs
@@ -7,12 +7,25 @@ fn main() {
// ANCHOR: component
fn App(cx: Scope) -> Element {
+ // count will be initialized to 0 the first time the component is rendered
let mut count = use_state(cx, || 0);
cx.render(rsx!(
h1 { "High-Five counter: {count}" }
- button { onclick: move |_| count += 1, "Up high!" }
- button { onclick: move |_| count -= 1, "Down low!" }
+ button {
+ onclick: move |_| {
+ // changing the count will cause the component to re-render
+ count += 1
+ },
+ "Up high!"
+ }
+ button {
+ onclick: move |_| {
+ // changing the count will cause the component to re-render
+ count -= 1
+ },
+ "Down low!"
+ }
))
}
// ANCHOR_END: component
diff --git a/docs/guide/examples/hooks_use_ref.rs b/docs/guide/examples/hooks_use_ref.rs
index 4910325de..00794fbb7 100644
--- a/docs/guide/examples/hooks_use_ref.rs
+++ b/docs/guide/examples/hooks_use_ref.rs
@@ -8,13 +8,12 @@ fn main() {
// ANCHOR: component
fn App(cx: Scope) -> Element {
let list = use_ref(cx, Vec::new);
- let list_formatted = format!("{:?}", *list.read());
cx.render(rsx!(
- p { "Current list: {list_formatted}" }
+ p { "Current list: {list.read():?}" }
button {
onclick: move |event| {
- list.write().push(event)
+ list.with_mut(|list| list.push(event));
},
"Click me!"
}
diff --git a/docs/guide/examples/meme_editor_dark_mode.rs b/docs/guide/examples/meme_editor_dark_mode.rs
index e394a4a7f..e62a64eb7 100644
--- a/docs/guide/examples/meme_editor_dark_mode.rs
+++ b/docs/guide/examples/meme_editor_dark_mode.rs
@@ -11,9 +11,10 @@ fn main() {
struct DarkMode(bool);
// ANCHOR_END: DarkMode_struct
+#[rustfmt::skip]
pub fn App(cx: Scope) -> Element {
// ANCHOR: context_provider
- use_shared_state_provider(cx, || DarkMode(false));
+use_shared_state_provider(cx, || DarkMode(false));
// ANCHOR_END: context_provider
let is_dark_mode = use_is_dark_mode(cx);
@@ -34,9 +35,10 @@ pub fn App(cx: Scope) -> Element {
}))
}
+#[rustfmt::skip]
pub fn use_is_dark_mode(cx: &ScopeState) -> bool {
// ANCHOR: use_context
- let dark_mode_context = use_shared_state::(cx);
+let dark_mode_context = use_shared_state::(cx);
// ANCHOR_END: use_context
dark_mode_context
diff --git a/docs/guide/examples/rendering_lists.rs b/docs/guide/examples/rendering_lists.rs
index 75dc69b58..b95e712dc 100644
--- a/docs/guide/examples/rendering_lists.rs
+++ b/docs/guide/examples/rendering_lists.rs
@@ -12,42 +12,80 @@ struct Comment {
id: usize,
}
+#[rustfmt::skip]
pub fn App(cx: Scope) -> Element {
// ANCHOR: render_list
- let comment_field = use_state(cx, String::new);
- let mut next_id = use_state(cx, || 0);
- let comments = use_ref(cx, Vec::::new);
+let comment_field = use_state(cx, String::new);
+let mut next_id = use_state(cx, || 0);
+let comments = use_ref(cx, Vec::::new);
- let comments_lock = comments.read();
- let comments_rendered = comments_lock.iter().map(|comment| {
- cx.render(rsx!(CommentComponent {
+let comments_lock = comments.read();
+let comments_rendered = comments_lock.iter().map(|comment| {
+ rsx!(CommentComponent {
+ key: "{comment.id}",
+ comment: comment.clone(),
+ })
+});
+
+cx.render(rsx!(
+ form {
+ onsubmit: move |_| {
+ comments.write().push(Comment {
+ content: comment_field.get().clone(),
+ id: *next_id.get(),
+ });
+ next_id += 1;
+
+ comment_field.set(String::new());
+ },
+ input {
+ value: "{comment_field}",
+ oninput: |event| comment_field.set(event.value.clone()),
+ }
+ input {
+ r#type: "submit",
+ }
+ },
+ comments_rendered,
+))
+ // ANCHOR_END: render_list
+}
+
+#[rustfmt::skip]
+pub fn AppForLoop(cx: Scope) -> Element {
+ // ANCHOR: render_list_for_loop
+let comment_field = use_state(cx, String::new);
+let mut next_id = use_state(cx, || 0);
+let comments = use_ref(cx, Vec::::new);
+
+cx.render(rsx!(
+ form {
+ onsubmit: move |_| {
+ comments.write().push(Comment {
+ content: comment_field.get().clone(),
+ id: *next_id.get(),
+ });
+ next_id += 1;
+
+ comment_field.set(String::new());
+ },
+ input {
+ value: "{comment_field}",
+ oninput: |event| comment_field.set(event.value.clone()),
+ }
+ input {
+ r#type: "submit",
+ }
+ },
+ for comment in &*comments.read() {
+ // Notice the body of this for loop is rsx code, not an expression
+ CommentComponent {
key: "{comment.id}",
comment: comment.clone(),
- }))
- });
-
- cx.render(rsx!(
- form {
- onsubmit: move |_| {
- comments.write().push(Comment {
- content: comment_field.get().clone(),
- id: *next_id.get(),
- });
- next_id += 1;
-
- comment_field.set(String::new());
- },
- input {
- value: "{comment_field}",
- oninput: |event| comment_field.set(event.value.clone()),
- }
- input {
- r#type: "submit",
- }
- },
- comments_rendered,
- ))
- // ANCHOR_END: render_list
+ }
+ }
+))
+ // ANCHOR_END: render_list_for_loop
}
#[inline_props]
diff --git a/docs/guide/examples/rsx_overview.rs b/docs/guide/examples/rsx_overview.rs
index 64fa24c78..93c991d0d 100644
--- a/docs/guide/examples/rsx_overview.rs
+++ b/docs/guide/examples/rsx_overview.rs
@@ -19,91 +19,151 @@ pub fn App(cx: Scope) -> Element {
))
}
+#[rustfmt::skip]
pub fn Empty(cx: Scope) -> Element {
// ANCHOR: empty
- cx.render(rsx!(div {}))
+cx.render(rsx!(div {
+ // attributes / listeners
+ // children
+}))
// ANCHOR_END: empty
}
+#[rustfmt::skip]
pub fn Children(cx: Scope) -> Element {
// ANCHOR: children
- cx.render(rsx!(ol {
- li {"First Item"}
- li {"Second Item"}
- li {"Third Item"}
- }))
+cx.render(rsx!(ol {
+ li {"First Item"}
+ li {"Second Item"}
+ li {"Third Item"}
+}))
// ANCHOR_END: children
}
+#[rustfmt::skip]
pub fn Fragments(cx: Scope) -> Element {
// ANCHOR: fragments
- cx.render(rsx!(
- p {"First Item"},
- p {"Second Item"},
- Fragment {
- span { "a group" },
- span { "of three" },
- span { "items" },
- }
- ))
+cx.render(rsx!(
+ p {"First Item"},
+ p {"Second Item"},
+ Fragment {
+ span { "a group" },
+ span { "of three" },
+ span { "items" },
+ }
+))
// ANCHOR_END: fragments
}
+#[rustfmt::skip]
+pub fn ManyRoots(cx: Scope) -> Element {
+ // ANCHOR: manyroots
+cx.render(rsx!(
+ p {"First Item"},
+ p {"Second Item"},
+))
+ // ANCHOR_END: manyroots
+}
+
+#[rustfmt::skip]
pub fn Attributes(cx: Scope) -> Element {
// ANCHOR: attributes
- cx.render(rsx!(a {
- href: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
- class: "primary_button",
- "Log In"
- }))
+cx.render(rsx!(a {
+ href: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
+ class: "primary_button",
+ color: "red",
+}))
// ANCHOR_END: attributes
}
+#[rustfmt::skip]
pub fn VariableAttributes(cx: Scope) -> Element {
// ANCHOR: variable_attributes
- let written_in_rust = true;
- let button_type = "button";
- cx.render(rsx!(button {
- disabled: "{written_in_rust}",
- class: "{button_type}",
- "Rewrite it in rust"
- }))
+let written_in_rust = true;
+let button_type = "button";
+cx.render(rsx!(button {
+ disabled: "{written_in_rust}",
+ class: "{button_type}",
+ "Rewrite it in rust"
+}))
// ANCHOR_END: variable_attributes
}
+#[rustfmt::skip]
pub fn CustomAttributes(cx: Scope) -> Element {
// ANCHOR: custom_attributes
cx.render(rsx!(b {
"customAttribute": "value",
- "Rust is Cool"
}))
// ANCHOR_END: custom_attributes
}
+#[rustfmt::skip]
pub fn Formatting(cx: Scope) -> Element {
// ANCHOR: formatting
- let coordinates = (42, 0);
- let country = "es";
- cx.render(rsx!(div {
- class: "country-{country}",
- "Coordinates: {coordinates:?}",
- // arbitrary expressions are allowed,
- // as long as they don't contain `{}`
- div {
- "{country.to_uppercase()}"
- },
- div {
- "{7*6}"
- },
- }))
- // ANCHOR_END: formatting
+let coordinates = (42, 0);
+let country = "es";
+cx.render(rsx!(div {
+ class: "country-{country}",
+ "position": "{coordinates:?}",
+ // arbitrary expressions are allowed,
+ // as long as they don't contain `{}`
+ div {
+ "{country.to_uppercase()}"
+ },
+ div {
+ "{7*6}"
+ },
+ // {} can be escaped with {{}}
+ div {
+ "{{}}"
+ },
+}))
+// ANCHOR_END: formatting
}
+#[rustfmt::skip]
pub fn Expression(cx: Scope) -> Element {
// ANCHOR: expression
- let text = "Dioxus";
- cx.render(rsx!(span {
- text.to_uppercase()
- }))
+let text = "Dioxus";
+cx.render(rsx!(span {
+ text.to_uppercase(),
+ // create a list of text from 0 to 9
+ (0..10).map(|i| rsx!{ i.to_string() })
+}))
// ANCHOR_END: expression
}
+
+#[rustfmt::skip]
+pub fn Loops(cx: Scope) -> Element {
+ // ANCHOR: loops
+cx.render(rsx!{
+ // use a for loop where the body itself is RSX
+ div {
+ // create a list of text from 0 to 9
+ for i in 0..3 {
+ // NOTE: the body of the loop is RSX not a rust statement
+ div {
+ "{i}"
+ }
+ }
+ }
+ // iterator equivalent
+ div {
+ (0..3).map(|i| rsx!{ div { "{i}" } })
+ }
+})
+ // ANCHOR_END: loops
+}
+
+#[rustfmt::skip]
+pub fn IfStatements(cx: Scope) -> Element {
+ // ANCHOR: ifstatements
+cx.render(rsx!{
+ // use if statements without an else
+ if true {
+ rsx!(div { "true" })
+ }
+})
+ // ANCHOR_END: ifstatements
+}
diff --git a/docs/guide/examples/use_future.rs b/docs/guide/examples/use_future.rs
index c0d41e5af..01464704a 100644
--- a/docs/guide/examples/use_future.rs
+++ b/docs/guide/examples/use_future.rs
@@ -12,49 +12,51 @@ struct ApiResponse {
image_url: String,
}
+#[rustfmt::skip]
fn App(cx: Scope) -> Element {
- // ANCHOR: use_future
- let future = use_future(cx, (), |_| async move {
- reqwest::get("https://dog.ceo/api/breeds/image/random")
- .await
- .unwrap()
- .json::()
- .await
- });
- // ANCHOR_END: use_future
+// ANCHOR: use_future
+let future = use_future(cx, (), |_| async move {
+ reqwest::get("https://dog.ceo/api/breeds/image/random")
+ .await
+ .unwrap()
+ .json::()
+ .await
+});
+// ANCHOR_END: use_future
- // ANCHOR: render
- cx.render(match future.value() {
- Some(Ok(response)) => rsx! {
- button {
- onclick: move |_| future.restart(),
- "Click to fetch another doggo"
+// ANCHOR: render
+cx.render(match future.value() {
+ Some(Ok(response)) => rsx! {
+ button {
+ onclick: move |_| future.restart(),
+ "Click to fetch another doggo"
+ }
+ div {
+ img {
+ max_width: "500px",
+ max_height: "500px",
+ src: "{response.image_url}",
}
- div {
- img {
- max_width: "500px",
- max_height: "500px",
- src: "{response.image_url}",
- }
- }
- },
- Some(Err(_)) => rsx! { div { "Loading dogs failed" } },
- None => rsx! { div { "Loading dogs..." } },
- })
- // ANCHOR_END: render
+ }
+ },
+ Some(Err(_)) => rsx! { div { "Loading dogs failed" } },
+ None => rsx! { div { "Loading dogs..." } },
+})
+// ANCHOR_END: render
}
+#[rustfmt::skip]
#[inline_props]
fn RandomDog(cx: Scope, breed: String) -> Element {
- // ANCHOR: dependency
- let future = use_future(cx, (breed,), |(breed,)| async move {
- reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random"))
- .await
- .unwrap()
- .json::()
- .await
- });
- // ANCHOR_END: dependency
+// ANCHOR: dependency
+let future = use_future(cx, (breed,), |(breed,)| async move {
+ reqwest::get(format!("https://dog.ceo/api/breed/{breed}/images/random"))
+ .await
+ .unwrap()
+ .json::()
+ .await
+});
+// ANCHOR_END: dependency
cx.render(rsx!(()))
}
diff --git a/docs/guide/src/en/SUMMARY.md b/docs/guide/src/en/SUMMARY.md
index 28ecfe688..83840fe90 100644
--- a/docs/guide/src/en/SUMMARY.md
+++ b/docs/guide/src/en/SUMMARY.md
@@ -7,6 +7,7 @@
- [Web](getting_started/web.md)
- [Hot Reload](getting_started/hot_reload.md)
- [Server-Side Rendering](getting_started/ssr.md)
+ - [Liveview](getting_started/liveview.md)
- [Terminal UI](getting_started/tui.md)
- [Mobile](getting_started/mobile.md)
- [Describing the UI](describing_ui/index.md)
diff --git a/docs/guide/src/en/async/spawn.md b/docs/guide/src/en/async/spawn.md
index d001204c6..b29f63ebf 100644
--- a/docs/guide/src/en/async/spawn.md
+++ b/docs/guide/src/en/async/spawn.md
@@ -8,16 +8,6 @@ The `use_future` and `use_coroutine` hooks are useful if you want to uncondition
> Note: `spawn` will always spawn a *new* future. You most likely don't want to call it on every render.
-The future must be `'static` – so any values captured by the task cannot carry any references to `cx`, such as a `UseState`.
-
-However, since you'll typically need a way to update the value of a hook, you can use `to_owned` to create a clone of the hook handle. You can then use that clone in the async closure.
-
-To make this a bit less verbose, Dioxus exports the `to_owned!` macro which will create a binding as shown above, which can be quite helpful when dealing with many values.
-
-```rust
-{{#include ../../../examples/spawn.rs:to_owned_macro}}
-```
-
Calling `spawn` will give you a `JoinHandle` which lets you cancel or pause the future.
## Spawning Tokio Tasks
diff --git a/docs/guide/src/en/async/use_coroutine.md b/docs/guide/src/en/async/use_coroutine.md
index 326141b2b..95c932901 100644
--- a/docs/guide/src/en/async/use_coroutine.md
+++ b/docs/guide/src/en/async/use_coroutine.md
@@ -1,12 +1,12 @@
# Coroutines
-Another good tool to keep in your async toolbox are coroutines. Coroutines are futures that can be manually stopped, started, paused, and resumed.
+Another tool in your async toolbox are coroutines. Coroutines are futures that can be manually stopped, started, paused, and resumed.
-Like regular futures, code in a Dioxus coroutine will run until the next `await` point before yielding. This low-level control over asynchronous tasks is quite powerful, allowing for infinitely looping tasks like WebSocket polling, background timers, and other periodic actions.
+Like regular futures, code in a coroutine will run until the next `await` point before yielding. This low-level control over asynchronous tasks is quite powerful, allowing for infinitely looping tasks like WebSocket polling, background timers, and other periodic actions.
## `use_coroutine`
-The basic setup for coroutines is the `use_coroutine` hook. Most coroutines we write will be polling loops using async/await.
+The `use_coroutine` hook allows you to create a coroutine. Most coroutines we write will be polling loops using async/await.
```rust
fn app(cx: Scope) -> Element {
@@ -50,11 +50,45 @@ if sync.is_running() {
This pattern is where coroutines are extremely useful – instead of writing all the complicated logic for pausing our async tasks like we would with JavaScript promises, the Rust model allows us to just not poll our future.
+## Yielding Values
+
+To yield values from a coroutine, simply bring in a `UseState` handle and set the value whenever your coroutine completes its work.
+
+The future must be `'static` – so any values captured by the task cannot carry any references to `cx`, such as a `UseState`.
+
+You can use [to_owned](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned) to create a clone of the hook handle which can be moved into the async closure.
+
+```rust
+let sync_status = use_state(cx, || Status::Launching);
+let sync_task = use_coroutine(cx, |rx: UnboundedReceiver| {
+ let sync_status = sync_status.to_owned();
+ async move {
+ loop {
+ delay_ms(1000).await;
+ sync_status.set(Status::Working);
+ }
+ }
+})
+```
+
+To make this a bit less verbose, Dioxus exports the `to_owned!` macro which will create a binding as shown above, which can be quite helpful when dealing with many values.
+
+```rust
+let sync_status = use_state(cx, || Status::Launching);
+let load_status = use_state(cx, || Status::Launching);
+let sync_task = use_coroutine(cx, |rx: UnboundedReceiver| {
+ to_owned![sync_status, load_status];
+ async move {
+ // ...
+ }
+})
+```
+
## Sending Values
You might've noticed the `use_coroutine` closure takes an argument called `rx`. What is that? Well, a common pattern in complex apps is to handle a bunch of async code at once. With libraries like Redux Toolkit, managing multiple promises at once can be challenging and a common source of bugs.
-With Coroutines, we have the opportunity to centralize our async logic. The `rx` parameter is an Unbounded Channel for code external to the coroutine to send data *into* the coroutine. Instead of looping on an external service, we can loop on the channel itself, processing messages from within our app without needing to spawn a new future. To send data into the coroutine, we would call "send" on the handle.
+With Coroutines, we can centralize our async logic. The `rx` parameter is an Channel that allows code external to the coroutine to send data *into* the coroutine. Instead of looping on an external service, we can loop on the channel itself, processing messages from within our app without needing to spawn a new future. To send data into the coroutine, we would call "send" on the handle.
```rust
@@ -103,7 +137,7 @@ async fn editor_service(rx: UnboundedReceiver) {
}
```
-We can combine coroutines with Fermi to emulate Redux Toolkit's Thunk system with much less headache. This lets us store all of our app's state *within* a task and then simply update the "view" values stored in Atoms. It cannot be understated how powerful this technique is: we get all the perks of native Rust tasks with the optimizations and ergonomics of global state. This means your *actual* state does not need to be tied up in a system like Fermi or Redux – the only Atoms that need to exist are those that are used to drive the display/UI.
+We can combine coroutines with [Fermi](https://docs.rs/fermi/latest/fermi/index.html) to emulate Redux Toolkit's Thunk system with much less headache. This lets us store all of our app's state *within* a task and then simply update the "view" values stored in Atoms. It cannot be understated how powerful this technique is: we get all the perks of native Rust tasks with the optimizations and ergonomics of global state. This means your *actual* state does not need to be tied up in a system like Fermi or Redux – the only Atoms that need to exist are those that are used to drive the display/UI.
```rust
static USERNAME: Atom = |_| "default".to_string();
@@ -152,27 +186,9 @@ async fn sync_service(mut rx: UnboundedReceiver, atoms: AtomRoot) {
}
```
-## Yielding Values
-
-To yield values from a coroutine, simply bring in a `UseState` handle and set the value whenever your coroutine completes its work.
-
-
-```rust
-let sync_status = use_state(cx, || Status::Launching);
-let sync_task = use_coroutine(cx, |rx: UnboundedReceiver| {
- to_owned![sync_status];
- async move {
- loop {
- delay_ms(1000).await;
- sync_status.set(Status::Working);
- }
- }
-})
-```
-
## Automatic injection into the Context API
-Coroutine handles are automatically injected through the context API. `use_coroutine_handle` with the message type as a generic can be used to fetch a handle.
+Coroutine handles are automatically injected through the context API. You can use the `use_coroutine_handle` hook with the message type as a generic to fetch a handle.
```rust
fn Child(cx: Scope) -> Element {
diff --git a/docs/guide/src/en/async/use_future.md b/docs/guide/src/en/async/use_future.md
index 0e3071bc1..864dd5acb 100644
--- a/docs/guide/src/en/async/use_future.md
+++ b/docs/guide/src/en/async/use_future.md
@@ -2,7 +2,7 @@
[`use_future`](https://docs.rs/dioxus-hooks/latest/dioxus_hooks/fn.use_future.html) lets you run an async closure, and provides you with its result.
-For example, we can make an API request inside `use_future`:
+For example, we can make an API request (using [reqwest](https://docs.rs/reqwest/latest/reqwest/index.html)) inside `use_future`:
```rust
{{#include ../../../examples/use_future.rs:use_future}}
@@ -25,7 +25,7 @@ The `UseFuture` handle provides a `restart` method. It can be used to execute th
## Dependencies
-Often, you will need to run the future again every time some value (e.g. a prop) changes. Rather than `.restart`ing it manually, you can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. Example:
+Often, you will need to run the future again every time some value (e.g. a prop) changes. Rather than calling `restart` manually, you can provide a tuple of "dependencies" to the hook. It will automatically re-run the future when any of those dependencies change. Example:
```rust
diff --git a/docs/guide/src/en/best_practices/antipatterns.md b/docs/guide/src/en/best_practices/antipatterns.md
index 36aee2c79..873af6e39 100644
--- a/docs/guide/src/en/best_practices/antipatterns.md
+++ b/docs/guide/src/en/best_practices/antipatterns.md
@@ -1,6 +1,6 @@
# Antipatterns
-This example shows what not to do and provides a reason why a given pattern is considered an "AntiPattern". Most anti-patterns are considered wrong due performance reasons, or for harming code re-usability.
+This example shows what not to do and provides a reason why a given pattern is considered an "AntiPattern". Most anti-patterns are considered wrong for performance or code re-usability reasons.
## Unnecessarily Nested Fragments
@@ -14,7 +14,7 @@ Only Component and Fragment nodes are susceptible to this issue. Dioxus mitigate
## Incorrect Iterator Keys
-As described in the conditional rendering chapter, list items must have unique keys that are associated with the same items across renders. This helps Dioxus associate state with the contained components, and ensures good diffing performance. Do not omit keys, unless you know that the list is static and will never change.
+As described in the [dynamic rendering chapter](../interactivity/dynamic_rendering.md#the-key-attribute), list items must have unique keys that are associated with the same items across renders. This helps Dioxus associate state with the contained components and ensures good diffing performance. Do not omit keys, unless you know that the list will never change.
```rust
{{#include ../../../examples/anti_patterns.rs:iter_keys}}
diff --git a/docs/guide/src/en/contributing.md b/docs/guide/src/en/contributing.md
index 0bfde1a15..35efa11ac 100644
--- a/docs/guide/src/en/contributing.md
+++ b/docs/guide/src/en/contributing.md
@@ -1,6 +1,6 @@
# Contributing
-Development happens in the [Dioxus GitHub repository](https://github.com/DioxusLabs/dioxus). If you've found a bug or have an idea for a feature, please submit an issue (check if someone hasn't [done it already](https://github.com/DioxusLabs/dioxus/issues) though).
+Development happens in the [Dioxus GitHub repository](https://github.com/DioxusLabs/dioxus). If you've found a bug or have an idea for a feature, please submit an issue (but first check if someone hasn't [done it already](https://github.com/DioxusLabs/dioxus/issues)).
[GitHub discussions](https://github.com/DioxusLabs/dioxus/discussions) can be used as a place to ask for help or talk about features. You can also join [our Discord channel](https://discord.gg/XgGxMSkvUM) where some development discussion happens.
@@ -10,11 +10,11 @@ If you'd like to improve the docs, PRs are welcome! Both Rust docs ([source](htt
## Working on the Ecosystem
-Part of what makes React great is the rich ecosystem. We'd like the same for Dioxus! So if you have a library in mind that you'd like to write, and that you think many people would benefit from, it will be appreciated. You can [browse npm.js](https://www.npmjs.com/search?q=keywords:react-component) for inspiration.
+Part of what makes React great is the rich ecosystem. We'd like the same for Dioxus! So if you have a library in mind that you'd like to write and many people would benefit from, it will be appreciated. You can [browse npm.js](https://www.npmjs.com/search?q=keywords:react-component) for inspiration.
## Bugs & Features
-If you've fixed [an issue that's open](https://github.com/DioxusLabs/dioxus/issues), feel free to submit a PR! You can also take a look at [the roadmap](./roadmap.md) and work on something in there. Consider [reaching out](https://discord.gg/XgGxMSkvUM) to the team first to make sure everyone's on the same page, and you don't do useless work!
+If you've fixed [an open issue](https://github.com/DioxusLabs/dioxus/issues), feel free to submit a PR! You can also take a look at [the roadmap](./roadmap.md) and work on something in there. Consider [reaching out](https://discord.gg/XgGxMSkvUM) to the team first to make sure everyone's on the same page, and you don't do useless work!
All pull requests (including those made by a team member) must be approved by at least one other team member.
Larger, more nuanced decisions about design, architecture, breaking changes, trade-offs, etc. are made by team consensus.
diff --git a/docs/guide/src/en/custom_renderer/index.md b/docs/guide/src/en/custom_renderer/index.md
index 347176b1f..17c2cca07 100644
--- a/docs/guide/src/en/custom_renderer/index.md
+++ b/docs/guide/src/en/custom_renderer/index.md
@@ -11,46 +11,50 @@ Implementing the renderer is fairly straightforward. The renderer needs to:
1. Handle the stream of edits generated by updates to the virtual DOM
2. Register listeners and pass events into the virtual DOM's event system
-Essentially, your renderer needs to implement the `RealDom` trait and generate `EventTrigger` objects to update the VirtualDOM. From there, you'll have everything needed to render the VirtualDOM to the screen.
+Essentially, your renderer needs to process edits and generate events to update the VirtualDOM. From there, you'll have everything needed to render the VirtualDOM to the screen.
Internally, Dioxus handles the tree relationship, diffing, memory management, and the event system, leaving as little as possible required for renderers to implement themselves.
-For reference, check out the javascript interperter or tui renderer as a starting point for your custom renderer.
+For reference, check out the [javascript interpreter](https://github.com/DioxusLabs/dioxus/tree/master/packages/interpreter) or [tui renderer](https://github.com/DioxusLabs/dioxus/tree/master/packages/tui) as a starting point for your custom renderer.
-## DomEdits
+## Templates
-The "DomEdit" type is a serialized enum that represents an atomic operation occurring on the RealDom. The variants roughly follow this set:
+Dioxus is built around the concept of [Templates](https://docs.rs/dioxus-core/latest/dioxus_core/prelude/struct.Template.html). Templates describe a UI tree known at compile time with dynamic parts filled at runtime. This is useful internally to make skip diffing static nodes, but it is also useful for the renderer to reuse parts of the UI tree. This can be useful for things like a list of items. Each item could contain some static parts and some dynamic parts. The renderer can use the template to create a static part of the UI once, clone it for each element in the list, and then fill in the dynamic parts.
+
+## Mutations
+
+The `Mutation` type is a serialized enum that represents an operation that should be applied to update the UI. The variants roughly follow this set:
```rust
-enum DomEdit {
- PushRoot,
+enum Mutation {
AppendChildren,
+ AssignId,
+ CreatePlaceholder,
+ CreateTextNode,
+ HydrateText,
+ LoadTemplate,
ReplaceWith,
+ ReplacePlaceholder,
InsertAfter,
InsertBefore,
- Remove,
- CreateTextNode,
- CreateElement,
- CreateElementNs,
- CreatePlaceholder,
+ SetAttribute,
+ SetText,
NewEventListener,
RemoveEventListener,
- SetText,
- SetAttribute,
- RemoveAttribute,
- PopRoot,
+ Remove,
+ PushRoot,
}
```
The Dioxus diffing mechanism operates as a [stack machine](https://en.wikipedia.org/wiki/Stack_machine) where the "push_root" method pushes a new "real" DOM node onto the stack and "append_child" and "replace_with" both remove nodes from the stack.
-### An example
+### An Example
-For the sake of understanding, lets consider this example – a very simple UI declaration:
+For the sake of understanding, let's consider this example – a very simple UI declaration:
```rust
-rsx!( h1 {"hello world"} )
+rsx!( h1 {"count {x}"} )
```
To get things started, Dioxus must first navigate to the container of this h1 tag. To "navigate" here, the internal diffing algorithm generates the DomEdit `PushRoot` where the ID of the root is the container.
@@ -66,7 +70,7 @@ stack: [
]
```
-Next, Dioxus will encounter the h1 node. The diff algorithm decides that this node needs to be created, so Dioxus will generate the DomEdit `CreateElement`. When the renderer receives this instruction, it will create an unmounted node and push into its own stack:
+Next, Dioxus will encounter the h1 node. The diff algorithm decides that this node needs to be created, so Dioxus will generate the DomEdit `CreateElement`. When the renderer receives this instruction, it will create an unmounted node and push it into its own stack:
```rust
instructions: [
@@ -91,7 +95,7 @@ stack: [
"hello world"
]
```
-Remember, the text node is not attached to anything (it is unmounted) so Dioxus needs to generate an Edit that connects the text node to the h1 element. It depends on the situation, but in this case we use `AppendChildren`. This pops the text node off the stack, leaving the h1 element as the next element in line.
+Remember, the text node is not attached to anything (it is unmounted) so Dioxus needs to generate an Edit that connects the text node to the h1 element. It depends on the situation, but in this case, we use `AppendChildren`. This pops the text node off the stack, leaving the h1 element as the next element in line.
```rust
instructions: [
@@ -141,13 +145,13 @@ Over time, our stack looked like this:
[]
```
-Notice how our stack is empty once UI has been mounted. Conveniently, this approach completely separates the VirtualDOM and the Real DOM. Additionally, these edits are serializable, meaning we can even manage UIs across a network connection. This little stack machine and serialized edits makes Dioxus independent of platform specifics.
+Notice how our stack is empty once UI has been mounted. Conveniently, this approach completely separates the Virtual DOM and the Real DOM. Additionally, these edits are serializable, meaning we can even manage UIs across a network connection. This little stack machine and serialized edits make Dioxus independent of platform specifics.
Dioxus is also really fast. Because Dioxus splits the diff and patch phase, it's able to make all the edits to the RealDOM in a very short amount of time (less than a single frame) making rendering very snappy. It also allows Dioxus to cancel large diffing operations if higher priority work comes in while it's diffing.
It's important to note that there _is_ one layer of connectedness between Dioxus and the renderer. Dioxus saves and loads elements (the PushRoot edit) with an ID. Inside the VirtualDOM, this is just tracked as a u64.
-Whenever a `CreateElement` edit is generated during diffing, Dioxus increments its node counter and assigns that new element its current NodeCount. The RealDom is responsible for remembering this ID and pushing the correct node when PushRoot(ID) is generated. Dioxus reclaims IDs of elements when removed. To stay in sync with Dioxus you can use a sparce Vec (Vec