A comprehensive utility library for Dioxus applications providing state management, browser APIs, and fullstack development utilities.
dioxus-utils is a collection of utilities designed to simplify common tasks in Dioxus applications. It provides abstractions for data loading states, dialog/form management, browser interactions, and seamless client/server code sharing for fullstack applications.
- State Management:
DataStateandRenderStatefor managing async data loading states - Dialog Management:
DialogValuefor tracking form changes in dialogs - Browser Utilities: Console logging, JavaScript evaluation, UUID generation, date/time handling
- Fullstack Support: Client/server compatible utilities for focus management, local storage, page reload, and async sleep
- Global Settings: Access to window location and local storage through
GlobalAppSettings
This project uses a subset of dioxus-utils APIs. The examples below mirror the exact
patterns in the codebase to reduce drift.
Best practice here is to keep loading logic in a get_data helper and keep the
component focused on rendering:
use dioxus::prelude::*;
#[component]
pub fn RenderSettingsPage() -> Element {
let cs = use_signal(|| VadSettingsState::default());
let cs_ra = cs.read();
let input_data = match get_data(cs, &*cs_ra) {
Ok(input_data) => input_data,
Err(err) => return err,
};
render_vad_settings(cs, input_data)
}
fn get_data<'s>(
mut cs: Signal<VadSettingsState>,
cs_ra: &'s VadSettingsState,
) -> Result<&'s VadSettingsData, Element> {
match cs_ra.data.as_ref() {
dioxus_utils::RenderState::None => {
let lang = cs_ra.lang;
spawn(async move {
cs.write().data.set_loading();
let data = crate::api::vad_settings::get_vad_settings(lang).await;
match data {
Ok(data) => cs.write().set_data(data),
Err(err) => cs.write().data.set_error(err.to_string()),
}
});
Err(crate::components::loading())
}
dioxus_utils::RenderState::Loading => Err(crate::components::loading()),
dioxus_utils::RenderState::Loaded(data) => Ok(data),
dioxus_utils::RenderState::Error(err) => {
Err(crate::components::loading_data_error(err))
}
}
}After mutations (save/delete), the code resets the data so it reloads:
state.write().data.reset();Some pages use additional helpers beyond set_value:
// Mark data as loaded without changing the payload type
state.write().data.set_loaded(());
// Read only when loaded, otherwise fall back
let Some(data) = state.read().data.try_unwrap_as_loaded() else {
return vec![];
};
// Guard validation until initial data arrives
if !state.read().data.has_value() {
return false;
}Used for lightweight client-side persistence:
use dioxus_utils::js::GlobalAppSettings;
const STORAGE_KEY: &str = "client-view-search";
pub fn get() -> Vec<String> {
let result = GlobalAppSettings::get_local_storage().get(STORAGE_KEY);
result
.unwrap_or_default()
.split(';')
.map(|itm| itm.to_string())
.collect()
}
pub fn save(items: &[String]) {
let joined = items.join(";");
GlobalAppSettings::get_local_storage().set(STORAGE_KEY, joined.as_str());
}Used to throttle refresh loops:
use dioxus_utils::js::sleep;
use std::time::Duration;
loop {
refresh_data(cs).await;
sleep(Duration::from_secs(3)).await;
}Used for lightweight error logging in async loops:
dioxus_utils::console_log(
format!("Error reading background data. Err:{:?}", err).as_str(),
);Used in the toast helper to run small JS snippets:
let js = format!("document.getElementById('toast-message').innerText = \"{}\";", msg);
let _ = dioxus_utils::eval(js.as_str());Used when creating new items with empty ids:
let id = if item.id.is_empty() {
dioxus_utils::generate_uuid()
} else {
item.id.clone()
};Used when building export payloads:
let now = dioxus_utils::now_date_time();
result.push_str(format!("Timestamp: {}", now.to_rfc3339()).as_str());DialogValueis not currently used in this repo.- Focus helpers are implemented locally in
src/web/set_focus.rs.
Add this to your Cargo.toml:
[dependencies]
dioxus-utils = { tag = "{last_tag}", git = "https://github.com/MyJetTools/dioxus-utils.git" }To enable fullstack features (client/server compatible code):
[dependencies]
dioxus-utils = {
tag = "{last_tag}",
git = "https://github.com/MyJetTools/dioxus-utils.git",
features = ["fullstack"]
}
[features]
server = [..., "dioxus-utils/server"]Available Features:
fullstack: Enables fullstack utilities (focus, local storage, page reload, sleep)server: Enables server-side implementations (UUID generation, date/time, console logging)
DataState<T> is a wrapper around RenderState<T> that tracks whether data has been loaded at least once. Useful for managing async data loading in components.
States:
None: Initial state, no data loadedLoading: Data is currently being fetchedLoaded(T): Data successfully loadedError(String): Error occurred during loading
Example:
use dioxus::prelude::*;
use dioxus_utils::{DataState, RenderState};
fn MyComponent() -> Element {
let mut data_state = use_signal(|| DataState::<Vec<String>>::new());
match data_state.read().as_ref() {
RenderState::None => {
spawn(async move {
data_state.write().set_loading();
let result = fetch_data().await;
match result {
Ok(data) => data_state.write().set_value(data),
Err(e) => data_state.write().set_error(e),
}
});
rsx! { "Loading..." }
}
RenderState::Loading => rsx! { "Loading..." },
RenderState::Loaded(data) => rsx! {
for item in data {
div { "{item}" }
}
},
RenderState::Error(err) => rsx! { "Error: {err}" },
}
}Key Methods:
new(): Create empty stateset_loading(): Mark as loadingset_value(value): Set loaded dataset_error(err): Set error statereset(): Return toNoneto trigger a reload
RenderState<T> is the core state enum used by DataState. Can be used directly for simpler cases.
Example:
use dioxus_utils::RenderState;
let mut state = RenderState::<String>::new();
state.set_loading();
// ... later
state.set_loaded("Hello".to_string());DialogValue<T> tracks the initial and current value of a form field, useful for dialogs where you need to detect changes and allow cancellation.
Example:
use dioxus_utils::DialogValue;
fn EditDialog() -> Element {
let mut name = use_signal(|| DialogValue::new("Initial Name".to_string()));
rsx! {
input {
value: "{name.read().get_value()}",
oninput: move |e| name.write().set_value(e.value()),
}
button {
onclick: move |_| {
if name.read().is_value_updated() {
// Save changes
save_name(name.read().get_value());
}
},
"Save"
}
button {
onclick: move |_| {
// Reset to initial value
name.write().init(name.read().get_init_value().clone());
},
"Cancel"
}
}
}Key Methods:
new(value): Create with initial valueinit(value): Reset both initial and current valueset_value(value): Update current valueget_value(): Get current valueget_init_value(): Get initial valueis_value_updated(): Check if current differs from initialget_value_mut(): Get mutable reference to current value
console_log() provides platform-agnostic logging that works on both client and server.
Client: Logs to browser console via JavaScript Server: Logs to stdout
Example:
use dioxus_utils::console_log;
console_log("Debug message");
console_log(format!("User ID: {}", user_id));eval(js) evaluates JavaScript code. On server, returns JsValue::NULL.
Example:
use dioxus_utils::eval;
let result = eval("Math.max(1, 2, 3)");generate_uuid() generates a UUID v4 string.
Client: Uses crypto.randomUUID() via JavaScript
Server: Uses uuid crate
Example:
use dioxus_utils::generate_uuid;
let id = generate_uuid();
// Returns: "550e8400-e29b-41d4-a716-446655440000"now_date_time() returns current date/time as DateTimeAsMicroseconds from rust-extensions.
Client: Uses JavaScript Date().toISOString()
Server: Uses system time
Example:
use dioxus_utils::now_date_time;
let now = now_date_time();Available when fullstack feature is enabled.
This repo uses a local helper (src/web/set_focus.rs) instead of the dioxus-utils
focus helper. If you want to switch to the library helper, add it and update usages.
WebLocalStorage provides access to browser local storage.
Client: Uses web_sys::Storage
Server: Mock implementation (no-op)
Example:
use dioxus_utils::js::fullstack::WebLocalStorage;
let storage = GlobalAppSettings::new().get_local_storage();
storage.set("key", "value");
let value = storage.get("key");
storage.delete("key");reload_page() reloads the current page.
Example:
use dioxus_utils::js::fullstack::reload_page;
button {
onclick: move |_| reload_page(),
"Reload"
}sleep(duration) provides async sleep functionality.
Client: Uses gloo-timers
Server: Uses tokio::time::sleep
Example:
use dioxus_utils::js::fullstack::sleep;
use std::time::Duration;
async fn delayed_action() {
sleep(Duration::from_secs(1)).await;
// Continue after 1 second
}GlobalAppSettings provides access to window location and local storage.
Example:
use dioxus_utils::js::GlobalAppSettings;
let href = GlobalAppSettings::get_href(); // Full URL
let origin = GlobalAppSettings::get_origin(); // Origin URL
let storage = GlobalAppSettings::get_local_storage();use dioxus::prelude::*;
use dioxus_utils::{
DataState, RenderState, DialogValue,
console_log, generate_uuid, now_date_time,
};
use dioxus_utils::js::fullstack::*;
fn App() -> Element {
let mut users = use_signal(|| DataState::<Vec<User>>::new());
let mut edit_dialog = use_signal(|| None::<DialogValue<String>>);
use_effect(move || {
spawn(async move {
users.write().set_loading();
// Fetch users...
users.write().set_loaded(vec![]);
});
});
rsx! {
match users.read().as_ref() {
RenderState::Loading => rsx! { "Loading users..." },
RenderState::Loaded(users_list) => rsx! {
for user in users_list {
div { "{user.name}" }
}
},
RenderState::Error(err) => rsx! { "Error: {err}" },
_ => rsx! {},
}
button {
onclick: move |_| {
let id = generate_uuid();
console_log(&format!("Created user with ID: {}", id));
},
"Create User"
}
}
}dioxus: Core Dioxus frameworkweb-sys: WebAssembly bindings for web APIsjs-sys: JavaScript bindingsrust-extensions: Utility extensions (for DateTimeAsMicroseconds)gloo-timers: Timer utilities for webtokio: Async runtime (optional, for server feature)uuid: UUID generation (optional, for server feature)
- Web: Full support for all features
- Server: Supported features when
serverfeature is enabled - Fullstack: Seamless client/server code sharing with
fullstackfeature
[Add your license here]
[Add contribution guidelines here]