Skip to main content
Version: v5

External controls

React Query Builder exports the same query tools it uses internally to manage query updates. These functions can be used outside the context of the <QueryBuilder /> component, giving developers greater flexibility in UI design while enabling the same query management features.

In the example below, the default add/remove buttons have been hidden. The internal query methods (add, remove, update, and move) are used within the event handlers assigned to the controls above the query builder. The function return values are assigned to the same state variable that the query builder uses, so the updates are synchronized.

import { useState } from 'react';
import {
  add,
  defaultCombinators,
  Field,
  move,
  QueryBuilder,
  remove,
  RuleGroupType,
  update,
} from 'react-querybuilder';
import 'react-querybuilder/dist/query-builder.css';

const fields: Field[] = [
  { name: 'firstName', label: 'First Name' },
  { name: 'lastName', label: 'Last Name' },
];

const initialQuery: RuleGroupType = {
  combinator: 'and',
  rules: [
    { field: 'firstName', operator: '=', value: 'Steve' },
    { field: 'lastName', operator: '=', value: 'Vai' },
  ],
};

const NullComponent = () => null;

export default () => {
  const [query, setQuery] = useState(initialQuery);

  // add
  const addRule = () =>
    setQuery(add(query, { field: 'firstName', operator: '=', value: 'Steve' }, []));

  // remove
  const removeFirstRule = () => setQuery(remove(query, [0]));

  // update
  const updateCombinator = (e: ChangeEvent<HTMLSelectElement>) =>
    setQuery(update(query, 'combinator', e.target.value, []));

  // move
  const moveBottomRuleToTop = () => setQuery(move(query, [query.rules.length - 1], [0]));

  return (
    <div>
      <div style={{ display: 'flex', gap: '0.5rem', marginBottom: '0.5rem' }}>
        <button onClick={addRule} title="Add a rule at the bottom of the group">
          Add rule
        </button>
        <button onClick={removeFirstRule} title="Remove the top-most rule">
          Remove first rule
        </button>
        <button onClick={moveBottomRuleToTop} title="Move the bottom-most rule to the top">
          Move bottom rule to top
        </button>
        <select value={query.combinator} onChange={updateCombinator} title="Update the combinator">
          {defaultCombinators.map(c => (
            <option key={c.name} value={c.name}>
              Update combinator to {c.label}
            </option>
          ))}
        </select>
      </div>
      <QueryBuilder
        fields={fields}
        query={query}
        onQueryChange={q => setQuery(q)}
        controlElements={{
          // These declarations prevent the "+Rule", "+Group",
          // and "x" (rule removal) buttons from rendering:
          addGroupAction: NullComponent,
          addRuleAction: NullComponent,
          removeRuleAction: NullComponent,
        }}
      />
    </div>
  );
};