Builder Ref
Use useBuilderRef() with the Builder ref to access internal node actions and history from custom toolbars, menus, keyboard shortcuts, or surrounding workflow logic.
Basic builderRef setup
import '@vojtechportes/react-query-builder/styles.css';import React, { useState } from 'react';import {Builder,useBuilderRef,type DenormalizedQuery,type IBuilderFieldProps,} from '@vojtechportes/react-query-builder';const fields: IBuilderFieldProps[] = [{field: 'STATUS',label: 'Status',type: 'LIST',operators: ['EQUAL', 'NOT_EQUAL'],value: [{ value: 'ACTIVE', label: 'Active' },{ value: 'ARCHIVED', label: 'Archived' },],},];const initialData: DenormalizedQuery = [{type: 'GROUP',value: 'AND',isNegated: false,children: [{field: 'STATUS',operator: 'EQUAL',value: 'ACTIVE',},],},];export const BuilderRefExample = () => {const [data, setData] = useState<DenormalizedQuery>(initialData);const builderRef = useBuilderRef();return (<><buttontype="button"onClick={() => {const rootGroupId = builderRef.current?.getNodes().find(node => 'type' in node)?.id;if (!rootGroupId) {return;}builderRef.current?.addRule({field: 'STATUS',operator: 'NOT_EQUAL',value: 'ARCHIVED',},rootGroupId);}}>Add rule</button><Builder ref={builderRef} fields={fields} data={data} onChange={setData} /></>);};
When to use it
- Trigger builder changes from controls rendered outside the builder.
- Implement keyboard shortcuts for clone, delete, undo, or redo.
- Insert preconfigured rules or groups from business-specific UI flows.
- Inspect normalized nodes or history without reimplementing builder internals.
Read methods
Reading builder state
const builderRef = useBuilderRef();const allNodes = builderRef.current?.getNodes();const singleNode = builderRef.current?.getNodeById(nodeId);const denormalizedData = builderRef.current?.getData();const isCityInUse = builderRef.current?.isFieldInUse('CITY');const cityOptionState = builderRef.current?.getFieldOptionState('CITY');
getNodeById(id)returns one normalized node by id.getNodes()returns the current normalized node array.getData()returns the denormalized public query shape.isFieldInUse(field)tells you whether a field is currently present in the query.getFieldOptionState(field)returns runtime options and the current option status for that field.
Mutation methods
Mutating nodes
const builderRef = useBuilderRef();builderRef.current?.cloneNode(nodeId);builderRef.current?.moveNode(nodeId, 0, targetGroupId);builderRef.current?.setNodeLock(nodeId, 'self');builderRef.current?.unlockNode(nodeId);builderRef.current?.replaceNode(nodeId, nextNode);builderRef.current?.updateNode(nodeId, node => ({...node,readOnly: true,}));
cloneNode,deleteNode, andmoveNodefollow the same behavior as the built-in UI controls.addNode,addGroup,addRule, andinsertNodeslet you build structure imperatively.replaceNodeswaps a node directly, whileupdateNodeis useful when you want the next value to depend on the current node.setNodeLock,lockNode, andunlockNodewrite the same read-only states as the lock UI.
Dynamic field options
Managing field options
builderRef.current?.isFieldInUse('CITY');builderRef.current?.getFieldOptionState('CITY');builderRef.current?.setFieldOptionsStatus('CITY', 'loading');builderRef.current?.setFieldOptions('CITY', [{ value: 'PRG', label: 'Prague' },{ value: 'BRN', label: 'Brno' },]);builderRef.current?.invalidateFieldOptions('CITY');builderRef.current?.reloadFieldOptions('CITY');builderRef.current?.clearFieldOptions('CITY');
setFieldOptionsStatus(field, status)lets surrounding app code reflect loading, success, idle, or error state.setFieldOptions(field, options)replaces the runtime option set without changing the originalfieldsarray.invalidateFieldOptions(field)drops runtime options and falls back to the static options defined infield.value.clearFieldOptions(field)removes the runtime state entirely, which is useful when a dependent field leaves scope.
History methods
History access
const builderRef = useBuilderRef();const history = builderRef.current?.getHistory();builderRef.current?.undo();builderRef.current?.redo();builderRef.current?.setHistory({past: [],future: [],});
undo()andredo()use the same history engine as the built-in controls.getHistory()returns the currentpastandfuturestacks.setHistory()lets you replace or clear the internal history state.
Data shapes
Most imperative methods use normalized nodes because that matches the internal builder engine. Use
getData() when you need the public denormalized query shape.API reference