UmrissProvider

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.

Examples

At the root

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>
  );
}

Open overlays inside a full-screen panel

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>
  );
}

Set up an application

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>
  );
}

When to use something else

Keyboard

The keys of Button, Menu and DatePicker apply here.

Accessibility

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.

API

UmrissProviderProps

NameTypeDefaultDescription
densityDensity
"comfortable" | "compact"
none - a component keeps its own densityThe 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

portalTargetPortalTargetnullWhere 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

toastToastConfig—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

languageLanguageOptionsnone - the surrounding language holds, English at the rootFormats and wording, entry by entry - see Language.

Shown in: Set up an application

childrenrequiredReactNode—The subtree the settings apply to.

Shown in: Open overlays inside a full-screen panel, Set up an application

ToastConfig

NameTypeDefaultDescription
durationnumber5000Display duration in ms, where the individual toast names none.

Shown in: At the root, Set up an application

positionToastPosition
| "top-start"
| "top-center"
| "top-end"
| "bottom-start"
| "bottom-center"
| "bottom-end"
"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)

limitnumber3How 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)

Types on this page

Density

How 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";

PortalTarget

Where 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;

ToastPosition

Where 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";

Known limits

What umriss deliberately does not build, and why: ADR-0032.

Every page of @umriss-ui/core

Getting started

Customising

Layout

Typography

Actions

Forms

Choosing

Dates and times

Feedback

Overlays

Navigation

Data display

API index