Schema-Driven Forms in React: Building with TrueFoundry FormBuilder

Conçu pour la vitesse : latence d'environ 10 ms, même en cas de charge
Une méthode incroyablement rapide pour créer, suivre et déployer vos modèles !
- Gère plus de 350 RPS sur un seul processeur virtuel, aucun réglage n'est nécessaire
- Prêt pour la production avec un support complet pour les entreprises
If you’ve used TrueFoundry to deploy a service, create a cluster, configure an LLM model, or manage secrets, you’ve interacted with forms that look like normal UI, but are built very differently from typical web forms.
Why TrueFoundry uses schema-driven forms
TrueFoundry forms often represent manifests - YAML/JSON objects you can also apply with the CLI (tfy apply -f ...). The same object might be edited in the UI, downloaded as YAML, or submitted to the API.
A schema-driven approach gives TrueFoundry:
- One source of truth for form structure (loaded from the backend per resource type).
- Consistent behavior across deployments, clusters, policies, models, secrets, settings forms, or any other form in the platform.
- Nested and conditional fields without rewriting form logic on every screen.
- Domain-specific widgets (cluster picker, secret selector, resource limits) plugged into a shared runtime.
The mental model: the schema describes the shape of the data and how to edit it; FormBuilder turns that description into a working form.
1. Form schema:
A form schema is an array of field definitions. Each field is an object with a few important properties:
Here is a simplified generic example - not a real TrueFoundry schema, but close to how the product thinks about configuration:
[
{
"sort": 1,
"jsonKey": "name",
"label": "Service Name",
"uiType": "Input",
"validate": { "required": true, "pattern": "^[a-z0-9-]+$" }
},
{
"sort": 2,
"jsonKey": "image",
"label": "Image",
"uiType": "Group",
"subParameters": [
{
"jsonKey": "type",
"label": "Source",
"uiType": "Radio",
"validate": {
"required": true,
"defaultValue": "build",
"options": [
{ "label": "Build", "value": "build" },
{ "label": "Existing image", "value": "existing" }
]
}
},
{
"jsonKey": "uri",
"label": "Image URI",
"uiType": "Input",
"conditions": [
{ "jsonKey": "image.type", "op": "==", "value": "existing" }
],
"validate": { "required": true }
}
]
}
]
Resulting manifest/config on submit:
{
"name": "my-service",
"image": { "type": "existing", "uri": "registry.io/app:v1" }
}
2. How components are mapped and fields are rendered
Rendering happens in a small pipeline, the flow is:

FormBuilder walks the schema tree and picks a React component per node based on uiType.
Component types:
- Basic: Input, Select, Radio, Switch, Number
- Structural: Group (section), Structs (repeatable list), KV / ENV (key-value)
- Domain Specific: ClusterSelect, SecretSelect, Resources, ModelSelect, PermissionsMatrix, MCP fields, and more
Conditions: If image.type !== "existing", the URI field never mounts. With shouldUnregister: true, it also leaves form state, so hidden values cannot leak into the payload.
Mapping logic in code:
// FormComponentMap
const ComponentType = FormComponents[schema.uiType]
const CustomComp = CustomComponentsMap?.[schema.uiType]
if (!enabledByCondition) return null
return CustomComp
? <CustomComp schema={schema} />
: <ComponentType schema={schema} />
3. How form state is handled
FormBuilder uses react-hook-form as its state engine. That choice matters for how the product behaves.
Initialization/Component Usage - create & edit:
<FormBuilder
schema={schema}
defaultValues={existingManifest} // edit: pre-fill; create: empty/template
onSubmit={(manifest) => createOrUpdate({ manifest })}
/>
Form setup:
const methods = useForm({
mode: 'onChange', // validate as user types
defaultValues,
shouldUnregister: true, // hidden fields drop out of state
})
Field registration - each input binds to its path:
// Inside a text field component
const { register } = useFormContext()
<input
{...register(schema.jsonKey, registerProps)}
defaultValue={defaultValue}
/>
Extra context - specific form pass runtime data separate from field values:
<FormBuilder
schema={schema}
extraContext={{
workspace,
cluster,
serviceAccountOptions, // dynamic dropdown options
dataTestPrefix: 'create-cluster',
}}
/>
Single object, nested paths:
All field values live in one form object. Each field registers under its full jsonKey path:
- name → top-level string
- image.type → nested value
- ports.0.container_port → first item in an array
When you edit a field, you are mutating this shared object. On submit, FormBuilder reads the whole object and passes it to the form’s handler (which usually calls the TrueFoundry API or produces YAML).
Defaults and edit mode:
When opening a form to create something, defaultValues may be empty or come from a template.
When editing, defaultValues is typically the existing manifest. The form fields pre-fill from that object. Some fields are marked immutable in edit mode (e.g. resource name) so they render read-only.
Hidden fields are removed from state:
TrueFoundry forms use shouldUnregister: true. That means:
- If a field is hidden by a condition, it is unregistered from form state
- Its value does not leak into the submitted payload
This is important for conditional forms: you only submit what the user could actually see and edit.
4. How validation works
Validation in TrueFoundry forms happens at two layers.
Layer 1: Schema validation (declarative)
Each field’s validate block can specify:
- required
- min / max (allowed range of integer value)
- minLength / maxLength (string or array length)
- pattern (regex) with custom message
- defaultValue
- immutable (read-only in edit mode)
These rules are converted into react-hook-form validation rules. Errors are shown inline next to the field. Validation runs on change (mode: 'onChange'), so users get feedback as they type, not only on submit.
Layer 2: Custom and async validation
Schemas can also attach a custom validate function, commonly used for:
- Name uniqueness (“A entity with this name already exists”)
- API-backed checks before submit
- Cross-field rules inside a custom component
- Screens attach these at runtime with helpers like “attach validation to field name”. The UI shows a spinner while async validation runs (e.g. debounced name check).
useAttachValidation(schema, {
name: async (name) => {
const taken = await checkNameExists(name)
return taken ? 'Name already exists' : true
},
})Putting it together: what you experience as a user
When you open “Create Cluster”, “Deploy Service”, “Add Model”, or “Manage Secrets”:
- TrueFoundry loads (or builds) a schema for that resource
- FormBuilder renders each schema node as the desired input component
- Your edits update a single nested form object
- Validation runs continuously and again on submit
- The final object is a manifest - the same shape you’d use in YAML or the CLI
That is why TrueFoundry can support very complex configuration UIs without every screen being a one-off form: the complexity lives in the schema and a library of field components, orchestrated by one shared runtime.
TrueFoundry AI Gateway offre une latence d'environ 3 à 4 ms, gère plus de 350 RPS sur 1 processeur virtuel, évolue horizontalement facilement et est prête pour la production, tandis que LiteLM souffre d'une latence élevée, peine à dépasser un RPS modéré, ne dispose pas d'une mise à l'échelle intégrée et convient parfaitement aux charges de travail légères ou aux prototypes.



Gouvernez, déployez et suivez l'IA dans votre propre infrastructure
Blogs récents
Questions fréquemment posées
Pourquoi choisir une approche pilotée par un schéma plutôt que de coder les formulaires manuellement ?
Les formulaires pilotés par un schéma constituent une source unique de vérité pour vos manifestes de configuration. Ils garantissent un comportement cohérent de l'interface utilisateur, prennent en charge la logique imbriquée/conditionnelle sans duplication de code, et permettent au backend de piloter la structure du formulaire de manière dynamique.
Comment fonctionne la validation dans FormBuilder ?
La validation s'effectue sur deux niveaux : la validation de schéma déclarative (utilisant des règles comme obligatoire, motif ou min/max) et la validation personnalisée/asynchrone pour les contrôles adossés à une API, tels que la vérification de noms de ressources uniques.
Puis-je l'intégrer avec des bibliothèques React existantes ?
Oui. Le FormBuilder de TrueFoundry utilise react-hook-form comme moteur d'état principal, ce qui le rend compatible avec les modèles React standards et facile à étendre avec des composants personnalisés.











%20(28).webp)
.webp)

.png)

.png)















