For AI agents: the complete documentation index is available at /ooui-react/en/llms.txt, the full documentation bundle is available at /ooui-react/en/llms-full.txt, and this page is available as Markdown at /ooui-react/en/guide/configuration.md.
  • English
  • Global configuration

    OOUIProvider carries the cross-component global settings: copy, where popups mount, mobile mode, access-key display and so on. Just wrap a layer around your app; without it, components work with their defaults. For wiring up component copy and site messages, see Messages and i18n.

    Usage

    import { OOUIProvider } from "ooui-react";
    import zhHans from "ooui-react/locales/zh-hans";
    
    root.render(
      <OOUIProvider messages={zhHans}>{app}</OOUIProvider>,
    );

    It also works without a wrapper: English copy, popups mounted to document.body, non-mobile mode, text direction resolved from the anchor element, and viewport spacing of 0.

    Options

    OptionDescriptionTypeDefault
    messagesMessage override map; see Messages and i18nPartial<Record<MessageKey, MessageValue>>English
    getPortalContainerThe container popups mount into; takes the popup's anchor element(trigger: HTMLElement) => HTMLElementdocument.body
    isMobileMobile-mode switchbooleanfalse
    dirText direction of popups'ltr' | 'rtl'Resolved from the anchor
    viewportSpacingViewport spacing counted when a popup hugs the edge or clamps its heightnumber or a per-edge object0 on each edge
    getAccessKeyLabelDisplay text for access keys(accessKey: string) => string | undefinedThe raw key

    getPortalContainer decides where popups (dropdown menus, popup panels, Popup) mount. By default they mount to document.body; popups inside a dialog mount to that dialog's container, so they stack and are isolated along with the dialog.

    Once this option is set, it always wins — including popups inside a dialog, in which case those popups are no longer isolated with the dialog and stacking is left to the container. The container should not establish a new positioning context (popups are absolutely positioned against page coordinates).

    Mobile mode

    When isMobile is on, some components switch to a mobile form: TabSelect scrolls the selected item to the center, IndexLayout / BookletLayout stop auto-focusing, and dialogs and dropdowns use the mobile layout. It defaults to false; the host can wire up matchMedia and decide for itself before passing it in.

    Text direction

    By default it resolves from the anchor element's text direction, so setting dir="rtl" on the page or a container is enough; most cases need no configuration here.

    You only need to set dir explicitly when a popup mounts outside the content area and the anchor's direction doesn't represent the popup's (for example, when the popup mounts into a container outside the site's content area).

    Viewport spacing

    viewportSpacing is the spacing on all four sides counted when a popup hugs the edge, clamps its height or flips. When the site has a fixed header or similar floating element, use it to avoid overlap, e.g. viewportSpacing={{ top: 48 }}. It defaults to 0 on each edge.

    Access-key label

    getAccessKeyLabel gives the display text for an access key in title. Unset, it shows as Save [s]; once set, it can show a site-local chord:

    import { Button, OOUIProvider } from "ooui-react";
    
    function App() {
      return (
        <OOUIProvider getAccessKeyLabel={(key) => `Alt+Shift+${key}`}>
          <Button accessKey="s" title="Save">
            Save
          </Button>
        </OOUIProvider>
      );
    }
    
    export default App;

    Messages and i18n

    Some places — message dialogs, the file picker and so on — have copy, in English by default. Pass a language pack via messages to switch, and changes to messages take effect immediately.

    import { useState } from "react";
    import type { MessageKey, MessageValue } from "ooui-react";
    import { Button, OOUIProvider, SelectFileInputWidget } from "ooui-react";
    import ja from "ooui-react/locales/ja";
    import ru from "ooui-react/locales/ru";
    import zhHans from "ooui-react/locales/zh-hans";
    
    type Locale = "zh-hans" | "en" | "ja" | "ru";
    
    const LOCALES: Array<{
      value: Locale;
      label: string;
      messages?: Partial<Record<MessageKey, MessageValue>>;
    }> = [
      { value: "zh-hans", label: "简体中文", messages: zhHans },
      { value: "en", label: "English" },
      { value: "ja", label: "日本語", messages: ja },
      { value: "ru", label: "Русский", messages: ru },
    ];
    
    function App() {
      const [locale, setLocale] = useState<Locale>("en");
      const pack = LOCALES.find((item) => item.value === locale);
    
      return (
        <OOUIProvider messages={pack?.messages}>
          <div style={{ display: "grid", gap: 16, justifyItems: "start" }}>
            <div style={{ display: "flex", gap: 8 }}>
              {LOCALES.map((item) => (
                <Button
                  key={item.value}
                  active={item.value === locale}
                  onClick={() => setLocale(item.value)}
                >
                  {item.label}
                </Button>
              ))}
            </div>
            <SelectFileInputWidget />
          </div>
        </OOUIProvider>
      );
    }
    
    export default App;

    Supported languages

    25 in total, imported by language code (ooui-react/locales/<code>); language packs are loaded on demand and don't affect each other's bundle size. en is the built-in default baseline, always part of the main bundle, and doesn't need importing; any key missing from a pack falls back to its English default, key by key.

    All languages
    • zh-hans Simplified Chinese
    • zh-hant Traditional Chinese
    • yue-hant Cantonese
    • ja Japanese
    • ko Korean
    • ru Russian
    • uk Ukrainian
    • de German
    • fr French
    • es Spanish
    • pt-br Brazilian Portuguese
    • it Italian
    • nl Dutch
    • pl Polish
    • cs Czech
    • sv Swedish
    • tr Turkish
    • vi Vietnamese
    • id Indonesian
    • th Thai
    • hi Hindi
    • bn Bengali
    • ar Arabic
    • he Hebrew
    • fa Persian

    For right-to-left languages such as Arabic, Hebrew and Persian, the page or container text direction is covered in Text direction.

    Overriding individual messages

    messages is merged key by key, so pass only the keys you want to change and the rest keep their original copy:

    <OOUIProvider messages={{ "ooui-dialog-message-accept": "Got it" }}>

    Key names follow OOUI's message names; the full list is in the built-in language pack (see src/locales/en.ts). With nested OOUIProviders the inner one wins, so you can mount a language pack on the outer layer and override just a few messages on the inner one.

    Using it on a MediaWiki site

    MediaWiki ships OOUI's own messages under the same key names as this library, so you don't need a language pack — just wire up the site's mw.msg to follow the site language. Write values as functions so they resolve only when read:

    <OOUIProvider
      messages={{
        "ooui-dialog-message-accept": () => mw.msg("ooui-dialog-message-accept"),
        "ooui-dialog-message-reject": () => mw.msg("ooui-dialog-message-reject"),
      }}
    >

    Only the keys you provide go through site messages; the rest fall back to English, so wire up just the copy actually used in the UI. Imperative dialogs (confirm / alert / prompt) are rendered by OOUIProvider's host and inherit this same mapping — no separate registration needed.

    Imperative dialogs

    confirm / alert / prompt are standalone functions — import them and await from any event handler, with no hook or extra mounting:

    const ok = await confirm("Delete this?", { title: "Delete" });

    As long as the call sits under an OOUIProvider, the dialog inherits its config automatically (copy, isMobile, dir, and your own Providers), with no manual injection; with nested Providers it uses the outermost one.

    Without an OOUIProvider it renders with English defaults; to change copy in that case use the module-level registerMessages:

    import { registerMessages } from "ooui-react";
    
    registerMessages({ "ooui-dialog-message-accept": "OK" });

    For return values, options and runtime semantics, see Imperative dialogs.

    Reading the config

    The effective config can be read outside components too:

    HookReturns
    useOOUIConfigThe full merged config object
    useMessage(key, ...params)The current text of one message (OOUIProvider > registerMessages > English default)
    useIsMobile / useDir / useViewportSpacingThe effective value of the matching option
    useAccessKeyLabel(accessKey)The display text for an access key

    Nesting

    Inner overrides outer, and messages is merged key by key: mount a language pack on the outer layer and override just a few messages on the inner one.

    FAQ

    The imperative dialog's copy didn't change? Make sure the confirm / alert / prompt call sits under an OOUIProvider — the dialog is rendered by that Provider's host and inherits its messages. Only when there's no OOUIProvider at all do you need registerMessages — see Imperative dialogs.

    The whole tree re-renders after switching language? Pass a stable reference for messages (such as a module-level constant imported from ooui-react/locales/*); an inline object literal creates a new object on every render.