Skip to Content
SchemaConditional Fields

Conditional Fields

How to hide fields or make them read-only based on other values in the record, or on the current user.

You can reduce the complexity of a form by controlling when fields appear, and by locking fields that should not be edited in certain situations. Use hidden to show or hide a field, and readOnly to make it non-editable. Both properties accept a static true or false, or a callback that returns a boolean.

This is commonly called conditional fields.

Prerequisites

  • A Flexkit schema you can edit. See Schemas for an introduction to entities and attributes.
  • Familiarity with JavaScript functions, since conditions are written as callbacks.

Examples

Hide based on a value in the current record

Only show the subtitle field if the product name is truthy :

React
{
  name: 'subtitle',
  label: 'Subtitle',
  scope: 'local',
  dataType: 'string',
  inputType: 'text',
  hidden: ({ record }) => !record.name,
}

Hide based on a sibling field

Show a compare-at price only when the product is on sale:

React
{
  name: 'onSale',
  label: 'On sale',
  scope: 'global',
  dataType: 'boolean',
  inputType: 'switch',
  defaultValue: false,
}
 
{
  name: 'compareAtPrice',
  label: 'Compare-at price',
  scope: 'local',
  dataType: 'float',
  inputType: 'number',
  hidden: ({ record }) => !record.onSale,
  validation: (z) => z.number().min(0, { message: 'Must be >= 0' }),
}

Hide based on a status value

Show restock fields only when inventory is depleted:

React
{
  name: 'inventory',
  label: 'Inventory',
  scope: 'global',
  dataType: 'int',
  inputType: 'number',
  defaultValue: 0,
}
 
{
  name: 'restockNote',
  label: 'Restock note',
  scope: 'local',
  dataType: 'string',
  inputType: 'textarea',
  hidden: ({ record, value }) => !value && Number(record.inventory) > 0,
}

Use value when the condition should also consider the current field. In the example above, a restock note that already has a value stays visible even after inventory is replenished.

Set read-only based on the current user’s role

Keep the SKU editable for owners; show it as read-only for everyone else:

React
{
  name: 'sku',
  label: 'SKU',
  scope: 'global',
  dataType: 'string',
  inputType: 'text',
  unique: true,
  readOnly: ({ currentUser }) => currentUser?.role !== 'owner',
}

Set read-only based on another field

Lock the price after a product is published:

React
{
  name: 'status',
  label: 'Status',
  scope: 'global',
  dataType: 'string',
  inputType: 'select',
  defaultValue: 'draft',
  options: {
    list: [
      { label: 'Draft', value: 'draft' },
      { label: 'Published', value: 'published' },
    ],
  },
}
 
{
  name: 'price',
  label: 'Price',
  scope: 'local',
  dataType: 'float',
  inputType: 'number',
  readOnly: ({ record }) => record.status === 'published',
}

You cannot return a Promise from hidden or readOnly callbacks. Conditions must be synchronous.

Mention a condition in the field options.comment when it might surprise editors. For example, explain that the compare-at price appears only when On sale is enabled.

Static vs callback visibility

  • hidden: true hides the field in the form and the list/grid column.
  • hidden: ({ record }) => … is form-only. List columns stay visible because a callback cannot be decided without a specific record.
  • Hidden fields still submit their existing values. They are only removed from the form UI.
  • Currently-read-only attributes are omitted from create and update mutations.
  • Currently-hidden and currently-read-only fields are not validated, so a required field the editor cannot change cannot block save.

This is different from entity.menu.hidden, which hides the entity from the sidebar menu.

Studio-only callbacks

Callbacks live in your TypeScript schema (flexkit.config.tsx) and run in Studio. They are stripped when the schema is serialized to JSON on deploy, the same as validation. Deployed JSON schemas can still use static hidden: true and readOnly: true.

Reference

Callback properties

The hidden and readOnly callbacks receive an object with the following properties:

record ({ [attributeName: string]: unknown })

The current entity values being edited. Values are unwrapped from form wrappers, so write record.status, not record.status.value. Use optional chaining when a field may be empty, for example record?.name.

value (unknown)

The current field’s value.

currentUser (object | undefined)

The signed-in user, or undefined when no user is loaded:

FieldTypeDescription
idstringUser id
emailstringEmail address
display_namestringDisplay name
avatar_urlstringAvatar URL
rolestringRole, for example owner or viewer
spacesstring[]Space codes the user can access

Attributes are a flat list on each entity. There are no nested object fields, so conditions always read sibling values from record.

Last updated on

© 2026