diff --git a/.github/workflows/size.yml b/.github/workflows/size.yml
index 7a6498c2..413e99e1 100644
--- a/.github/workflows/size.yml
+++ b/.github/workflows/size.yml
@@ -14,6 +14,10 @@ jobs:
run: bun install --frozen-lockfile
- name: Prepare wouter-preact (copy source files)
run: cd packages/wouter-preact && npm run prepublishOnly
+ - name: Symlink npm to bun
+ run: |
+ sudo ln -sf $(which bun) /usr/local/bin/npm
+ sudo ln -sf $(which bunx) /usr/local/bin/npx
- uses: andresz1/size-limit-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/README.md b/README.md
index 1b587976..afcf59d7 100644
--- a/README.md
+++ b/README.md
@@ -80,6 +80,7 @@ projects that use wouter: **[Ultra](https://ultrajs.dev/)**,
- [Can I initiate navigation from outside a component?](#can-i-initiate-navigation-from-outside-a-component)
- [Can I use _wouter_ in my TypeScript project?](#can-i-use-wouter-in-my-typescript-project)
- [How can add animated route transitions?](#how-can-add-animated-route-transitions)
+ - [How do I add view transitions to my app?](#how-do-i-add-view-transitions-to-my-app)
- [Preact support?](#preact-support)
- [Server-side Rendering support (SSR)?](#server-side-rendering-support-ssr)
- [How do I configure the router to render a specific route in tests?](#how-do-i-configure-the-router-to-render-a-specific-route-in-tests)
@@ -630,6 +631,16 @@ available options:
- `hrefs: (href: boolean) => string` — a function for transforming `href` attribute of an `` element rendered by `Link`. It is used to support hash-based routing. By default, `href` attribute is the same as the `href` or `to` prop of a `Link`. A location hook can also define a `hook.hrefs` property, in this case the `href` will be inferred.
+- **`aroundNav: (navigate, to, options) => void`** — a handler that wraps all navigation calls. Use this to intercept navigation and perform custom logic before and after the navigation occurs. You can modify navigation parameters, add side effects, or prevent navigation entirely. This is particularly useful for implementing [view transitions](#how-do-i-add-view-transitions-to-my-app). By default, it simply calls `navigate(to, options)`.
+
+ ```js
+ const aroundNav = (navigate, to, options) => {
+ // do something before navigation
+ navigate(to, options); // perform navigation
+ // do something after navigation
+ };
+ ```
+
## FAQ and Code Recipes
### I deploy my app to the subfolder. Can I specify a base path?
@@ -837,6 +848,64 @@ export const MyComponent = ({ isVisible }) => {
More complex examples involve using `useRoutes` hook (similar to how React Router does it), but wouter does not ship it out-of-the-box. Please refer to [this issue](https://github.com/molefrog/wouter/issues/414#issuecomment-1954192679) for the workaround.
+### How do I use wouter with View Transitions API?
+
+Wouter works seamlessly with the [View Transitions API](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API), but you'll need to manually activate it. This is because view transitions require synchronous DOM rendering and must be wrapped in `flushSync` from `react-dom`. Following wouter's philosophy of staying lightweight and avoiding unnecessary dependencies, view transitions aren't built-in. However, there's a simple escape hatch to enable them: the `aroundNav` prop.
+
+```jsx
+import { flushSync } from "react-dom";
+import { Router, type AroundNavHandler } from "wouter";
+
+const aroundNav: AroundNavHandler = (navigate, to, options) => {
+ // Check if View Transitions API is supported
+ if (!document.startViewTransition) {
+ navigate(to, options);
+ return;
+ }
+
+ document.startViewTransition(() => {
+ flushSync(() => {
+ navigate(to, options);
+ });
+ });
+};
+
+const App = () => (
+
+ {/* Your routes here */}
+
+);
+```
+
+You can also enable transitions selectively using the `transition` prop, which will be available in the `options` parameter:
+
+```jsx
+// Enable transition for a specific link
+About
+
+// Or programmatically
+const [location, navigate] = useLocation();
+navigate("/about", { transition: true });
+
+// Then check for it in your handler
+const aroundNav: AroundNavHandler = (navigate, to, options) => {
+ if (!document.startViewTransition) {
+ navigate(to, options);
+ return;
+ }
+
+ if (options?.transition) {
+ document.startViewTransition(() => {
+ flushSync(() => {
+ navigate(to, options);
+ });
+ });
+ } else {
+ navigate(to, options);
+ }
+};
+```
+
### Preact support?
Preact exports are available through a separate package named `wouter-preact` (or within the
diff --git a/packages/magazin/client.tsx b/packages/magazin/client.tsx
index 7984e137..8182c814 100644
--- a/packages/magazin/client.tsx
+++ b/packages/magazin/client.tsx
@@ -1,12 +1,33 @@
import { hydrateRoot } from "react-dom/client";
-import { Router } from "wouter";
+import { flushSync } from "react-dom";
+import { Router, type NavigateOptions, type AroundNavHandler } from "wouter";
import { HelmetProvider } from "@dr.pogodin/react-helmet";
import { App } from "./App";
+// Enable view transitions for navigation
+const aroundNav: AroundNavHandler = (navigate, to, options) => {
+ // Feature detection for browsers that don't support View Transitions
+ if (!document.startViewTransition) {
+ navigate(to, options);
+ return;
+ }
+
+ // Only use view transitions if explicitly requested
+ if (options?.transition) {
+ document.startViewTransition(() => {
+ flushSync(() => {
+ navigate(to, options);
+ });
+ });
+ } else {
+ navigate(to, options);
+ }
+};
+
hydrateRoot(
document.body,
-
+
diff --git a/packages/magazin/components/navbar.tsx b/packages/magazin/components/navbar.tsx
index 3a045598..ff03835d 100644
--- a/packages/magazin/components/navbar.tsx
+++ b/packages/magazin/components/navbar.tsx
@@ -14,6 +14,7 @@ function NavLink({
return (
`text-sm font-medium ${
active ? "text-gray-900" : "text-gray-500 hover:text-gray-900"
@@ -31,6 +32,7 @@ export function Navbar() {
@@ -43,6 +45,7 @@ export function Navbar() {
diff --git a/packages/magazin/components/with-status-code.tsx b/packages/magazin/components/with-status-code.tsx
index 51c5048d..31435bf4 100644
--- a/packages/magazin/components/with-status-code.tsx
+++ b/packages/magazin/components/with-status-code.tsx
@@ -10,9 +10,8 @@ export function WithStatusCode({
const router = useRouter();
// Set status code on SSR context if available
- // Cast to any because statusCode is not yet in the official types
if (router.ssrContext) {
- (router.ssrContext as any).statusCode = code;
+ router.ssrContext.statusCode = code;
}
return <>{children}>;
diff --git a/packages/magazin/routes/home.tsx b/packages/magazin/routes/home.tsx
index 9a9f286a..da38abd4 100644
--- a/packages/magazin/routes/home.tsx
+++ b/packages/magazin/routes/home.tsx
@@ -6,15 +6,16 @@ function ProductCard({ slug, brand, category, name, price, image }: Product) {
return (