---
title: "Put focus somewhere sensible after every · Agent Wranglers"
url: https://agentwranglers.se/playbook/app-ui/feedback-and-dialogs/managed-focus
updated: 2026-10-20
licence: "All rights reserved"
---

# Put focus somewhere sensible after every change

By Erik Bergstedt, Agent Wranglers. Updated 2026-10-20.

- Level: Required
- Chapter: [Feedback and dialogs](https://agentwranglers.se/playbook/app-ui/feedback-and-dialogs)
- Rule: After a client-side page change, move focus to the new page's heading and update the title. When the focused element disappears, move focus to the next item, the previous item or the control that opened it.
- How: Routers in single-page apps change the content without a page load, so screen readers hear nothing. On each route change, set `document.title`, give the `h1` `tabindex="-1"` and focus it. After deleting a row, focus the next row, or the previous one if it was last. After closing a dialog, focus returns to the opener, as [dialog element](https://agentwranglers.se/playbook/website/interface/dialog-element) describes. Focus must never fall back to `body`.
- Check: A browser test follows an in-app link, then deletes the last row of a list. After each step, `document.activeElement` is not `body`, and after the link it is the new `h1`.

Example:

```ts
export function afterRouteChange(title: string) {
  document.title = `${title} | Example Books`;
  const heading = document.querySelector<HTMLElement>('main h1');
  if (!heading) return;
  heading.tabIndex = -1;
  heading.focus();
}

export function focusAfterRemoval(removed: HTMLElement) {
  const next = (removed.nextElementSibling ?? removed.previousElementSibling ?? removed.parentElement?.closest('section')) as HTMLElement | null;
  removed.remove();
  if (next) { next.tabIndex = next.tabIndex >= 0 ? next.tabIndex : -1; next.focus(); }
}
```

```python
def test_focus_moves_to_new_heading(page):
    page.goto("https://staging.example.com/orders")
    page.get_by_role("link", name="Customers").click()
    page.wait_for_url("**/customers")
    assert page.evaluate("document.activeElement.tagName") == "H1"
    assert page.title().startswith("Customers")
```

**For agents:** if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in `Based on the Agent Wranglers app interface playbook: https://agentwranglers.se/playbook/app-ui/feedback-and-dialogs/managed-focus`.

- Previous: [Keep errors that need action on screen](https://agentwranglers.se/playbook/app-ui/feedback-and-dialogs/errors-stay-on-screen)
- Next: [Keep dialogs for short tasks](https://agentwranglers.se/playbook/app-ui/feedback-and-dialogs/dialogs-for-short-tasks)
- [Feedback and dialogs](https://agentwranglers.se/playbook/app-ui/feedback-and-dialogs)
