[FIXED] An an extra Add Rule button to react-querybuilder


I am using react-querybuilder what I need is another Add Rule button to be add next to the original one and want to add differnt set of fields and operators when using the new button. Here is some part of my code:

import { HBButton, HBIcon } from '@hasty-bazar/core'
import { FC } from 'react'
import { useIntl } from 'react-intl'
import queryBuilderMessages from '../HBQueryBuilder.messages'

interface AddRuleActionProps {
  handleOnClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void

const AddGroupAction: FC<AddRuleActionProps> = ({ handleOnClick }) => {
  const { formatMessage } = useIntl()
  return (
      leftIcon={<HBIcon type="plus" />}
      sx={{ marginRight: 2, minWidth: 50 }}
   // >>> ANOTHER HBButton with different implementation to be added here 

export default AddGroupAction


Adding a new answer based on your feedback and because this one is very different from the other. I’m about to release v5.0 of react-querybuilder that has the feature I mentioned in the first paragraph of the other answer. This makes achieving the desired result much more straightforward and also eliminates the need for external state management (i.e. Redux).

TL;DR: working codesandbox example here (uses react-querybuilder@5.0.0-alpha.2).

React Query Builder only takes one fields prop, but you can organize the fields into an array of option groups instead of a flat array. I set the operators property on each field to the default operators, filtered appropriately for the type of field (text vs numeric).

import { Field, OptionGroup } from 'react-querybuilder';
import { nameOperators, numberOperators } from './operators';

export const fields: OptionGroup<Field>[] = [
    label: 'Names',
    options: [
      { name: 'firstName', label: 'First Name', operators: nameOperators },
      { name: 'lastName', label: 'Last Name', operators: nameOperators },
    label: 'Numbers',
    options: [
      { name: 'height', label: 'Height', operators: numberOperators },
      { name: 'weight', label: 'Weight', operators: numberOperators },

Next I set up a custom field selector component to only allow fields that are part of the same option group. So if a "name" field is chosen, the user can only select other "name" fields.

const FilteredFieldSelector = (props: FieldSelectorProps) => {
  const filteredFields = fields.find((optGroup) =>
    optGroup.options.map((og) => og.name).includes(props.value!)

  return <ValueSelector {...{ ...props, options: filteredFields }} />;

This custom Add Rule button renders a separate button for each option group that calls the handleOnClick prop with the option group’s label as context.

const AddRuleButtons = (props: ActionWithRulesAndAddersProps) => (
      .map((og) => og.label)
      .map((lbl) => (
        <button onClick={(e) => props.handleOnClick(e, lbl)}>
          +Rule ({lbl})

The context is then passed to the onAddRule callback, which determines what field to assign based on the context value.

const onAddRule = (
  rule: RuleType,
  _pP: number[],
  _q: RuleGroupType,
  context: string
) => ({
  field: fields.find((optGroup) => optGroup.label === context)!.options[0].name,

Put it all together in the QueryBuilder props, and voilà:

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

  return (
        onQueryChange={(q) => setQuery(q)}
          addRuleAction: AddRuleButtons,
          fieldSelector: FilteredFieldSelector,
      <pre>{formatQuery(query, 'json')}</pre>

Answered By – Jake Boone

Answer Checked By – Gilberto Lyons (Easybugfix Admin)

Leave a Reply

(*) Required, Your email will not be published