User Field
User and owner selector with avatar display
The User Field component provides a user selector for assigning users or owners to records. It displays user avatars and supports both single and multiple user selection.
Basic Usage
Single User Selection
Multiple Users
Multiple User Selection
Read-Only Record Owner
A record-owner field is a plain user field whose NAME carries the ownership
meaning — there is no separate owner type. Marking it readonly is what makes
it display-only.
Record Owner Read Only
Field Schema
A user field is authored as UserFieldMetadata (@object-ui/types), which is the
source of truth for the key set: it extends BaseFieldMetadata with the picker style,
the subtitle fields, the avatar field and the candidate filters. user is a lookup
specialised to the sys_user system object, so its filters are LookupFilterDef.
import type { UserFieldMetadata } from '@object-ui/types';
const owner: UserFieldMetadata = {
type: 'user',
name: 'owner_id',
label: 'Owner',
required: true,
multiple: false,
picker: 'search',
subtitle: ['primary_business_unit_id.name', 'email'],
avatar_field: 'image',
lookupFilters: [{ field: 'banned', operator: 'ne', value: true }],
};The field stores the selected user's id (or an array of ids when multiple is set),
not a user object; the picker resolves names and avatars from sys_user itself.
The value being edited, and the className / disabled a host supplies, are not
metadata keys — they are runtime widget props. See Field Widget Props.
User vs Owner
Both are the same field type. What differs is the field's name and whether it is writable:
- Assignment field: a selectable
userfield for assignees, team members, etc. - Owner field: a
userfield namedowner, typicallyreadonlyand defaulted to the record creator.
There is no owner field type. It existed as a synonym until objectui#4814
retired it (it resolved to the very same widget, and it was never a member of
@objectstack/spec's FieldType). Authoring type: 'owner' now renders a
visible refusal naming this migration rather than silently falling back to a
text input. Write { type: 'user', name: 'owner' } instead.
Display Features
- Avatar Badges: User avatars with initials if no image
- Color Coding: Consistent colors for user avatars
- Multiple Display: Shows up to 3 users, then "+N more"
- Hover Info: User details on avatar hover
Cell Renderer
In tables/grids, displays avatar with name:
import { UserCellRenderer } from '@object-ui/fields';
// Single user: Avatar + name
// Multiple users: Overlapping avatars + countHow it works
user fields are a lookup specialized to the framework's sys_user
object — there is no custom user API to wire up. The UserField widget
delegates to the shared lookup picker with the reference fixed to sys_user,
reusing the same debounced search, record-picker dialog and id resolution as any
lookup field:
- The field stores the selected user's id (a foreign key to
sys_user);multiple: truestores an array of ids. - A
dataSource(provided bySchemaRenderer/ the app shell) supplies the candidate search — the picker queriessys_userby name/email as you type. - On read,
$expandresolves the id(s) to the full user record so cells and read-only views show the name/avatar (viaUserCellRenderer).
No custom user-management integration is required when a dataSource is present.
Permission Patterns
Common permission configurations:
// Record owner only — a `user` field whose NAME carries the ownership meaning
{
type: 'user',
name: 'owner',
label: 'Owner',
readonly: true,
defaultValue: 'current_user'
}
// Assignable user
{
type: 'user',
name: 'assigned_to',
label: 'Assigned To',
required: true
}
// Team members
{
type: 'user',
name: 'collaborators',
label: 'Collaborators',
multiple: true
}Use Cases
- Task Management: Task assignee, reviewer
- CRM: Account owner, opportunity owner
- Support: Ticket assignee, support agent
- Project Management: Project members, task owners
- Approval Workflows: Approvers, reviewers
- Collaboration: Document collaborators, editors
Backend behavior
The platform handles the common cases natively — you generally don't write backend code for user fields:
- Owner stamping — declare
defaultValue: 'current_user'and the acting user's id is filled in at insert time (no create hook required). - Display resolution — request
expand=<field>and the stored id(s) resolve to the fullsys_userrecord (name / avatar) for rendering. - Ownership & permissions — record ownership and row-level security continue
to use the platform's
owner_idconvention and security rules.