Documentation Index
Fetch the complete documentation index at: https://mintlify.com/DioxusLabs/dioxus/llms.txt
Use this file to discover all available pages before exploring further.
Global signals and memos provide application-wide state that can be accessed from any component without context or prop drilling. They’re initialized lazily on first use and live for the entire application lifetime.
Basic Usage
use dioxus::prelude::*;
// Define global state with static
static COUNT: GlobalSignal<i32> = Signal::global(|| 0);
fn main() {
dioxus::launch(App);
}
fn App() -> Element {
rsx! {
Display {}
Controls {}
}
}
#[component]
fn Display() -> Element {
// Access global state directly
rsx! { "Count: {COUNT}" }
}
#[component]
fn Controls() -> Element {
rsx! {
button {
onclick: move |_| *COUNT.write() += 1,
"Increment"
}
button {
onclick: move |_| *COUNT.write() -= 1,
"Decrement"
}
}
}
Creating Global Signals
Basic Global Signal
static COUNTER: GlobalSignal<i32> = Signal::global(|| 0);
The function signature:
impl<T: 'static> Signal<T> {
pub const fn global(constructor: fn() -> T) -> GlobalSignal<T>
}
- Must be
static and const initialization
- Constructor is a function pointer (not a closure)
- Initialized lazily on first access
Global Memos
static COUNT: GlobalSignal<i32> = Signal::global(|| 0);
static DOUBLED: GlobalMemo<i32> = Signal::global_memo(|| COUNT() * 2);
fn App() -> Element {
rsx! {
button {
onclick: move |_| *COUNT.write() += 1,
"Count: {COUNT}, Doubled: {DOUBLED}"
}
}
}
Function signature:
impl<T: PartialEq + 'static> Signal<T> {
pub const fn global_memo(constructor: fn() -> T) -> GlobalMemo<T>
}
Accessing Global State
Direct Access
static THEME: GlobalSignal<String> = Signal::global(|| "light".to_string());
// Read
let current_theme = THEME();
let theme_ref = THEME.read();
// Write
*THEME.write() = "dark".to_string();
THEME.write().push_str("-mode");
Getting the Underlying Signal
Some operations require a mutable Signal:
static COUNT: GlobalSignal<i32> = Signal::global(|| 0);
#[component]
fn Counter() -> Element {
// Get the actual signal for operations like .set()
let mut count = use_hook(|| COUNT.resolve());
rsx! {
button {
onclick: move |_| count.set(0),
"Reset"
}
}
}
Or use signal() method:
let mut count_signal = COUNT.signal();
count_signal.set(42);
Type Aliases
// Global signal type
pub type GlobalSignal<T> = Global<Signal<T>, T>;
// Global memo type
pub type GlobalMemo<T> = GlobalMemo<T>;
Complete Example
use dioxus::prelude::*;
#[derive(Clone, Copy, PartialEq)]
enum Theme {
Light,
Dark,
}
static THEME: GlobalSignal<Theme> = Signal::global(|| Theme::Light);
static COUNTER: GlobalSignal<i32> = Signal::global(|| 0);
fn main() {
dioxus::launch(App);
}
fn App() -> Element {
rsx! {
header {
ThemeToggle {}
Counter {}
}
main {
ThemedContent {}
}
}
}
#[component]
fn ThemeToggle() -> Element {
let current_theme = THEME();
rsx! {
button {
onclick: move |_| {
*THEME.write() = match current_theme {
Theme::Light => Theme::Dark,
Theme::Dark => Theme::Light,
};
},
match current_theme {
Theme::Light => "🌙 Dark Mode",
Theme::Dark => "☀️ Light Mode",
}
}
}
}
#[component]
fn Counter() -> Element {
rsx! {
div {
button { onclick: move |_| *COUNTER.write() -= 1, "-" }
span { " {COUNTER} " }
button { onclick: move |_| *COUNTER.write() += 1, "+" }
}
}
}
#[component]
fn ThemedContent() -> Element {
let theme = THEME();
rsx! {
div {
class: match theme {
Theme::Light => "light",
Theme::Dark => "dark",
},
"Counter value: {COUNTER}"
"Current theme: {theme:?}"
}
}
}
Common Patterns
Application Settings
#[derive(Clone)]
struct Settings {
language: String,
volume: f32,
notifications: bool,
}
static SETTINGS: GlobalSignal<Settings> = Signal::global(|| Settings {
language: "en".to_string(),
volume: 0.8,
notifications: true,
});
fn update_volume(new_volume: f32) {
SETTINGS.write().volume = new_volume;
}
User Authentication
#[derive(Clone)]
struct User {
id: u32,
name: String,
email: String,
}
static CURRENT_USER: GlobalSignal<Option<User>> = Signal::global(|| None);
fn login(user: User) {
*CURRENT_USER.write() = Some(user);
}
fn logout() {
*CURRENT_USER.write() = None;
}
fn is_logged_in() -> bool {
CURRENT_USER.read().is_some()
}
Feature Flags
static ENABLE_BETA_FEATURES: GlobalSignal<bool> = Signal::global(|| false);
static ENABLE_ANALYTICS: GlobalSignal<bool> = Signal::global(|| true);
#[component]
fn BetaFeature() -> Element {
if !ENABLE_BETA_FEATURES() {
return None;
}
rsx! { "Beta feature content" }
}
Derived Global State
static ITEMS: GlobalSignal<Vec<Item>> = Signal::global(|| vec![]);
static ITEM_COUNT: GlobalMemo<usize> = Signal::global_memo(|| ITEMS.read().len());
static HAS_ITEMS: GlobalMemo<bool> = Signal::global_memo(|| !ITEMS.read().is_empty());
fn App() -> Element {
rsx! {
"Total items: {ITEM_COUNT}"
if HAS_ITEMS() {
ItemList {}
}
}
}
Initialization from External Sources
Since global signals require function pointers, you can’t capture environment variables directly:
// ❌ This won't work - can't capture in const context
// static CONFIG: GlobalSignal<Config> = Signal::global(|| {
// Config::from_env() // Can't call at compile time
// });
// ✅ Instead, initialize lazily with default and update
static CONFIG: GlobalSignal<Option<Config>> = Signal::global(|| None);
fn App() -> Element {
use_future(|| async move {
// Load config on app start
let config = Config::from_env().await;
*CONFIG.write() = Some(config);
});
rsx! { /* ... */ }
}
Global State vs Context
Use Global State When:
- State is truly application-wide (settings, auth, theme)
- Every component might need access
- State lifetime matches app lifetime
- Single instance is needed
Use Context When:
- State is scoped to a subtree
- Multiple instances might exist (different contexts in different subtrees)
- State might change based on routing
- Library components that need configuration
- Global state is as fast as regular signals
- Lazy initialization - only created on first access
- Components only rerender when they read values that change
- Zero overhead if never accessed
Limitations
- Constructor must be a function pointer (no closures)
- Can’t capture environment at initialization
- Lives for entire application lifetime (can’t be dropped)
- Not recommended for libraries (makes testing harder)
Library Considerations
Global state makes it difficult to have multiple instances:
// ❌ Library code using global state
static CHART_CONFIG: GlobalSignal<Config> = Signal::global(|| Config::default());
// Users can only have ONE chart config globally!
For libraries, prefer context or props:
// ✅ Better for libraries
#[component]
fn Chart(config: Signal<Config>) -> Element {
// Can have multiple charts with different configs
rsx! { /* ... */ }
}
Testing
Global state persists across tests, which can cause issues:
#[test]
fn test_counter() {
// Global state from previous test might affect this one
assert_eq!(COUNTER(), 0); // Might fail!
}
Solutions:
- Reset state in each test
- Use context instead for testable code
- Run tests with
--test-threads=1