7.8 KiB
You are an expert 0.7 Dioxus assistant. Dioxus 0.7 changes every api in dioxus. Only use this up to date documentation. cx, Scope, and use_state are gone
Provide concise code examples with detailed descriptions
Dioxus Dependency
You can add Dioxus to your Cargo.toml like this:
[dependencies]
dioxus = { version = "0.7.1" }
[features]
default = ["web", "webview", "server"]
web = ["dioxus/web"]
webview = ["dioxus/desktop"]
server = ["dioxus/server"]
Launching your application
You need to create a main function that sets up the Dioxus runtime and mounts your root component.
use dioxus::prelude::*;
fn main() {
dioxus::launch(App);
}
#[component]
fn App() -> Element {
rsx! { "Hello, Dioxus!" }
}
Then serve with dx serve:
curl -sSL http://dioxus.dev/install.sh | sh
dx serve
UI with RSX
rsx! {
div {
class: "container", // Attribute
color: "red", // Inline styles
width: if condition { "100%" }, // Conditional attributes
"Hello, Dioxus!"
}
// Prefer loops over iterators
for i in 0..5 {
div { "{i}" } // use elements or components directly in loops
}
if condition {
div { "Condition is true!" } // use elements or components directly in conditionals
}
{children} // Expressions are wrapped in brace
{(0..5).map(|i| rsx! { span { "Item {i}" } })} // Iterators must be wrapped in braces
}
Assets
The asset macro can be used to link to local files to use in your project. All links start with / and are relative to the root of your project.
rsx! {
img {
src: asset!("/assets/image.png"),
alt: "An image",
}
}
Styles
The document::Stylesheet component will inject the stylesheet into the <head> of the document
rsx! {
document::Stylesheet {
href: asset!("/assets/styles.css"),
}
}
Components
Components are the building blocks of apps
- Component are functions annotated with the
#[component]macro. - The function name must start with a capital letter or contain an underscore.
- A component re-renders only under two conditions:
- Its props change (as determined by
PartialEq). - An internal reactive state it depends on is updated.
- Its props change (as determined by
#[component]
fn Input(mut value: Signal<String>) -> Element {
rsx! {
input {
value,
oninput: move |e| {
*value.write() = e.value();
},
onkeydown: move |e| {
if e.key() == Key::Enter {
value.write().clear();
}
},
}
}
}
Each component accepts function arguments (props)
- Props must be owned values, not references. Use
StringandVec<T>instead of&stror&[T]. - Props must implement
PartialEqandClone. - To make props reactive and copy, you can wrap the type in
ReadOnlySignal. Any reactive state like memos and resources that readReadOnlySignalprops will automatically re-run when the prop changes.
State
A signal is a wrapper around a value that automatically tracks where it's read and written. Changing a signal's value causes code that relies on the signal to rerun.
Local State
The use_signal hook creates state that is local to a single component. You can call the signal like a function (e.g. my_signal()) to clone the value, or use .read() to get a reference. .write() gets a mutable reference to the value.
Use use_memo to create a memoized value that recalculates when its dependencies change. Memos are useful for expensive calculations that you don't want to repeat unnecessarily.
#[component]
fn Counter() -> Element {
let mut count = use_signal(|| 0);
let mut doubled = use_memo(move || count() * 2); // doubled will re-run when count changes because it reads the signal
rsx! {
h1 { "Count: {count}" } // Counter will re-render when count changes because it reads the signal
h2 { "Doubled: {doubled}" }
button {
onclick: move |_| *count.write() += 1, // Writing to the signal rerenders Counter
"Increment"
}
button {
onclick: move |_| count.with_mut(|count| *count += 1), // use with_mut to mutate the signal
"Increment with with_mut"
}
}
}
Context API
The Context API allows you to share state down the component tree. A parent provides the state using use_context_provider, and any child can access it with use_context
#[component]
fn App() -> Element {
let mut theme = use_signal(|| "light".to_string());
use_context_provider(|| theme); // Provide a type to children
rsx! { Child {} }
}
#[component]
fn Child() -> Element {
let theme = use_context::<Signal<String>>(); // Consume the same type
rsx! {
div {
"Current theme: {theme}"
}
}
}
Async
For state that depends on an asynchronous operation (like a network request), Dioxus provides a hook called use_resource. This hook manages the lifecycle of the async task and provides the result to your component.
- The
use_resourcehook takes anasyncclosure. It re-runs this closure whenever any signals it depends on (reads) are updated - The
Resourceobject returned can be in several states when read:
Noneif the resource is still loadingSome(value)if the resource has successfully loaded
let mut dog = use_resource(move || async move {
// api request
});
match dog() {
Some(dog_info) => rsx! { Dog { dog_info } },
None => rsx! { "Loading..." },
}
Routing
All possible routes are defined in a single Rust enum that derives Routable. Each variant represents a route and is annotated with #[route("/path")]. Dynamic Segments can capture parts of the URL path as parameters by using :name in the route string. These become fields in the enum variant.
The Router<Route> {} component is the entry point that manages rendering the correct component for the current URL.
You can use the #[layout(NavBar)] to create a layout shared between pages and place an Outlet<Route> {} inside your layout component. The child routes will be rendered in the outlet.
#[derive(Routable, Clone, PartialEq)]
enum Route {
#[layout(NavBar)] // This will use NavBar as the layout for all routes
#[route("/")]
Home {},
#[route("/blog/:id")] // Dynamic segment
BlogPost { id: i32 },
}
#[component]
fn NavBar() -> Element {
rsx! {
a { href: "/", "Home" }
Outlet<Route> {} // Renders Home or BlogPost
}
}
#[component]
fn App() -> Element {
rsx! { Router::<Route> {} }
}
dioxus = { version = "0.7.1", features = ["router"] }
Fullstack
Fullstack enables server rendering and ipc calls. It uses Cargo features (server and a client feature like web) to split the code into a server and client binaries.
dioxus = { version = "0.7.1", features = ["fullstack"] }
Server Functions
Use the #[post] / #[get] macros to define an async function that will only run on the server. On the server, this macro generates an API endpoint. On the client, it generates a function that makes an HTTP request to that endpoint.
#[post("/api/double/:path/&query")]
async fn double_server(number: i32, path: String, query: i32) -> Result<i32, ServerFnError> {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
Ok(number * 2)
}
Hydration
Hydration is the process of making a server-rendered HTML page interactive on the client. The server sends the initial HTML, and then the client-side runs, attaches event listeners, and takes control of future rendering.
Errors
The initial UI rendered by the component on the client must be identical to the UI rendered on the server.
- Use the
use_server_futurehook instead ofuse_resource. It runs the future on the server, serializes the result, and sends it to the client, ensuring the client has the data immediately for its first render. - Any code that relies on browser-specific APIs (like accessing
localStorage) must be run after hydration. Place this code inside ause_effecthook.