---
url: /a11y-hud/cookbook/route-change-rescans.md
---
# Route-change rescans

Framework adapters rescan after every render commit, which covers most route transitions automatically. This recipe shows the explicit patterns for cases where you need finer control.

## Why adapters handle most cases automatically

* **React**: `useEffect` with no deps fires after every commit, including route-change renders.
* **Vue**: the core's MutationObserver picks up DOM changes from navigation; a post-flush `watch` on `scope` rescans when the scope prop changes.
* **Angular**: `afterEveryRender` fires after every render commit, including navigation-driven renders.
* **Svelte 5**: `$effect` fires after every reactive update.
* **Solid**: `createEffect` tracks reactive reads, including router signals.

If you mount `<A11yHud>` in your root layout and your router triggers a full re-render on navigation, the rescan happens for free.

## When you need explicit rescans

* The router replaces only the page content region, not the whole tree.
* You want to rescan after an async data load that follows the navigation.
* You need to rate-limit rescans during heavy route transitions.

## React Router

```tsx
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
import { useA11yHud } from "@a11y-hud/react";

function A11yHudWithRouter() {
  const hud = useA11yHud({ theme: "auto" });
  const location = useLocation();

  useEffect(() => {
    // location.pathname changes on every navigation
    hud.runScan();
  }, [location.pathname]);

  return null;
}
```

## Vue Router

```ts
import { watch } from "vue";
import { useRoute } from "vue-router";
import { useA11yHud } from "@a11y-hud/vue";

const hud = useA11yHud({ theme: "auto" });
const route = useRoute();

watch(
  () => route.fullPath,
  () => hud.runScan(),
  { flush: "post" }  // after DOM update
);
```

## Angular Router

Grab the component instance with `@ViewChild` and call `runScan()` on it. (Do not inject `A11yHudService` separately — the component provides its own instance, and a second, un-initialised instance would resolve `null` from `runScan()`.)

```typescript
import { Component, ViewChild, inject } from "@angular/core";
import { Router, NavigationEnd, RouterOutlet } from "@angular/router";
import { filter } from "rxjs/operators";
import { A11yHudComponent } from "@a11y-hud/angular";

@Component({
  standalone: true,
  imports: [A11yHudComponent, RouterOutlet],
  template: `
    <a11y-hud-angular [theme]="'auto'" />
    <router-outlet />
  `,
})
export class RootComponent {
  @ViewChild(A11yHudComponent) hud?: A11yHudComponent;
  private readonly router = inject(Router);

  constructor() {
    this.router.events
      .pipe(filter((e) => e instanceof NavigationEnd))
      .subscribe(() => void this.hud?.runScan());
  }
}
```

## SvelteKit

```svelte [src/routes/+layout.svelte]
<script lang="ts">
  import { page } from "$app/state";
  import { useA11yHud } from "@a11y-hud/svelte";

  const hud = useA11yHud(() => ({ theme: "auto" }));

  $effect(() => {
    // track the pathname — re-runs on every navigation
    page.url.pathname;
    hud.runScan();
  });
</script>

<slot />
```

## Solid Router

```tsx
import { createEffect } from "solid-js";
import { useLocation } from "@solidjs/router";
import { createA11yHud } from "@a11y-hud/solid";

function A11yHudWithRouter() {
  const hud = createA11yHud({ theme: "auto" });
  const location = useLocation();

  createEffect(() => {
    // location.pathname is a reactive signal
    location.pathname;
    hud.runScan();
  });

  return null;
}
```

## Vanilla (any router)

```js
import { mount } from "a11y-hud";

const hud = mount({ theme: "auto" });

// Listen to the History API
const originalPushState = history.pushState.bind(history);
history.pushState = (...args) => {
  originalPushState(...args);
  hud.runScan();
};

window.addEventListener("popstate", () => hud.runScan());
```
