npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vojtechportes/react-query-builder

v1.31.1

Published

Simple, highly configurable query builder for react

Readme

React Query Builder


React Query Builder is a TypeScript library for building nested filter editors, formatting them into external query syntaxes, and parsing supported expressions back into builder state.

It also supports an optional SQL text mode for Builder, with built-in syntax and semantic validation, plus an optional Monaco-based advanced editor integration for protected locked ranges.

Field-to-field comparisons are also supported through allowFieldComparisons, per-field fieldComparison metadata, and valueSource: 'field' / valueField rule data.

Full documentation, API reference, and the interactive demo are available on the project website:

  • Documentation
  • API
  • Demo

Installation

npm install @vojtechportes/react-query-builder

React Query Builder supports React 18+.

Example

import React, { useState } from 'react';
import {
  Builder,
  type DenormalizedQuery,
  type IBuilderFieldProps,
} from '@vojtechportes/react-query-builder';

const fields: IBuilderFieldProps[] = [
  {
    field: 'STATE',
    label: 'State',
    type: 'LIST',
    operators: ['EQUAL', 'NOT_EQUAL'],
    value: [
      { value: 'CZ', label: 'Czech Republic' },
      { value: 'SK', label: 'Slovakia' },
    ],
  },
  {
    field: 'IS_IN_EU',
    label: 'Is in EU',
    type: 'BOOLEAN',
  },
];

const initialData: DenormalizedQuery = [
  {
    type: 'GROUP',
    value: 'AND',
    isNegated: false,
    children: [
      {
        field: 'STATE',
        operator: 'EQUAL',
        value: 'CZ',
        readOnly: true,
      },
      {
        field: 'IS_IN_EU',
        operator: 'EQUAL',
        value: true,
      },
    ],
  },
];

export const MyBuilder = () => {
  const [data, setData] = useState(initialData);

  return <Builder fields={fields} data={data} onChange={setData} />;
};

Validation and Usage Limits

Field metadata can define both value validation and structural usage limits. Use validation for rule-local checks like required values or numeric bounds, and use usageLimit when a field or shared field bucket should only appear a limited number of times globally or within the same parent group.

const fields: IBuilderFieldProps[] = [
  {
    field: 'PRIMARY_EMAIL',
    label: 'Primary email',
    type: 'TEXT',
    operators: ['EQUAL', 'CONTAINS'],
    usageLimit: {
      max: 1,
      scope: 'global',
    },
    validation: {
      required: true,
    },
  },
  {
    field: 'BILLING_CONTACT',
    label: 'Billing contact',
    type: 'TEXT',
    operators: ['EQUAL'],
    usageLimit: {
      key: 'contact-field',
      max: 1,
      scope: 'parent',
    },
  },
  {
    field: 'SHIPPING_CONTACT',
    label: 'Shipping contact',
    type: 'TEXT',
    operators: ['EQUAL'],
    usageLimit: {
      key: 'contact-field',
      max: 1,
      scope: 'parent',
    },
  },
];

When a usage limit is exhausted, the field becomes disabled in the selector, and the built-in Add Rule action is disabled when no selectable fields remain in the current scope.

  • Documentation: Validation
  • API: Fields

Dynamic Field Options

When list options depend on other rules or on external application state, keep the fields array stable and update options through the imperative builderRef API instead of replacing the full field definition.

This is especially useful for dependent selects, async option loading, and integrations with tools like TanStack React Query.

Use field-level methods when every rule of the same field should share one runtime option set:

  • isFieldInUse(field)
  • setFieldOptions(field, options)
  • setFieldOptionsStatus(field, status)
  • invalidateFieldOptions(field)
  • reloadFieldOptions(field)
  • clearFieldOptions(field)

Use rule-level methods when options depend on other rules or on surrounding app state:

  • bindRuleOptions(field, config)
  • useBuilderRuleDependencies(builderRef, field, dependencyFields)
  • subscribeToRuleDependencies(field, dependencyFields, listener)
  • setRuleOptions(ruleId, options)
  • setRuleOptionsStatus(ruleId, status)
  • reconcileRuleValueWithOptions(ruleId, { strategy: 'clear-if-missing' })

See the Dynamic Field Options documentation for the full concept, live example, and React Query integration example:

  • Dynamic Field Options Documentation

UI Adapters

The package also exposes ready-made component mappings through versioned subpath exports:

  • @vojtechportes/react-query-builder/mui/v9 for new Material UI projects
  • @vojtechportes/react-query-builder/mui/v7 for applications still on MUI 7
  • @vojtechportes/react-query-builder/bootstrap/v5 for Bootstrap 5 projects
  • @vojtechportes/react-query-builder/antd/v6 for new Ant Design projects
  • @vojtechportes/react-query-builder/antd/v5 for applications still on Ant Design 5
  • @vojtechportes/react-query-builder/mantine/v9 for new Mantine projects
  • @vojtechportes/react-query-builder/mantine/v8 for applications still on Mantine 8
  • @vojtechportes/react-query-builder/fluentui/v8 for Fluent UI React 8 projects
  • @vojtechportes/react-query-builder/radix/v1 for Radix Themes 1 projects

Install the peer dependencies that match the adapter you want to use and pass the exported components object to Builder.

Bootstrap 5 example:

import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
import { Builder } from '@vojtechportes/react-query-builder';
import { components } from '@vojtechportes/react-query-builder/bootstrap/v5';

export const MyBootstrapBuilder = () => {
  return (
    <Builder
      fields={fields}
      data={data}
      components={components}
      onChange={setData}
    />
  );
};

Material UI example:

npm install @mui/material@^9.0.1 @mui/icons-material@^9.0.1 @emotion/react @emotion/styled
import React from 'react';
import { Builder } from '@vojtechportes/react-query-builder';
import { components } from '@vojtechportes/react-query-builder/mui/v9';

export const MyMuiBuilder = () => {
  return (
    <Builder
      fields={fields}
      data={data}
      components={components}
      onChange={setData}
    />
  );
};

Ant Design example:

npm install antd@^6.0.0 @ant-design/icons@^6.0.0
import React from 'react';
import { Builder } from '@vojtechportes/react-query-builder';
import { components } from '@vojtechportes/react-query-builder/antd/v6';

export const MyAntdBuilder = () => {
  return (
    <Builder
      fields={fields}
      data={data}
      components={components}
      onChange={setData}
    />
  );
};

Mantine example:

npm install @mantine/core@^9.0.0 @mantine/hooks@^9.0.0
import React from 'react';
import '@mantine/core/styles.css';
import { MantineProvider } from '@mantine/core';
import { Builder } from '@vojtechportes/react-query-builder';
import { components } from '@vojtechportes/react-query-builder/mantine/v9';

export const MyMantineBuilder = () => {
  return (
    <MantineProvider>
      <Builder
        fields={fields}
        data={data}
        components={components}
        onChange={setData}
      />
    </MantineProvider>
  );
};

Fluent UI example:

npm install @fluentui/react@^8.125.6
import React from 'react';
import { Builder } from '@vojtechportes/react-query-builder';
import { components } from '@vojtechportes/react-query-builder/fluentui/v8';

export const MyFluentUiBuilder = () => {
  return (
    <Builder
      fields={fields}
      data={data}
      components={components}
      onChange={setData}
    />
  );
};

Radix example:

npm install @radix-ui/themes@^1.1.2 @radix-ui/react-icons@^1.3.2

The Radix adapter also uses @radix-ui/react-icons, so install it alongside @radix-ui/themes.

import React from 'react';
import '@radix-ui/themes/styles.css';
import { Theme } from '@radix-ui/themes';
import { Builder } from '@vojtechportes/react-query-builder';
import { components } from '@vojtechportes/react-query-builder/radix/v1';

export const MyRadixBuilder = () => {
  return (
    <Theme>
      <Builder
        fields={fields}
        data={data}
        components={components}
        onChange={setData}
      />
    </Theme>
  );
};

ThemeProvider customizes the built-in default component set. It does not theme the MUI, ANTD, Mantine, Fluent UI, or Radix adapters, which keep their styling in their host UI libraries.

More adapter details:

  • Documentation: Adapters
  • API: Adapters

Text Mode

Builder can optionally switch between the visual query UI and a SQL text editor view.

<Builder
  fields={fields}
  data={data}
  textMode
  defaultMode="text"
  onChange={setData}
/>;

For advanced text editing, the package also exposes @vojtechportes/react-query-builder/monaco. The built-in editor is lightweight and works without extra dependencies, while the Monaco integration is the recommended path when locked query segments must stay protected in text mode. Set allowGroupNegation={false} to remove the group-level NOT toggle and reject NOT (...) groups in text mode, while still allowing operator-level negation such as NOT IN or IS NOT NULL.

  • Documentation: Text Mode
  • API: Builder
  • API: Components

Read-only and Protected Editing

Rules and groups can be fully read-only or partially read-only through readOnly.targets.

  • Rule targets: field, operator, value
  • Group targets: combinator, negation

Targeted read-only controls stay visible but become non-editable. When using the Monaco text editor integration, protected SQL fragments remain locked while users can still edit the unlocked parts of the query.

By default, deleting a group is also blocked when that delete would indirectly remove read-only protected descendants. Set readOnlyProtectsDelete={false} on Builder if you want to disable that subtree delete protection.

  • Documentation: Locking and Read-only
  • Documentation: Text Mode
  • API: Builder
  • API: Data

Query Conversion

The library also provides parser and formatter helpers through subpath exports:

  • @vojtechportes/react-query-builder/formatQuery
  • @vojtechportes/react-query-builder/parseQuery

Supported formats are documented on the website:

Field-to-field comparisons are supported in the builder model and in native query formats that have a direct right-hand-side field-reference form. See the supported-formats documentation for the current list and unsupported cases such as Elasticsearch and RSQL.

  • Documentation: Parsing and Formatting
  • Documentation: Supported Formats
  • API: formatQuery
  • API: parseQuery

Responsive Behavior

The default builder components include a compact responsive layout for medium-width screens.

  • Rule rows reflow to preserve field, operator, action, and value legibility when horizontal space gets tighter.
  • Multi-select values use a summarized closed state to avoid chip overflow.
  • The default responsive behavior applies automatically when you use the built-in components.
  • If you replace layout containers such as components.Rule or components.Group, your custom components are responsible for their own responsive behavior.

Responsive behavior is documented in more detail on the website:

  • Documentation: Components
  • API: Components