Focus Zone
Groups focusable widgets into a logical unit for Tab traversal. Focus zones help organize complex UIs by creating focus "islands" that users navigate between with Tab.
Groups focusable widgets into a logical unit for Tab traversal. Focus zones help organize complex UIs by creating focus "islands" that users navigate between with Tab.
Usage
ui.focusZone(
{ id: "toolbar", tabIndex: 0 },
[
ui.row({ gap: 1 }, [
ui.button({ id: "new", label: "New" }),
ui.button({ id: "open", label: "Open" }),
ui.button({ id: "save", label: "Save" }),
]),
]
)Layout behavior
focusZone is layout-transparent when it wraps a single child: it does not force
that child into a column axis. The child keeps its natural layout behavior
(for example, a row stays horizontal, grid stays grid, nested stacks
preserve their own axis rules).
When you pass multiple direct children, Rezi currently uses an implicit column fallback. Keep explicit layout wrappers around multi-child zones so structure stays obvious and stable.
When you want explicit structure, keep using standard layout widgets:
ui.focusZone(
{ id: "toolbar" },
[
ui.row({ gap: 1 }, [
ui.button({ id: "a", label: "A" }),
ui.button({ id: "b", label: "B" }),
]),
]
)Props
| Prop | Type | Default | Description |
|---|---|---|---|
id | string | required | Unique identifier for the zone |
tabIndex | number | 0 | Order in which zones receive focus (lower = earlier) |
navigation | "linear" | "grid" | "none" | "linear" | How focus moves within the zone |
columns | number | 1 | Number of columns for grid navigation |
wrapAround | boolean | true | Wrap from last to first item |
onEnter | () => void | - | Callback when zone receives focus |
onExit | () => void | - | Callback when focus leaves zone |
key | string | - | Reconciliation key |
Navigation Modes
Linear (Default)
Arrow keys move through items in document order:
ui.focusZone({ id: "list", navigation: "linear" }, [
ui.column({ gap: 1 }, [
ui.button({ id: "a", label: "A" }),
ui.button({ id: "b", label: "B" }),
ui.button({ id: "c", label: "C" }),
]),
])
// Up/Down or Left/Right moves A -> B -> CGrid
Arrow keys navigate a 2D grid layout:
ui.focusZone({ id: "grid", navigation: "grid", columns: 3 }, [
ui.grid({ columns: 3 }, [
ui.button({ id: "1", label: "1" }),
ui.button({ id: "2", label: "2" }),
ui.button({ id: "3", label: "3" }),
ui.button({ id: "4", label: "4" }),
ui.button({ id: "5", label: "5" }),
ui.button({ id: "6", label: "6" }),
]),
])
// Left/Right moves horizontally
// Up/Down moves between rowsNone
Disables internal arrow key navigation (Tab still works):
ui.focusZone({ id: "custom", navigation: "none" }, [...])Zone Ordering
Use tabIndex to control the order zones receive focus:
ui.column({}, [
// User presses Tab: sidebar -> main -> footer
ui.focusZone({ id: "sidebar", tabIndex: 0 }, [...]),
ui.focusZone({ id: "main", tabIndex: 1 }, [...]),
ui.focusZone({ id: "footer", tabIndex: 2 }, [...]),
])Enter/Exit Callbacks
Track when focus enters or leaves a zone:
ui.focusZone({
id: "search",
onEnter: () => showSearchHint(),
onExit: () => hideSearchHint(),
}, [
ui.row({ gap: 1 }, [
ui.input({ id: "query", value: state.query }),
ui.button({ id: "search", label: "Search" }),
]),
])Form Example
Organize a form into logical sections:
ui.column({ gap: 2 }, [
ui.focusZone({ id: "credentials", tabIndex: 0 }, [
ui.column({ gap: 1 }, [
ui.field({ label: "Username", children:
ui.input({ id: "user", value: state.user })
}),
ui.field({ label: "Password", children:
ui.input({ id: "pass", value: state.pass })
}),
]),
]),
ui.focusZone({ id: "actions", tabIndex: 1 }, [
ui.row({ gap: 1 }, [
ui.button({ id: "login", label: "Login" }),
ui.button({ id: "cancel", label: "Cancel" }),
]),
]),
])Mouse Behavior
Clicking any focusable widget inside a focus zone moves focus to that widget, just like Tab navigation. The zone's onEnter callback fires when focus enters the zone via mouse click.
Related
- Focus Trap - Constrain focus within a region
- Modal - Modal dialog with focus trap
- Button - Focusable button
- Mouse Support - Mouse interaction details