The standard field library and registry for Object UI.
- 📚 Standard Fields - Implementation of all ObjectStack protocol fields (Text, Number, Date, Lookup, etc.)
- 🔌 Plugin System -
FieldRegistryallows registering custom renderers or overriding standard ones. - 🛠 Helpers - Utilities for schema mapping, validation, and expression evaluation.
npm install @object-ui/fieldsThe Field Registry is the core mechanism that allows decoupling view components from specific field implementations.
You can override standard fields or add new ones:
import { registerFieldRenderer } from '@object-ui/fields';
import { MyCustomColorPicker } from './MyCustomColorPicker';
// Register a new 'color' field type
registerFieldRenderer('color', MyCustomColorPicker);View components use getCellRenderer to resolve the correct component for a field type.
import { getCellRenderer } from '@object-ui/fields';
const MyGridCell = ({ field, value }) => {
const Renderer = getCellRenderer(field.type);
return <Renderer field={field} value={value} />;
};Supported types out of the box:
- Basic:
text,textarea,number,boolean - Format:
currency,percent - Date:
date,datetime,time - Selection:
select,lookup,master_detail - Contact:
email,phone,url - Media:
file,image - System:
formula,summary,auto_number
The full widget surface is exported for consumers that render field widgets outside a record form (ADR-0059):
FORM_FIELD_TYPES— the frozen list of every type the form can render.resolveFormWidgetType(type)— resolves any field-type spelling to its widget key (spec aliases liketoggle/json/secretincluded; unknown types fall back totext, mirroring the form).getLazyFieldWidget(type)— the widget wrapped inReact.lazy(cached per type; render inside<Suspense>), sharing the same loadersregisterFielduses so nothing is bundled eagerly.
The app-shell ActionParamDialog uses these to render declared action params
through the exact same widgets as the object form — with a drift test pinning
param support ⊇ form support.
GridField (the master-detail line-items grid) supports type: 'file' columns:
the cell renders a compact upload button plus removable file chips (thumbnails
for images) instead of degrading to a text input, so users can attach a receipt
or photo per row without opening the row form (objectui#2360). Columns accept
accept?: string[] and multiple?: boolean; uploads run through the same
UploadProvider pipeline as the full-size FileField (the compact control is
exported as FileCell). Auto-derived subform columns map file/image/
avatar fields to file columns instead of dropping them.
A select field declared multiple: true selects zero-or-more values (spec
allows multiple on select). SelectField delegates to the multi-value chip
picker (the same widget the multiselect type uses) and stores a string[].
Delegating inside SelectField — rather than at a type-resolution layer — means
every surface that renders the select widget (the object form, the inline grid
editor, and the app-shell ActionParamDialog) gets multi-select identically,
with no per-surface drift. Both single- and multi-value selects resolve
per-option visibleWhen cascading and dependsOn gating through the same
useCascadingOptions hook, so the
offered chips narrow (and now-invalid selections are pruned) exactly as the
single dropdown does.
select, multiselect, radio, and checkboxes options support a per-option
visibleWhen CEL predicate (offered only when TRUE, evaluated against the live
record + current_user) and a field-level dependsOn. Together they drive
dependent selects (country → province → city) and role-gated options with no
bespoke matrix — the same primitives dependent lookups use. All four widgets
resolve this through the shared useCascadingOptions
hook, which wraps the pure resolveCascadingOptions helper in @object-ui/core
(also used by the form renderer's inline pre-filter, so gating and filtering
never drift). While a dependsOn parent is empty the control is gated; a parent
change re-filters the list and clears a now-invalid value (scalar select /
radio drop the value; multi-value multiselect / checkboxes prune just the
offered-out entries).
Client-side hiding is UX only — gate authorization-sensitive values on the
server too. See
content/docs/fields/select.mdx.
MIT — see LICENSE.