The ✨Dioxus CLI✨ is a tool to get Dioxus projects off the ground.
There's no documentation for commands here,but you can see all commands using dx --help once you've installed the CLI!Furthermore, you can run dx <command> --help to get help with a specific command.
Dioxus is designed to be cross-platform by default. This means that it should be easy to build applications that run on the web, desktop, and mobile. However, Dioxus should also be flexible enough to allow users to opt into platform-specific features when needed. The use_eval is one example of this. By default, Dioxus does not assume that the platform supports JavaScript, but it does provide a hook that allows users to opt into JavaScript when needed.
As Dioxus applications grow, they should remain relatively performant without the need for manual optimizations. There will be cases where manual optimizations are needed, but Dioxus should try to make these cases as rare as possible.
One of the benefits of the core architecture of Dioxus is that it delivers reasonable performance even when components are rerendered often. It is based on a Virtual Dom which performs diffing which should prevent unnecessary re-renders even when large parts of the component tree are rerun. On top of this, Dioxus groups static parts of the RSX tree together to skip diffing them entirely.
As teams grow, the Type safety of Rust is a huge advantage. Dioxus should leverage this advantage to make it easy to build applications with large teams.
To take full advantage of Rust's type system, Dioxus should try to avoid exposing public Any types and string-ly typed APIs where possible.
Dioxus should be easy to learn and ergonomic to use.
The API of Dioxus attempts to remain close to React's API where possible. This makes it easier for people to learn Dioxus if they already know React
We can avoid the tradeoff between simplicity and flexibility by providing multiple layers of API: One for the very common use case, one for low-level control
Hooks: the hooks crate has the most common use cases, but cx.hook provides a way to access the underlying persistent reference if needed.
The builder pattern in platform Configs: The builder pattern is used to default to the most common use case, but users can change the defaults if needed.
Documentation:
All public APIs should have rust documentation
Examples should be provided for all public features. These examples both serve as documentation and testing. They are checked by CI to ensure that they continue to compile
The most common workflows should be documented in the guide
This feature set and roadmap can help you decide if what Dioxus can do today works for you.
If a feature that you need doesn't exist or you want to contribute to projects on the roadmap, feel free to get involved by joining the discord.
Generally, here's the status of each platform:
Web: Dioxus is a great choice for pure web-apps – especially for CRUD/complex apps. However, it does lack the ecosystem of React, so you might be missing a component library or some useful hook.
SSR: Dioxus is a great choice for pre-rendering, hydration, and rendering HTML on a web endpoint. Be warned – the VirtualDom is not (currently) Send + Sync.
Desktop: You can build very competent single-window desktop apps right now. However, multi-window apps require support from Dioxus core and are not ready.
Mobile: Mobile support is very young. You'll be figuring things out as you go and there are not many support crates for peripherals.
LiveView: LiveView support is very young. You'll be figuring things out as you go. Thankfully, none of it is too hard and any work can be upstreamed into Dioxus.
While not currently fully implemented, the expectation is that LiveView apps can be a hybrid between Wasm and server-rendered where only portions of a page are "live" and the rest of the page is either server-rendered, statically generated, or handled by the host SPA.
We are currently working on a native renderer for Dioxus using WGPU called Blitz. This will allow you to build apps that are rendered natively for iOS, Android, and Desktop.
A slab acts like a hashmap with integer keys if you don't care about the value of the keys. It is internally backed by a dense vector which makes it more efficient than a hashmap. When you insert a value into a slab, it returns an integer key that you can use to retrieve the value later.
How does Dioxus use slabs?
Dioxus uses "synchronized slabs" to communicate between the renderer and the VDOM. When a node is created in the Virtual DOM, an (elementId, mutation) pair is passed to the renderer to identify that node, which the renderer will then render in actual DOM. These ids are also used by the Virtual Dom to reference that node in future mutations, like setting an attribute on a node or removing a node. When the renderer sends an event to the Virtual Dom, it sends the ElementId of the node that the event was triggered on. The Virtual DOM uses this id to find that node in the slab and then run the necessary event handlers.
The virtual DOM is a tree of scopes. A new Scope is created for every component when it is first rendered and recycled when the component is unmounted.
Scopes serve three main purposes:
They store the state of hooks used by the component
They store the current and previous versions of the VNode that was rendered, so they can bediffed to generate the set of mutations needed to re-render it.
The Virtual DOM will only ever re-render a Scope if it is marked as dirty. Each hook is responsible for marking the Scope as dirty if the state has changed. Hooks can mark a scope as dirty by sending a message to the Virtual Dom's channel. You can see the implementations for the hooks dioxus includes by default on how this is done. Calling needs_update() on a hook will also cause it to mark its scope as dirty.
There are generally two ways a scope is marked as dirty:
The renderer triggers an event: An event listener on this event may be called, which may mark acomponent as dirty, if processing the event resulted in any generated any mutations.
The renderer callswait_for_work:This polls dioxus internal future queue. One of these futures may mark a component as dirty.
Once at least one Scope is marked as dirty, the renderer can call render_with_deadline to diff the dirty scopes.
When a user clicks the "up high" button, the root Scope will be marked as dirty by the use_state hook. The desktop renderer will then call render_with_deadline, which will diff the root Scope.
To start the diffing process, the component function is run. After the root component is run it, the root Scope will look like this:
Next, the Virtual DOM will compare the new VNode with the previous VNode and only update the parts of the tree that have changed. Because of this approach, when a component is re-rendered only the parts of the tree that have changed will be updated in the DOM by the renderer.
The diffing algorithm goes through the list of dynamic attributes and nodes and compares them to the previous VNode. If the attribute or node has changed, a mutation that describes the change is added to the mutation list.
Here is what the diffing algorithm looks like for the root Scope (red lines indicate that a mutation was generated, and green lines indicate that no mutation was generated)
While it is technically acceptable to have a Mutex or a RwLock in the props, they will be difficult to use.
Suppose you have a struct User containing the field username: String. If you pass a Mutex<User> prop to a UserComponent component, that component may wish to pass the username as a &str prop to a child component. However, it cannot pass that borrowed field down, since it only would live as long as the Mutex's lock, which belongs to the UserComponent function. Therefore, the component will be forced to clone the username field.
That should be it! You should have nearly all the knowledge required on how to implement your renderer. We're super interested in seeing Dioxus apps brought to custom desktop renderers, mobile renderers, video game UI, and even augmented reality! If you're interested in contributing to any of these projects, don't be afraid to reach out or join the community.
This approach to error handling is best in apps that have "well defined" error states. Consider using a crate like thiserror or anyhow to simplify the generation of the error types.
This pattern is widely popular in many contexts and is particularly helpful whenever your code generates a non-recoverable error. You can gracefully capture these "global" error states without panicking or mucking up state.
There's a lot of these, so if you're having trouble implementing something, or you just want to see cool thingsthat you can do with Dioxus, make sure to give these a look!
Each of the examples in the main repository also has a permalink attached, in case the main one doesn't work.However, permalinks lead to an older "archived" version, so if you're reading this a long time in the future in a galaxy far, far away...The examples in the permalinks might not work.
Package-specific examples from the main repository. To learn more about these packages, search them up on crates.io, or navigate from the examples to the root of the package.