Performance and Async Behavior
Boreal UI keeps timers, polling, and transient state local to each mounted instance. Use stable data identities and the documented async contracts to keep applications responsive and predictable.
Isolated timers
Chips, toasts, modals, notifications, and uploads clean up work per mounted instance without affecting sibling components.
Completion-based polling
Notification and Select polling waits for the current request to settle before scheduling the next one, preventing overlap.
Stable table identity
Stable row keys and referentially stable data preserve selection, expansion, and efficient rendering across table updates.
Timed Feedback
Timers belong to the nearest mounted component or provider and are cleared when their owner unmounts.
- Each mounted Chip owns its auto-close and exit timers.
- Repeated Chip close requests are coalesced during the 300 ms exit window.
- ToastProvider replaces an existing toast ID and restarts that toast's expiry.
- Modal coalesces overlay, Escape, and close-button requests during its exit.
- Pending timers and animation-frame work are canceled on unmount.
<Chip
id="saved-chip"
message="Saved"
visible={showSaved}
duration={1500}
onClose={() => setShowSaved(false)}
/>const { addToast } = useToast();
addToast({
id: "profile-save",
message: "Saving profile...",
duration: 0,
});
addToast({
id: "profile-save",
message: "Profile saved",
duration: 2500,
});Polling
Requests never overlap: the next timeout begins only after the current async request settles.
<NotificationCenter
notifications={notifications}
onRemove={removeNotification}
fetchNotifications={loadNotifications}
pollInterval={10_000}
/><Select
aria-label="Project"
value={projectId}
onChange={setProjectId}
options={[]}
asyncOptions={loadProjects}
pollInterval={30_000}
/>Set pollInterval={0} to perform the initial load without later polling. Notification expiry uses the latestonRemove callback without restarting existing deadlines.
DataTable Rendering
Give mutable, paginated, sorted, or virtualized records an application-level identity.
const columns = useMemo(() => createColumns(), []);
const selectedKeys = useMemo(() => [...selection], [selection]);
<DataTable
columns={columns}
data={rows}
rowKey={(row) => row.id}
selectedRowKeys={selectedKeys}
pagination
selectableRows
/>- Provide rowKey whenever order can change or data can be replaced.
- Keep large columns, data, and controlled key arrays referentially stable.
- Use standalone imports such as @boreal-ui/next/DataTable for the clearest bundle boundary.
ThemeProvider Rendering
Server-generated theme attributes avoid a first-paint flash and remove the need for a duplicate initialization script.
Stable custom schemes
Equivalent custom scheme arrays reuse the existing snapshot. Keep large collections stable to avoid repeated serialization work.
No duplicate theme script
Next's provider defaults enableThemeScript to false. Keep it disabled when the server already applies theme attributes from the Boreal cookie.
Next.js Scroll Restoration
Boreal globals leave root scrolling at auto. If an app deliberately enables smooth scrolling on html, adddata-scroll-behavior="smooth" so Next.js can temporarily disable it during route restoration.