$ rezi
Widgets

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

PropTypeDefaultDescription
idstringrequiredUnique identifier for the zone
tabIndexnumber0Order in which zones receive focus (lower = earlier)
navigation"linear" | "grid" | "none""linear"How focus moves within the zone
columnsnumber1Number of columns for grid navigation
wrapAroundbooleantrueWrap from last to first item
onEnter() => void-Callback when zone receives focus
onExit() => void-Callback when focus leaves zone
keystring-Reconciliation key

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 -> C

Grid

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 rows

None

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.

On this page