MirioProvider
Provide the client and display options once so every Form can omit them.
Wrap your app (or a layout) once with MirioProvider and every Form below it finds the client through context. It also carries site-wide display options: theme, size, classNames, requiredIndicator and optionalIndicator.
Preview
MirioProvider renders nothing visible. See the required indicator preview for its effect.
Usage
// app/providers.tsx (Next.js app router: a client component, because the client holds functions)
'use client';
import { MirioProvider } from '@trymirio/mirio';
import { mirio } from '@/mirio/client';
export function Providers({ children }: { children: React.ReactNode }) {
return <MirioProvider client={mirio}>{children}</MirioProvider>;
}
// any page, even a server component: the schema is plain data
import { Form } from '@trymirio/mirio';
import { waitlist } from '@/mirio/forms/waitlist';
export default function Page() {
return <Form schema={waitlist} />;
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
client |
MirioClient |
inherited from a parent provider | From createClient. Optional when nesting: a provider without client keeps the parent's. |
requiredIndicator |
ReactNode | null |
inherited, else nothing | What Label appends to required fields. |
optionalIndicator |
ReactNode | null |
inherited, else muted "Optional" | What Label appends to optional fields. null for nothing. |
theme |
MirioTheme |
inherited | Colors, radius, font, colorScheme for every form below; merged per key with a parent provider's and with each form's (the form wins). See Theming. |
size |
'sm' | 'md' | 'lg' |
inherited, else 'md' |
Default size for every form below; a form's size wins. |
classNames |
Partial<Record<Slot, string>> |
inherited | Classes per part for every form below. Per slot, the parent's, this provider's and the form's classes are all kept, in that order, then the component's own className. |
children |
ReactNode |
— |
Renders no DOM element and has no className or data-mirio.
Hooks: useMirioClient() returns the nearest client or null; useFormOptions() returns the merged display options; useSlot(slot) returns the merged classes for one slot (for your own components). layout is a Form prop only. Form scopes its own options (merged over the provider's) to its subtree.
Variants and states
Nesting merges: an inner provider can change one option and keep the rest.
<MirioProvider client={mirio} theme={{ primary: 'var(--brand)', radius: 'lg' }} size="sm" classNames={{ label: 'uppercase tracking-wide' }}>
<Form schema={waitlist} /> {/* brand primary, lg radius, sm, uppercase labels */}
<Form schema={demo} size="md" theme={{ colorScheme: 'dark' }} /> {/* same, but md and dark */}
</MirioProvider>
<MirioProvider client={mirio} optionalIndicator={<span> (optional)</span>}>
…most forms…
<MirioProvider requiredIndicator={<span> (required)</span>} optionalIndicator={null}>
<Form schema={application} /> {/* same client, different marks */}
</MirioProvider>
</MirioProvider>
A Form with its own client prop ignores the context client; requiredIndicator on Form overrides the provider's for that form only.
Customization
Renders no DOM. theme becomes inline variables on each Form root; you can also set --mirio-* variables on :root or a wrapper (see Styling).
Accessibility
No effect on its own; see Label for how the required indicator is exposed (aria-hidden mark, aria-required on the control).