Locking and Read-only
Locking can be applied at the builder, rule, or group level. The key distinction is that rules lock only themselves, while groups can lock either just their own controls or their entire subtree. Object-based readOnly configs also support targeted read-only for specific controls.
GUI Locking
Set lockable on Builder to render lock controls directly in the UI. The built-in controls update the same readOnly fields that are already part of the query data model, so the resulting lock state is preserved in output. If a node already has readOnly.targets, the lock toggle preserves those targets and only changes whether the lock is enabled and, for groups, whether it inherits to descendants.
import '@vojtechportes/react-query-builder/styles.css';<Builderfields={fields}data={data}lockableonChange={setData}/>;// Rules cycle through:// unlocked -> locked//// Groups cycle through:// unlocked -> locked group only -> locked group and descendants//// The emitted query stores those states in readOnly:// rule: readOnly: true// group: readOnly: true// group + descendants: readOnly: { enabled: true, inheritToChildren: true }//// If a node already uses readOnly.targets, the lock toggle preserves those// targets and only changes enabled/inheritToChildren.
- Rules cycle through two states: unlocked and locked.
- Groups cycle through three states: unlocked, locked group only, and locked group with descendants.
- The default group cycle maps to
false,true, and{ enabled: true, inheritToChildren: true }. - When a parent group inherits a lock to descendants, child lock controls render disabled because descendants cannot override that inherited state.
- When
cloneableis also enabled, the clone button renders immediately to the left of the lock button.
Targeted read-only
Use object-based readOnly configs when you want to keep specific controls visible but non-editable instead of locking the entire rule or group.
const data: DenormalizedQuery = [{type: 'GROUP',value: 'AND',isNegated: false,readOnly: {enabled: true,targets: ['combinator'],},children: [{field: 'CUSTOMER_COUNTRY',operator: 'EQUAL',value: 'CZ',readOnly: {enabled: true,targets: ['field', 'operator'],},},{field: 'ORDER_TOTAL',operator: 'BETWEEN',value: [1000, 5000],readOnly: {enabled: true,targets: ['value'],},},],},];
- Rule targets are
field,operator, andvalue. - Group targets are
combinatorandnegation. - If
enabledistrueandtargetsis omitted, the whole node is read-only. - If
enabledisfalse, the config stays dormant until the node is locked again.
How each level behaves
<Builder readOnly />locks the entire builder. No rules or groups remain editable.rule.readOnly = truelocks only that rule. Siblings and parent groups are unaffected.rule.readOnly = { enabled: true, targets: ['field'] }keeps the field visible but non-editable, and also prevents deleting that rule.group.readOnly = truelocks only that group's own controls. Its child rules and child groups stay editable by default.group.readOnly = { enabled: true, targets: ['combinator'] }keeps the group combinator visible but non-editable without forcing descendant rules to become read-only.group.readOnly = { enabled: true, inheritToChildren: true }locks the group and all descendant rules and groups.
import '@vojtechportes/react-query-builder/styles.css';const data: DenormalizedQuery = [{type: 'GROUP',value: 'AND',isNegated: false,children: [{field: 'STATUS',operator: 'EQUAL',value: 'ACTIVE',readOnly: {enabled: true,targets: ['field', 'operator'],},},{type: 'GROUP',value: 'OR',isNegated: false,readOnly: {enabled: true,targets: ['combinator'],},children: [{field: 'COUNTRY',operator: 'EQUAL',value: 'CZ',},],},{type: 'GROUP',value: 'AND',isNegated: false,readOnly: {enabled: true,inheritToChildren: true,},children: [{field: 'IS_VAT_PAYER',operator: 'EQUAL',value: true,},],},],},];<Builder fields={fields} data={data} onChange={setData} />;
Custom Lock Control
The default lock button can be replaced through components.LockToggle.
import '@vojtechportes/react-query-builder/styles.css';const components = {LockToggle: MyLockToggle,};<Builderfields={fields}data={data}lockablecomponents={components}onChange={setData}/>;// LockToggle receives:// state: 'unlocked' | 'self' | 'all'// nodeType: 'rule' | 'group'// disabled?: boolean// onChange?: (nextState) => void
Custom Clone Control
The default clone button can be replaced through components.CloneButton.
import '@vojtechportes/react-query-builder/styles.css';const components = {CloneButton: MyCloneButton,};<Builderfields={fields}data={data}cloneablecomponents={components}onChange={setData}/>;// CloneButton receives:// nodeType: 'rule' | 'group'// disabled?: boolean// onClick?: () => void
What "locked" means in the UI
- Read-only targets stay visible. They become disabled, not hidden.
- Locked rules cannot change field, operator, or value, and cannot be deleted.
- Targeted rule read-only also blocks deleting that rule, even if only one target such as
fieldis protected. - Locked groups cannot change their group operator, negation, add actions, or delete action.
- By default, groups also cannot be deleted when that would remove protected descendants indirectly.
- Set
Builder.readOnlyProtectsDelete=to disable that subtree delete protection while keeping direct node-level read-only deletion rules. - Locked rules and groups are removed from drag-and-drop interactions.
- Clone controls render only for editable rules and groups. Cloned nodes preserve their read-only configuration.
- A locked group without inheritance still renders editable descendants when those descendants are not otherwise locked.
Inheritance and precedence
- Inheritance only applies to groups, never to rules.
inheritToChildrenhas an effect only whenenabledistrue.- Once a parent group inherits read-only to descendants, child nodes cannot opt back into editability with
readOnly: false. - Descendants may still add their own local
readOnlyflags, but they cannot override an inherited lock from an ancestor. - Group target inheritance preserves only the group-level semantics of the inherited lock. It does not turn rule-specific controls such as
fieldorvalueinto inherited targets.