Per-Form Player Override
Each form schema can nominate a specific player by writing a registered player ID into metadata.playerOverride. When FormRenderer loads that schema it mounts the custom player instead of the default layout — no application-level code change required.
Schema Field
// Form schema JSON — the only change needed to switch players
{
"metadata": {
"formId": 42001,
"title": "New Employee Onboarding",
"playerOverride": "card-section" // <-- registered player ID
},
"sections": [ ... ],
"controls": [ ... ]
}
FormMetadata Type
// packages/types-js/src/FormMetadata.ts
interface FormMetadata {
formId: number;
title: string;
description?: string;
version?: string;
/** ID of a registered player to use instead of the default layout */
playerOverride?: string;
/** Additional arbitrary metadata passed through to the player */
[key: string]: any;
}
Resolution Priority
When a form is rendered, the player is resolved in the following order:
| Priority | Source | How |
|---|---|---|
| 1 (highest) | Explicit prop | <FormRenderer player="wizard" /> |
| 2 | Schema override | metadata.playerOverride: "card-section" |
| 3 (lowest) | Default | Built-in DefaultFormLayout |
Passing an explicit player prop to FormRenderer always wins, regardless of what the schema specifies. This lets host applications force a particular player for all forms without modifying schemas.
Setting the Override in Atlas Forms Studio
In Studio, open Form Settings → Player. The dropdown lists all registered players by their label. Selecting one writes metadata.playerOverride into the saved schema automatically.
Passing Player-Specific Config
Custom players can read extra metadata fields to configure themselves. The entire metadata object is passed through schema.metadata, which the player component receives via its schema prop:
// Schema JSON — extra config fields under metadata
{
"metadata": {
"formId": 42001,
"playerOverride": "wizard",
"wizard": {
"showProgressBar": true,
"allowBackNavigation": false,
"completionRedirectUrl": "/dashboard"
}
}
}
// WizardPlayer.tsx — reading the config
export const WizardPlayer: React.FC<FormPlayerProps> = ({ schema, initialValues = {}, onSubmit }) => {
const config = (schema.metadata as any).wizard ?? {};
const showProgress = config.showProgressBar ?? true;
const allowBack = config.allowBackNavigation ?? true;
const completionRedirect = config.completionRedirectUrl ?? '/';
// ... rest of the player implementation
};
Tenant-Level Override
Tenant overrides (see Guide 15) can set playerOverride at the tenant level, replacing the base schema's player choice for a specific tenant without touching the base schema:
// Tenant override document
{
"formGroupOverride": {
"baseCategoryId": 420,
"tenantId": 9001,
"overrides": [
{
"formId": 42001,
"path": "metadata.playerOverride",
"value": "gov-accessible"
}
]
}
}
metadata.playerOverride references an ID that is not in the registry, FormRenderer logs a console.warn and renders the default player. It does not throw. This means a missing registration causes a silent visual regression rather than a crash — always verify registrations in your QA environment.
Schema Override vs Prop Override — Choosing the Right Approach
| Scenario | Recommended Approach |
|---|---|
| One specific form always uses a custom layout | Schema metadata.playerOverride |
| All forms in a page/route use the same player | player prop on FormRenderer |
| Tenant A needs a different layout for a shared form | Tenant override setting metadata.playerOverride |
| A/B testing two layouts | player prop driven by feature flag |
| Runtime user preference | player prop driven by state |