Atlas Forms
$json Source
source: "$json" binds a control to the primary JSON data object passed to the form — the initialValues prop. This is the most common binding pattern: you fetch an entity from an API and pass it as initialValues; each field binds to the corresponding path within that entity.
How $json Works
// 1. Fetch entity data from API
const applicant = await api.get('/applicants/42');
// {
// firstName: 'Jane',
// lastName: 'Smith',
// address: {
// city: 'London',
// country: 'GB'
// }
// }
// 2. Pass to the form as initialValues
<FormRenderer
schema={schema}
initialValues={applicant} // ← becomes the $json source
mode="edit"
onSubmit={handleSubmit}
/>
// 3. Controls bind to paths within initialValues
// { "id": "first-name", "binding": { "source": "$json", "path": "firstName" } }
// → initial value: 'Jane'
// { "id": "city", "binding": { "source": "$json", "path": "address.city" } }
// → initial value: 'London'
Writing Back
// When the user changes 'city' from 'London' to 'Manchester':
// engine.setValue('city', 'Manchester')
// engine.getValues() now returns:
// {
// 'first-name': 'Jane',
// 'city': 'Manchester',
// ...
// }
// Your submit handler persists this:
const handleSubmit = async (values: Record<string, any>) => {
await api.put('/applicants/42', {
firstName: values['first-name'],
'address.city': values['city'],
// ...
});
};
Nested Object Example
// Form schema — binding to nested paths
{
"controls": [
{
"id": "street",
"type": "text",
"label": "Street",
"binding": { "source": "$json", "path": "address.street" }
},
{
"id": "postcode",
"type": "text",
"label": "Post Code",
"binding": { "source": "$json", "path": "address.postcode" }
},
{
"id": "country",
"type": "select",
"label": "Country",
"binding": { "source": "$json", "path": "address.country" }
}
]
}
$json vs No Binding
| Property | binding: $json | No binding |
|---|---|---|
| Initial value source | Read from initialValues at path | Empty / control's own defaultValue |
| Appears in getValues() | Yes, at binding path key | Yes, at control id key |
| Written to on change | Yes (binding path in value map) | Yes (control id in value map) |
| Key in values object | The control's id (not the path) | The control's id |
Values Are Always Keyed by Control ID
Regardless of the binding path,
engine.getValues() always returns values keyed by the control's id. The binding path tells the engine where to read the initial value from — not what key to use in the returned values map.