Sets once, at the root, what an application shares: the density of its tables, where overlays open, how long toasts stay and where they stand, and the language. It is optional; every component works the same without it.
import { UmrissProvider } from "@umriss-ui/core";
It holds these four settings and nothing else: no theme (see Installation) and no defaults for a component's own props. It writes nothing onto the document.
Keep the objects you pass outside the component or memoised: a new object on every render is a new setting on every render.
Wrap the application once; here toast keeps every toast on screen for eight seconds unless the toast names its own duration.
import { Button, ToastProvider, UmrissProvider, useToast, type ToastConfig } from "@umriss-ui/core";
type ToastFn = ReturnType<typeof useToast>["toast"];
const deploy = (toast: ToastFn) => toast({ title: "Checkout 4.12.0 deployed", description: "Rolled out to all regions.", tone: "success" });
function DeployButton() {
const { toast } = useToast();
return (
<Button variant="primary" onClick={() => deploy(toast)}>
Deploy Checkout
</Button>
);
}
/* Kept outside the component: a new object on every render would be a new
setting on every render. */
const TOAST: ToastConfig = { duration: 8000 };
export default function AtTheRoot() {
return (
<UmrissProvider toast={TOAST}>
<ToastProvider>
<DeployButton />
</ToastProvider>
</UmrissProvider>
);
}
A full-screen element hides everything outside it, overlays included; point portalTarget at the element so menus and tooltips open inside it.
import { useRef } from "react";
import { Button, Card, CardBody, CardHeader, Menu, MenuItem, Text, UmrissProvider } from "@umriss-ui/core";
export default function PortalTarget() {
const panel = useRef<HTMLDivElement>(null);
return (
<div ref={panel} style={{ background: "var(--u-color-bg)" }}>
{/* A function, because the element exists only after the first render. */}
<UmrissProvider portalTarget={() => panel.current}>
<Card>
<CardHeader
eyebrow="Sprint 14"
title="Web team board"
actions={
<>
<Button size="sm" onClick={() => void panel.current?.requestFullscreen?.()}>
Full screen
</Button>
<Menu trigger={<Button size="sm">Move card</Button>}>
<MenuItem onSelect={() => {}}>To review</MenuItem>
<MenuItem onSelect={() => {}}>To done</MenuItem>
<MenuItem onSelect={() => {}}>Back to the backlog</MenuItem>
</Menu>
</>
}
/>
<CardBody>
<Text size="sm" tone="secondary">
Checkout flow redesign · Chloe Durand · 5 points
</Text>
</CardBody>
</Card>
</UmrissProvider>
</div>
);
}
All four settings, as a root sets them: German wording and notation, compact tables, overlays in the application's element, short toasts.
import { useRef, useState } from "react";
import { Button, DatePicker, FormField, Stack, ToastProvider, UmrissProvider, useToast } from "@umriss-ui/core";
import type { ToastConfig, UmrissProviderProps } from "@umriss-ui/core";
import { GERMAN_FORMATS, GERMAN_WORDING } from "@umriss-ui/core/wording/de";
const LANGUAGE: UmrissProviderProps["language"] = { wording: GERMAN_WORDING, formats: GERMAN_FORMATS };
const TOAST: ToastConfig = { duration: 3000 };
type ToastFn = ReturnType<typeof useToast>["toast"];
const rebook = (toast: ToastFn) => toast({ title: "Shipment SH-1042 rebooked" });
function DeliveryDate() {
const { toast } = useToast();
const rebookNow = () => rebook(toast);
const [date, setDate] = useState<Date | null>(new Date(2026, 2, 18));
return (
<Stack gap={3} align="flex-start">
<FormField label="Delivery date">
<DatePicker value={date} onChange={setDate} clearable />
</FormField>
<Button size="sm" disabled={date === null} onClick={rebookNow}>
Rebook
</Button>
</Stack>
);
}
export default function EverythingAtOnce() {
const root = useRef<HTMLDivElement>(null);
return (
<div ref={root}>
<UmrissProvider density="compact" portalTarget={() => root.current} toast={TOAST} language={LANGUAGE}>
<ToastProvider>
<DeliveryDate />
</ToastProvider>
</UmrissProvider>
</div>
);
}
The keys of Button, Menu and DatePicker apply here.
Where a ToastProvider stands inside it, the toasts' two live regions stand where the overlays go: in the portalTarget before the first message, so that it is heard there, and inside the topmost modal dialog while one is open. Toast describes them.
UmrissProviderProps| Name | Type | Default | Description |
|---|---|---|---|
density | Density"comfortable" | "compact" | none - a component keeps its own density | The default for the density of every component that reads it with
useDensityFor - in @umriss-ui/table Table, in
@umriss-ui/calculation Calculation ("comfortable" means "regular").Shown in: Set up an application |
portalTarget | PortalTarget | null | Where overlays portal to, unless an open dialog takes them. A function is
evaluated on every opening - a target that only enters the tree later
cannot be given otherwise; null means "as before".
The callers' own rules take precedence: the popover seam portals into the
nearest <dialog> ancestor, because a portal at the body does not reach
the top layer, and the toasts follow the topmost modal dialog, because a
toast outside it lies behind it and is inert.Shown in: Open overlays inside a full-screen panel, Set up an application |
toast | ToastConfig | — | The setting for toasts: the display duration where the individual toast
names none, where they stand, and how many stand at once. Shown in: At the root, Set up an application, Where they stand (Toast) and 1 more |
language | LanguageOptions | none - the surrounding language holds, English at the root | Formats and wording, entry by entry - see Language. Shown in: Set up an application |
childrenrequired | ReactNode | — | The subtree the settings apply to. Shown in: Open overlays inside a full-screen panel, Set up an application |
ToastConfig| Name | Type | Default | Description |
|---|---|---|---|
duration | number | 5000 | Display duration in ms, where the individual toast names none. Shown in: At the root, Set up an application |
position | ToastPosition| "top-start" | "bottom-end" | Where the toasts stand. There is no position per toast - toasts that
stand in different places are not read as one. Shown in: Where they stand (Toast) |
limit | number | 3 | How many toasts that leave by themselves stand at once; the oldest of
them gives way to a new one, as if it had been closed. A toast that
stays (duration: 0) or is still at work (loading) neither counts nor
gives way.Shown in: How many stand at once (Toast) |
DensityHow densely the library builds. A component with a density of its own
takes it through useDensityFor as the default for its density - in
type Density = "comfortable" | "compact";PortalTargetWhere overlays portal to.
A function is evaluated on every opening - a target that only comes into the
tree later cannot be given otherwise. null, or a result of null, means
"as before".
type PortalTarget = HTMLElement | (() => HTMLElement | null) | null;ToastPositionWhere the toasts stand: an edge of the window and a place along it. On a phone they take the width of the window, and only the edge counts.
type ToastPosition = "top-start" | "top-center" | "top-end" | "bottom-start" | "bottom-center" | "bottom-end";What umriss deliberately does not build, and why: ADR-0032.