diff --git a/Cargo.lock b/Cargo.lock index 7d69f23144..9e0acaf94f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4002,6 +4002,7 @@ dependencies = [ "dioxus-cli-config", "dioxus-core", "dioxus-core-macro", + "dioxus-document", "dioxus-fullstack-core", "dioxus-history", "dioxus-hooks", diff --git a/packages/router/Cargo.toml b/packages/router/Cargo.toml index 416230a942..7c802d160a 100644 --- a/packages/router/Cargo.toml +++ b/packages/router/Cargo.toml @@ -15,6 +15,7 @@ dioxus-core-macro = { workspace = true } dioxus-signals = { workspace = true } dioxus-hooks = { workspace = true } dioxus-html = { workspace = true, optional = true } +dioxus-document = { workspace = true, optional = true } dioxus-history = { workspace = true } dioxus-router-macro = { workspace = true } dioxus-fullstack-core = { workspace = true, optional = true } @@ -28,7 +29,7 @@ rustversion = { workspace = true } default = ["html"] streaming = ["dep:dioxus-fullstack-core"] wasm-split = [] -html = ["dep:dioxus-html"] +html = ["dep:dioxus-html", "dep:dioxus-document"] [dev-dependencies] axum = { workspace = true, features = ["ws"] } diff --git a/packages/router/src/components/link.rs b/packages/router/src/components/link.rs index 310a6168df..89971343ec 100644 --- a/packages/router/src/components/link.rs +++ b/packages/router/src/components/link.rs @@ -56,6 +56,15 @@ pub struct LinkProps { #[props(into)] pub to: NavigationTarget, + /// Whether to wrap the navigation in the browser's View Transition API + /// (`document.startViewTransition`). + /// + /// When enabled, the browser animates the transition between the old and new + /// page states. This is a no-op on browsers and webviews that do not support + /// the View Transition API. + #[props(default)] + pub view_transition: bool, + #[props(extends = GlobalAttributes)] attributes: Vec, } @@ -70,6 +79,7 @@ impl Debug for LinkProps { .field("onclick", &self.onclick.as_ref().map(|_| "onclick is set")) .field("onclick_only", &self.onclick_only) .field("rel", &self.rel) + .field("view_transition", &self.view_transition) .finish() } } @@ -145,6 +155,7 @@ pub fn Link(props: LinkProps) -> Element { rel, to, class, + view_transition, .. } = props; @@ -224,6 +235,15 @@ pub fn Link(props: LinkProps) -> Element { event.prevent_default(); if do_default && is_router_nav { + if view_transition { + // Opt into the browser's View Transition API. The old page state is + // captured synchronously here, before the route change. The new state + // is captured on the next frame, after Dioxus has applied the DOM + // mutations in the microtask checkpoint following this handler. + dioxus_document::eval( + "if (typeof document !== 'undefined' && document.startViewTransition) { document.startViewTransition(() => {}); }", + ); + } router.push_any(to.clone()); }