DioxusLabs/dioxus · error
access to `window`
Error message
access to `window`
What it means
`WebHistory::new(prefix, do_scroll_restoration)` - the browser History implementation behind the Dioxus router - immediately runs `window().expect("access to `window`")` in `new_inner`. Without a Window global (web worker, server/SSR process, headless test runtime), construction panics before routing ever starts.
Source
Thrown at packages/web/src/history.rs:48
/// Create a new [`WebHistory`].
///
/// If `do_scroll_restoration` is [`true`], [`WebHistory`] will take control of the history
/// state. It'll also set the browsers scroll restoration to `manual`.
pub fn new(prefix: Option<String>, do_scroll_restoration: bool) -> Self {
let myself = Self::new_inner(prefix, do_scroll_restoration);
let current_route = dioxus_history::History::current_route(&myself);
let current_route_str = current_route.to_string();
let prefix_str = myself.prefix.as_deref().unwrap_or("");
let current_url = format!("{prefix_str}{current_route_str}");
let state = myself.create_state();
let _ = replace_state_with_url(&myself.history, &state, Some(¤t_url));
myself
}
fn new_inner(prefix: Option<String>, do_scroll_restoration: bool) -> Self {
let window = window().expect("access to `window`");
let history = window.history().expect("`window` has access to `history`");
if do_scroll_restoration {
history
.set_scroll_restoration(ScrollRestoration::Manual)
.expect("`history` can set scroll restoration");
}
let prefix = prefix
// If there isn't a base path, try to grab one from the CLI
.or_else(dioxus_cli_config::web_base_path)
// Normalize the prefix to start and end with no slashes
.as_ref()
.map(|prefix| prefix.trim_matches('/'))
// If the prefix is empty, don't add it
.filter(|prefix| !prefix.is_empty())
// Otherwise, start with a slash
.map(|prefix| format!("/{prefix}"));View on GitHub (pinned to 393d190a80)
Solutions
- Construct WebHistory only in browser builds (`#[cfg(target_arch = "wasm32")]`)
- On the server or in tests, provide a MemoryHistory (`Rc<dyn History>`) instead - this is what dioxus fullstack/desktop renderers do
- Check `web_sys::window().is_some()` before choosing the history implementation
Example fix
// before: constructed unconditionally, panics on server/worker
let history = Rc::new(WebHistory::new(None, true)) as Rc<dyn History>;
// after: pick per environment
let history = if web_sys::window().is_some() {
Rc::new(WebHistory::new(None, true)) as Rc<dyn History>
} else {
Rc::new(MemoryHistory::default()) as Rc<dyn History>
}; Defensive patterns
Strategy: type-guard
Validate before calling
// choose the history implementation before constructing
if web_sys::window().is_none() {
// not a browser main thread: do not construct WebHistory here
let history: Rc<dyn History> = Rc::new(MemoryHistory::default());
// ... provide it to the router and return
} Type guard
fn can_use_web_history() -> bool {
web_sys::window().is_some()
} Prevention
- Construct WebHistory only inside #[cfg(target_arch = "wasm32")] browser builds
- Provide MemoryHistory for SSR/tests - the same pattern dioxus fullstack and desktop renderers use
- Check web_sys::window().is_some() before selecting a history implementation in shared code
When it happens
Trigger: Constructing WebHistory outside the browser main thread; a fullstack server build that instantiates web history because the router integration was not cfg-gated; wasm-bindgen-test creating a router outside a page.
Common situations: Shared routing setup code compiled into both server and client targets; running the app inside a worker; integration tests exercising navigation logic headlessly.
Related errors
- should have access to the Window
- `history` can set scroll restoration
- should be run in a context with a `Window` object (dioxus ca
- only an InputElement or TextAreaElement or an element with c
- should have access to the Document
AI-assisted analysis of DioxusLabs/dioxus@393d190a80 (2026-08-16).
Data as JSON: /api/errors/51d3b320885f3eee.
Report an issue: GitHub.