NatUI
Guides

App shell and data

Coordinate native window chrome, navigation, selection, and sorting from React state.

Root-attached chrome

Place MenuBar and Toolbar directly under the React root. Hosts hoist them into native window chrome instead of inserting them in normal layout.

function App() {
  return (
    <>
      <MenuBar menus={menus} onSelect={handleMenu} />
      <Toolbar items={toolbarItems} onAction={handleToolbar} />
      <SplitView>{content}</SplitView>
    </>
  );
}

macOS accepts the first root-level menu bar and toolbar. Windows hoists chrome components wherever they are created, but relying on that difference makes the tree non-portable.

Stable selection identity

Use tag for list rows and id for table rows:

<List value={projectId} onChange={(value) => setProjectId(value as string | null)}>
  {projects.map((project) => (
    <Label key={project.id} tag={project.id} systemImage="folder">
      {project.name}
    </Label>
  ))}
</List>

Selection can be a string, a string array, or null. Providing the value prop, even with null, enables selection.

Sort in the application

The native table emits a requested SortDescriptor. Keep data operations in JavaScript:

const visibleRows = useMemo(
  () => sortRows(rows, sort),
  [rows, sort],
);

<Table
  columns={columns}
  rows={visibleRows}
  sort={sort}
  onSortChange={setSort}
/>

Filtering follows the same model. The host receives final row data and does not own a separate sorted or filtered copy.

Use serializable specs

Menu, toolbar, alert, and table structures are data props rather than React child trees. Keep them JSON-compatible and update the data when checkmarks, toggle states, disabled states, or rows change.

On this page