Skip to main content
Version: v7 / v8

QueryBuilder

Refer to the TypeScript reference page for information about the types and interfaces referenced below.

The primary export of react-querybuilder is the <QueryBuilder /> React component.

QueryBuilder uses the useQueryBuilder hook to merge props with context values and default values, generate update methods, and prepare the query schema.

Subcomponents

QueryBuilder renders a RuleGroup representing the root of the query.

The root RuleGroup is wrapped in a <div> with the standard queryBuilder class, custom classes from controlClassnames.queryBuilder, and data- attributes indicating enabled features like drag-and-drop and inline combinators.

Everything is wrapped in <QueryBuilderStateProvider> and <QueryBuilderContext.Provider>, which inherit ancestor context values and propagate them to subcomponents. Props take precedence over context values.

Props

All QueryBuilder props are optional, but fields and onQueryChange are typically required for useful functionality (see getting started guide).

note

RuleGroupTypeAny refers to either RuleGroupType or RuleGroupTypeIC types. See independent combinators for RuleGroupTypeIC details.

fields

OptionList<Field> | Record<string, Field>

Array of fields or option groups containing field arrays. Alternatively, pass an object where keys are field names and values are field definitions. Object format sorts field options alphabetically by label in the fieldSelector component.

tip

Field objects can include custom properties. The complete field object is passed as fieldData to OperatorSelector and ValueEditor components (see controlElements).

onQueryChange

(query: RuleGroupTypeAny) => void

Called with the updated query whenever changes are made within the component.

query

RuleGroupTypeAny

Query object of type RuleGroupType or RuleGroupTypeIC. When provided, makes <QueryBuilder /> a controlled component.

Use with onQueryChange for controlled component behavior. See examples here.

defaultQuery

RuleGroupTypeAny

The initial query when <QueryBuilder /> is uncontrolled.

caution

Don't provide both query and defaultQuery props. Use query + onQueryChange for controlled components, or defaultQuery (or neither) for uncontrolled components. onQueryChange fires on every update regardless.

Providing both props logs errors in development mode. Switching between defined/undefined query across renders also logs errors.

context

any

Container for passing arbitrary props to custom components. Default components ignore this prop, but it's passed to all components throughout the QueryBuilder tree.

operators

OptionList<Operator>

Array of available operators. Custom operators require label and either name or value (value takes precedence). Optional arity property ("unary", "binary", or number) determines value editor rendering—no value editor shows when arity is "unary" or < 2.

Use getOperators to build operator lists dynamically per field. Non-null getOperators results override this prop.

The default operator list is below.

export const defaultOperators: DefaultOperators = [
{ name: '=', value: '=', label: '=' },
{ name: '!=', value: '!=', label: '!=' },
{ name: '<', value: '<', label: '<' },
{ name: '>', value: '>', label: '>' },
{ name: '<=', value: '<=', label: '<=' },
{ name: '>=', value: '>=', label: '>=' },
{ name: 'contains', value: 'contains', label: 'contains' },
{ name: 'beginsWith', value: 'beginsWith', label: 'begins with' },
{ name: 'endsWith', value: 'endsWith', label: 'ends with' },
{ name: 'doesNotContain', value: 'doesNotContain', label: 'does not contain' },
{ name: 'doesNotBeginWith', value: 'doesNotBeginWith', label: 'does not begin with' },
{ name: 'doesNotEndWith', value: 'doesNotEndWith', label: 'does not end with' },
{ name: 'null', value: 'null', label: 'is null' },
{ name: 'notNull', value: 'notNull', label: 'is not null' },
{ name: 'in', value: 'in', label: 'in' },
{ name: 'notIn', value: 'notIn', label: 'not in' },
{ name: 'between', value: 'between', label: 'between' },
{ name: 'notBetween', value: 'notBetween', label: 'not between' },
];
Source: /packages/react-querybuilder/src/defaults.ts#L123-L142

combinators

OptionList

Array of combinators for RuleGroups. Default combinator list:

export const defaultCombinators: DefaultCombinators = [
{ name: 'and', value: 'and', label: 'AND' } as const,
{ name: 'or', value: 'or', label: 'OR' } as const,
];
Source: /packages/react-querybuilder/src/defaults.ts#L179-L182

baseField

Record<string, unknown>

Properties applied to all field objects. Individual field properties override base properties.

baseOperator

Record<string, unknown>

Properties applied to all operator objects from operators prop or getOperators. Individual operator properties override base properties.

baseCombinator

Record<string, unknown>

Properties applied to all combinator objects. Individual combinator properties override base properties.

controlClassnames

Partial<Classnames>

Assign custom CSS classes to QueryBuilder controls. Each property accepts Classname types: string, string[], or Record<string, any> (see clsx docs):

Usage example

Example: Adding "bold" class to "+ Rule" buttons (with CSS rule .bold { font-weight: bold; }):

function App() {
return (
<QueryBuilder controlClassnames={{ addRule: 'bold' }}>
)
}
PropertyClass(es) applied to...
queryBuilderthe outermost <div> element
ruleGroupeach <div> wrapping a group
headereach <div> wrapping a group's header controls
bodyeach <div> wrapping a group's body elements (child rules/groups)
combinatorseach <select> control for combinators
addRuleeach <button> that adds a rule
addGroupeach <button> that adds a group
cloneRuleeach <button> that clones a rule
cloneGroupeach <button> that clones a group
removeGroupeach <button> that removes a group
lockRuleeach <button> that locks/disables a rule
lockGroupeach <button> that locks/disables a group
notToggleeach <label> on a "not" (aka "inversion") toggle
ruleeach <div> containing a rule
fieldseach <select> control for selecting a field
matchModeeach <select> control for match modes
matchThresholdeach <input> for match thresholds
operatorseach <select> control for selecting an operator
valueeach <input> for entering a value
removeRuleeach <button> that removes a rule
shiftActionseach <span> wrapping the up/down controls for shifting rules/groups
dragHandleeach <span> acting as a drag handle
valueSourceeach <select> control for selecting a value source
actionElementeach <button> control
valueSelectoreach <select> control
betweenRulesinline combinator elements
validvalid rules and groups
invalidinvalid rules and groups
dndDraggingrules and groups while being dragged
dndOverrules and groups hovered over by a dragged element
dndCopyrules and groups hovered over by a dragged element when the drop effect is "copy"
dndGrouprules and groups hovered over by a dragged element when the Ctrl key is pressed
disableddisabled elements
valueListItemeach element in a series of value editors
branches(not applied, but see Styling overview)
hasSubQueryrules that render a subquery

controlElements

Partial<Controls>

See controlElements for a list of all control elements and their props.

getOperators

(field: string, misc: { fieldData: Field }) => OptionList<Operator> | null

Returns allowed operators for the given field. Returning null uses the operators prop (or defaults).

fieldData provides the complete Field object for accessing custom properties.

getValueEditorType

(field: string, operator: string, misc: { fieldData: Field }) => ValueEditorType

Returns the ValueEditor type for the given field and operator. Options: "text" (default), "select", "multiselect", "checkbox", "radio", "textarea", "switch".

fieldData provides the complete Field object for accessing custom properties.

getValueSources

(field: string, operator: string, misc: { fieldData: Field }) => ValueSources | ValueSourceFullOptions;

Returns allowed value sources for a given field and operator. Must return array with "value", "field", or both. Array elements can be FlexibleOption objects with name/value of "value" or "field".

Defaults to () => ["value"]. First array element becomes the initial selection.

fieldData provides the complete Field object for accessing custom properties.

getValueEditorSeparator

(field: string, operator: string, misc: { fieldData: Field }) => ReactNode;

Returns separator element between multiple value editors (e.g., for "between" operator). Can return any React element, including strings ("and", "to") or HTML elements.

fieldData provides the complete Field object for accessing custom properties.

getInputType

(field: string, operator: string, misc: { fieldData: Field }) => string

Returns the type attribute for <input /> elements. Only applies when getValueEditorType returns "text" or falsy. Defaults to "text".

fieldData provides the complete Field object for accessing custom properties.

getValues

(field: string, operator: string, misc: { fieldData: Field }) => OptionList

Returns allowed values for the given field and operator. Only applies when getValueEditorType returns "select", "multiselect", or "radio". Defaults to empty array.

fieldData provides the complete Field object for accessing custom properties.

getMatchModes

(field: string, misc: { fieldData: Field }) => boolean | MatchMode[] | FlexibleOption<MatchMode>[]

Returns valid MatchModes for a field. Must return MatchMode[], Option<MatchMode>[], or boolean. true allows all match modes; other values disable match modes.

fieldData provides the complete Field object for accessing custom properties.

getSubQueryBuilderProps

(field: string, misc: { fieldData: Field }) => QueryBuilderProps

Returns props for subqueries (see Subqueries) to override parent query builder props. Props like query, onQueryChange, and enableDragAndDrop are ignored for subqueries.

fieldData provides the complete Field object for accessing custom properties.

getDefaultField

string | ((fieldsData: OptionList<Field>) => string)

Default field for new rules. Accepts field name string or function returning field name based on fields prop.

getDefaultOperator

string | ((field: string, misc: { fieldData: Field }) => string)

Default operator for new rules. Accepts operator name string or function returning operator name.

fieldData provides the complete Field object for accessing custom properties.

getDefaultValue

(rule: RuleType, misc: { fieldData: Field }) => any

Returns default value for new rules based on existing rule properties.

fieldData provides the complete Field object for accessing custom properties.

getRuleClassname

(rule: RuleType, misc: { fieldData: Field }) => Classname

Generates custom classes for rule outer div based on rule properties.

fieldData provides the complete Field object for accessing custom properties.

getRuleGroupClassname

(ruleGroup: RuleGroupTypeAny) => Classname

Generates custom classes for group outer div based on group properties.

onAddRule

(rule: RuleType, parentPath: Path, query: RuleGroupTypeAny, context?: any) => RuleType | false

Called before adding a new rule. Return modified RuleType object to proceed, or false to cancel. Use findPath to locate the parent group. The context parameter passes data from custom addRuleAction components.

For independent combinators, add combinatorPreceding property to specify the combinator before the new rule. Otherwise uses the last rule's combinator or first default combinator.

tip

To prevent adding new rules, use controlElements={{ addRuleAction: () => null }} to hide the "+ Rule" button.

onAddGroup

<RG extends RuleGroupTypeAny>(ruleGroup: RG, parentPath: Path, query: RG, context?: any) => RG | false

Called before adding a new group. Return modified group object (same type: RuleGroupType or RuleGroupTypeIC) to proceed, or false to cancel. Use findPath to locate the parent group. The context parameter passes data from custom addGroupAction components.

For independent combinators, add combinatorPreceding property to specify the combinator before the new group. Otherwise uses the last rule's combinator or first default combinator.

tip

To prevent adding new groups, use controlElements={{ addGroupAction: () => null }} to hide the "+ Group" button.

onMoveRule

<RG extends RuleGroupTypeAny>(
rule: RuleType,
fromPath: Path,
toPath: Path | 'up' | 'down',
query: RG,
nextQuery: RG,
options: MoveOptions,
context?: any
) => RG | boolean;

Called before a rule is moved or shifted. Return:

  1. true - Allow the move/shift
  2. false - Cancel the move/shift
  3. New query object (based on query or nextQuery parameters)

onMoveGroup

<RG extends RuleGroupTypeAny>(
ruleGroup: RG,
fromPath: Path,
toPath: Path | 'up' | 'down',
query: RG,
nextQuery: RG,
options: MoveOptions,
context?: any
) => RG | boolean;

Called before a group is moved or shifted. Return:

  1. true - Allow the move/shift
  2. false - Cancel the move/shift
  3. New query object (based on query or nextQuery parameters)

onRemove

<RG extends RuleGroupTypeAny>(ruleOrGroup: RG | RuleType, path: Path, query: RG, context?: any) => boolean

Called before removing a rule or group. Return true to proceed or false to abort removal.

translations

Partial<Translations>

Provides internationalization (i18n) support by overriding translatable texts for specific locales.

All object keys and properties are optional. The translations object deep-merges with defaults below. Default placeholder names are "~" and labels are "------".

export const defaultTranslations: TranslationsFull = {
fields: {
title: 'Fields',
placeholderName: defaultPlaceholderFieldName,
placeholderLabel: defaultPlaceholderFieldLabel,
placeholderGroupLabel: defaultPlaceholderFieldGroupLabel,
} as const,
operators: {
title: 'Operators',
placeholderName: defaultPlaceholderOperatorName,
placeholderLabel: defaultPlaceholderOperatorLabel,
placeholderGroupLabel: defaultPlaceholderOperatorGroupLabel,
} as const,
values: {
title: 'Values',
placeholderName: defaultPlaceholderValueName,
placeholderLabel: defaultPlaceholderValueLabel,
placeholderGroupLabel: defaultPlaceholderValueGroupLabel,
} as const,
matchMode: {
title: 'Match mode',
} as const,
matchThreshold: {
title: 'Match threshold',
} as const,
value: {
title: 'Value',
} as const,
removeRule: {
label: '⨯',
title: 'Remove rule',
} as const,
removeGroup: {
label: '⨯',
title: 'Remove group',
} as const,
addRule: {
label: '+ Rule',
title: 'Add rule',
} as const,
addGroup: {
label: '+ Group',
title: 'Add group',
} as const,
combinators: {
title: 'Combinators',
} as const,
notToggle: {
label: 'Not',
title: 'Invert this group',
} as const,
cloneRule: {
label: '⧉',
title: 'Clone rule',
} as const,
cloneRuleGroup: {
label: '⧉',
title: 'Clone group',
} as const,
shiftActionUp: {
label: '˄',
title: 'Shift up',
} as const,
shiftActionDown: {
label: '˅',
title: 'Shift down',
} as const,
dragHandle: {
label: '⁞⁞',
title: 'Drag handle',
} as const,
lockRule: {
label: '🔓',
title: 'Lock rule',
} as const,
lockGroup: {
label: '🔓',
title: 'Lock group',
} as const,
lockRuleDisabled: {
label: '🔒',
title: 'Unlock rule',
} as const,
lockGroupDisabled: {
label: '🔒',
title: 'Unlock group',
} as const,
valueSourceSelector: {
title: 'Value source',
} as const,
} satisfies TranslationsFull;
Source: /packages/react-querybuilder/src/defaultControlElements.ts#L34-L124

showCombinatorsBetweenRules

boolean (default false) Click here for demo

Renders combinator selectors between child rules/groups instead of in the group header, encouraging more natural query reading.

The combinator property remains group-level—inline selectors update all combinators within the same group. For independently managed inline combinators, use independent combinators.

showNotToggle

boolean (default false) Click here for demo

Displays "Not" (inversion) toggle switch for each rule group.

showCloneButtons

boolean (default false) Click here for demo

Displays "clone" button on each group header and rule. Clicking creates an exact duplicate (with new id) positioned immediately after the original.

showLockButtons

boolean (default false) Click here for demo

Displays "Lock rule" and "Lock group" buttons. Locked rules disable all elements except the lock button. Locked groups disable header elements (except lock button) and all child elements (including child lock buttons).

showShiftActions

boolean (default false) Click here for demo

Displays "shift up"/"shift down" buttons at the front of each rule and group (except root), stacked vertically by default. Upper button shifts up one position, lower button shifts down. Configure button labels via translations.shiftActionUp and translations.shiftActionDown.

resetOnFieldChange

boolean (default true) Click here for demo with this feature disabled

Pass false to preserve operator and value when field changes.

resetOnOperatorChange

boolean (default false) Click here for demo

Resets value when operator changes.

enableMountQueryChange

boolean (default true)

Pass false to disable onQueryChange on initial mount. Enabled by default because query/defaultQuery props are processed during first render and may differ from input (e.g., generated ids).

autoSelectField

boolean (default true) Click here for demo with this feature disabled

Pass false to add an "empty" option (value "~", label "------") as first element in fields array. This becomes the initial selection for new rules. Empty field selection hides operator selector and value components. Customize via translations.fields.placeholder*.

autoSelectOperator

boolean (default true) Click here for demo with this feature disabled

Pass false to add an "empty" option (value "~", label "------") as first element in operators array. This becomes the initial selection for new rules. Empty operator selection hides value components. Customize via translations.operators.placeholder*.

autoSelectValue

boolean (default true) Click here for demo with this feature disabled

Pass false to add an "empty" option (value "~", label "------") as first element in values array. This becomes the initial selection for rules with select list value editors. Customize via translations.values.placeholder*.

addRuleToNewGroups

boolean (default false) Click here for demo

Automatically adds a rule to new groups. When no query/defaultQuery prop is provided, adds a rule to the root group on mount. Groups with empty rules arrays don't receive automatic rules.

listsAsArrays

boolean (default false) Click here for demo

Updates rule values representing lists with proper arrays instead of comma-separated strings. Applies when valueEditorType is "multiselect" or operator is "between", "notBetween", "in", or "notIn".

For example, the default behavior for the "between" operator might produce this rule:

{
"field": "f1",
"operator": "between",
"value": "f2,f3",
"valueSource": "field"
}

When listsAsArrays is true, the rule's value will be an array:

{
"field": "f1",
"operator": "between",
"value": ["f2", "f3"],
"valueSource": "field"
}

parseNumbers

boolean | "enhanced" | "enhanced-limited" | "native" | "native-limited" | "strict" | "strict-limited" (default false) Click here for demo

Sets value type as number instead of string when possible. See Number parsing and formatQuery option.

info

Implemented in ValueEditor, not QueryBuilder. Custom value editors receive this prop but must implement the behavior themselves.

enableDragAndDrop

boolean (default false) Click here for demo

caution

No need to set this prop directly—it only works when:

  1. QueryBuilderDnD context provider from @react-querybuilder/dnd exists higher in the component tree
  2. react-dnd and react-dnd-html5-backend are installed

When conditions are met and enableDragAndDrop isn't explicitly false, it becomes implicitly true.

When true, displays a drag handle on the left side of each group header and rule for visual reordering.

npm i react-querybuilder @react-querybuilder/dnd react-dnd react-dnd-html5-backend
# OR yarn add / pnpm add / bun add
import { QueryBuilderDnD } from '@react-querybuilder/dnd';
import * as ReactDnD from 'react-dnd';
import * as ReactDndHtml5Backend from 'react-dnd-html5-backend';
import { QueryBuilder } from 'react-querybuilder';

const App = () => (
<QueryBuilderDnD dnd={{ ...ReactDnD, ...ReactDndHtml5Backend }}>
<QueryBuilder />
</QueryBuilderDnD>
);
tip

If your app already uses react-dnd, use QueryBuilderDndWithoutProvider instead of QueryBuilderDnD. Both are functionally equivalent, but the former assumes an existing <DndProvider /> higher in the tree. The latter renders its own DndProvider, causing conflicts with pre-existing ones (error: "Cannot have two HTML5 backends at the same time").

disabled

boolean | Path[] (default false) Click here for demo

Pass true to disable all subcomponents and prevent query changes. Pass path array to disable specific rules/groups. Example: disabled={[[0]]} disables the top-most rule/group and its subcomponents only.

suppressStandardClassnames

boolean (default false) Click here for demo

Disables standard classnames, including conditional and event-based classes for validation, drag-and-drop, etc.

debugMode

boolean (default false) Click here for demo

Enables debug logging with the onLog function.

onLog

(message: any) => void (default console.log)

Receives logging messages when debugMode is enabled.

idGenerator

() => string (default generateID)

Generates ids for rules and groups without them, or clones needing new ids. Default generates valid v4 UUIDs per RFC 4122 using crypto package or Math.random() fallback.

accessibleDescriptionGenerator

(props: { path: Path; qbId: string; }) => string (default generateAccessibleDescription)

Generates title attribute for each rule group's outermost <div>. For accessibility, output should be meaningful, descriptive, and unique within the page.

maxLevels

number (default Infinity)

Limits query nesting levels. Adding groups beyond this limit gets ignored. Useful for preventing overly complex queries.

validator

QueryValidator Click here for demo

Executed on each render. Return boolean (true = valid, false = invalid) or object with rule/group ids as keys. Object values can be boolean or { valid: boolean, reasons?: array }. The validation object gets passed to rule/group components. See validation documentation.

Independent combinators

Props specifying RuleGroupTypeAny are inferred as RuleGroupType or RuleGroupTypeIC. RuleGroupTypeIC queries use "independent combinators."

This places combinator values between each neighboring rule/group pair (every odd-numbered index). Similar to showCombinatorsBetweenRules, but each combinator is independently controlled. Users often find this more natural as it mirrors natural language query expression.

A standard query (RuleGroupType) might look like this:

const query: RuleGroupType = {
combinator: 'and',
rules: [
{ field: 'f1', operator: '=', value: 'v1' },
{ field: 'f2', operator: '=', value: 'v2' },
],
};

The same query using independent combinators would look like this:

const query: RuleGroupTypeIC = {
rules: [
{ field: 'f1', operator: '=', value: 'v1' },
'and',
{ field: 'f2', operator: '=', value: 'v2' },
],
};

Click here to load the demo with a query using independent combinators.