leptos-rs/leptos · error
queueMicrotask not available
Error message
queueMicrotask not available
What it means
DomRenderer::queue_microtask reads window.queueMicrotask via reflection and panics if the property is absent. The library expects a standard browser environment; throwing here flags a runtime where microtask scheduling is unavailable.
Source
Thrown at tachys/src/renderer/dom.rs:53
pub type CssStyleDeclaration = web_sys::CssStyleDeclaration;
pub type TemplateElement = web_sys::HtmlTemplateElement;
/// A microtask is a short function which will run after the current task has
/// completed its work and when there is no other code waiting to be run before
/// control of the execution context is returned to the browser's event loop.
///
/// Microtasks are especially useful for libraries and frameworks that need
/// to perform final cleanup or other just-before-rendering tasks.
///
/// [MDN queueMicrotask](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask)
pub fn queue_microtask(task: impl FnOnce() + 'static) {
use js_sys::{Function, Reflect};
let task = Closure::once_into_js(task);
let window = window();
let queue_microtask =
Reflect::get(&window, &JsValue::from_str("queueMicrotask"))
.expect("queueMicrotask not available");
let queue_microtask = queue_microtask.unchecked_into::<Function>();
_ = queue_microtask.call1(&JsValue::UNDEFINED, &task);
}
fn queue(fun: Box<dyn FnOnce()>) {
use std::cell::{Cell, RefCell};
thread_local! {
static PENDING: Cell<bool> = const { Cell::new(false) };
static QUEUE: RefCell<Vec<Box<dyn FnOnce()>>> = RefCell::new(Vec::new());
}
QUEUE.with_borrow_mut(|q| q.push(fun));
if !PENDING.replace(true) {
queue_microtask(|| {
let tasks = QUEUE.take();
for task in tasks {
task();View on GitHub (pinned to 32d20f6c9d)
Solutions
- Run in a real browser or polyfill queueMicrotask (e.g. via jsdom in tests or a core-js/microtask shim)
- For SSR, ensure the DOM renderer is not invoked server-side (use the appropriate renderer for the platform)
- Use Promise.resolve().then-based polyfill assigned onto globalThis/window before app boot
- Upgrade the webview/runtime to one supporting queueMicrotask
Example fix
// before // Node test without polyfill -> panics // after // polyfill.mjs loaded first globalThis.queueMicrotask ??= (fn) => Promise.resolve().then(fn);
Defensive patterns
Strategy: fallback
Validate before calling
let has_microtask = js_sys::Reflect::get(&web_sys::window().unwrap(), &wasm_bindgen::JsValue::from_str("queueMicrotask"))
.map(|v| !v.is_undefined()).unwrap_or(false); Try / catch
// install a polyfill fallback before app boot:
if !has_microtask() {
js_sys::eval("globalThis.queueMicrotask = (f) => Promise.resolve().then(f);").unwrap();
} Prevention
- Load polyfills (queueMicrotask, etc.) before mounting the app
- For Node tests, use a jsdom-based harness or install the polyfill in test setup
- Avoid running the DOM renderer outside browsers (use SSR renderer server-side)
- Pin/upgrade webviews to versions supporting queueMicrotask
When it happens
Trigger: Running the WASM DOM renderer in a non-browser JS environment (Node without jsdom/polyfills, minimal JS runtimes, service workers without window, outdated embedded webviews) where window.queueMicrotask is undefined.
Common situations: SSR/hydration tests running leptos DOM code under Node; custom wasm test harnesses; old WebKit-based webviews lacking queueMicrotask; bundler configs shimming window incompletely.
Related errors
- Executor::spawn called, but no global 'spawn' function is co
- Executor::spawn_local called, but no global 'spawn_local' fu
- internal error: entered unreachable code
- there to be a <body> element
- At {caller}, tried to spawn a Future with Executor::spawn()
AI-assisted analysis of leptos-rs/leptos@32d20f6c9d (2026-09-01).
Data as JSON: /api/errors/415b455550806210.
Report an issue: GitHub.