Files
skills/docs/ui/react/react-ui/src/provider/react-ui-provider/react-ui-provider.md
2026-05-29 08:20:56 +01:00

3.4 KiB

<ReactUiProvider> — opinionated chassis composition

(no submodules)

Purpose

The opinionated 95%-case wrapper for the chassis. Composes the three primitive providers in the canonical order <TranslationProvider> → <ThemeProvider> → <PackRegistryProvider> and adds an effect-driven loader that auto-registers the chassis bundled translations into the active i18n catalog (with optional consumer overrides applied on top). Emits the react_ui.provider.mount OTel span on every mount.

Features

  • 15-line provider composition in the right order
  • Auto-registration of the bundled chassis locale on mount
  • Consumer overrides via reactUiOverrides win over the default bundle keys
  • Re-loads the bundle on languageChanged so a runtime locale switch picks up the matching translations automatically
  • Records react_ui.locale.bundle.load_total{locale, outcome} and react_ui.locale.bundle.load_duration_ms{locale} per load
  • Records the react_ui.provider.mount span carrying theme, pack.count, and locale attributes
  • Defers to <TranslationProvider> for the i18n instance (the Provider is mode caller-managed because we always pass i18n)

Usage: Basic example

import { ReactUiProvider } from "@suaveplan/react-ui/provider";
import { lightTheme } from "@suaveplan/react-ui/theme";
import { createI18n } from "@suaveplan/i18n/plugin";

const i18n = createI18n({ defaultLocale: "en-GB" });

<ReactUiProvider i18n={i18n} themes={[lightTheme]} defaultTheme="light">
    <App />
</ReactUiProvider>;

Usage: Advanced example

import { ReactUiProvider } from "@suaveplan/react-ui/provider";
import {
    darkTheme,
    highContrastTheme,
    lightTheme,
} from "@suaveplan/react-ui/theme";
import { authPack } from "@suaveplan/react-ui-pack-auth";

<ReactUiProvider
    i18n={i18n}
    themes={[lightTheme, darkTheme, highContrastTheme]}
    defaultTheme="light"
    packs={[authPack]}
    components={{ auth: { LoginForm: AcmeLoginForm } }}
    reactUiOverrides={{
        "en-GB": {
            "react-ui.themeToggle.label": "Appearance",
        },
    }}
    scriptNonce={cspNonce}
>
    <App />
</ReactUiProvider>;

API Reference

  • <ReactUiProvider> — props per ReactUiProviderProps:
    • i18n: I18nInstance — required
    • themes: ReadonlyArray<ThemePlugin> — required
    • defaultTheme: string — required
    • packs?: ReadonlyArray<ComponentPack>
    • components?: RuntimeComponentOverride
    • reactUiOverrides?: ReactUiLocaleOverrides
    • logger?: PackLoggerLike
    • scriptNonce?: string
    • storageKey?: string
    • attribute?: string
    • children: ReactNode
  • type ReactUiLocaleOverrides = Partial<Record<SupportedLocale, Partial<Record<TranslationKey, string>>>>

Implementation notes

The bundle loader runs inside a useEffect and tracks an aborted-flag ref so a fast unmount does not leak a stale registration into the catalog. The languageChanged listener is registered on the i18n event bus rather than on the React reactive state because the i18n instance owns the locale truth — re-deriving from React state would introduce a one-render lag on every locale switch. The span.end() fires synchronously (before the lazy bundle resolves) so the trace boundary matches the React commit boundary; the bundle-load duration is captured separately via the histogram.