Home

Awesome

<p align="center"> <a href="https://ukrbublik.github.io/react-awesome-query-builder/" rel="noopener" target="_blank"><img src="https://raw.githubusercontent.com/ukrbublik/react-awesome-query-builder/19344a32349212cb7c9344a553173c2c4bc57ca6/logo_full_200.png" /></a> </p> <! -- [![travis](https://travis-ci.org/ukrbublik/react-awesome-query-builder.svg?branch=master)](https://travis-ci.com/github/ukrbublik/react-awesome-query-builder) [![Financial Contributors on Open Collective](https://opencollective.com/react-awesome-query-builder/all/badge.svg?label=financial+contributors)](https://opencollective.com/react-awesome-query-builder) -->

npm Smoke codecov antd mui bootstrap fluent demo sandbox TS sandbox JS

User-friendly React component to build queries (filters).

Inspired by jQuery QueryBuilder. Using awesome UI frameworks for widgets: Ant Design, Material-UI, Bootstrap. Now Fluent UI is also supported!

See live demo

Edit in codesandbox Open in GitHub Codespaces

Features

Screenshot

Getting started

From v6 library is divided into packages:

graph LR;
  core((core))-->ui(ui);
  ui-->antd;
  ui-->mui;
  ui-->material;
  ui-->bootstrap;
  ui-->fluent;

ui re-exports from core, other packages re-export from ui. For using this library on frontend you need to install and use only ui (for basic widgets) or one of framework-specific packages (antd / mui / bootstrap / fluent).

For using this library on server-side (Node.js) you need only core. This is useful if you want to pass query value from frontend to backend in JSON format and perform export eg. to SQL on server-side for security reasons.

Example of installation if you use MUI:

npm i @react-awesome-query-builder/mui --save

Note: We use pnpm. If you want to clone this project and run scripts, please install pnpm:

npm install -g pnpm

See basic usage for minimum code example.

See API and config for documentation.

Demo apps

Usage

Minimal JavaScript example with class component

import React, {Component} from 'react';

// >>>
import { Utils as QbUtils, Query, Builder, BasicConfig } from '@react-awesome-query-builder/ui';
import '@react-awesome-query-builder/ui/css/styles.css';
// or import '@react-awesome-query-builder/ui/css/compact_styles.css';
const InitialConfig = BasicConfig;
// <<<

// You need to provide your own config. See below 'Config format'
const config = {
  ...InitialConfig,
  fields: {
    qty: {
      label: 'Qty',
      type: 'number',
      fieldSettings: {
        min: 0,
      },
      valueSources: ['value'],
      preferWidgets: ['number'],
    },
    price: {
      label: 'Price',
      type: 'number',
      valueSources: ['value'],
      fieldSettings: {
        min: 10,
        max: 100,
      },
      preferWidgets: ['slider', 'rangeslider'],
    },
    name: {
      label: 'Name',
      type: 'text',
    },
    color: {
      label: 'Color',
      type: 'select',
      valueSources: ['value'],
      fieldSettings: {
        listValues: [
          { value: 'yellow', title: 'Yellow' },
          { value: 'green', title: 'Green' },
          { value: 'orange', title: 'Orange' }
        ],
      }
    },
    is_promotion: {
      label: 'Promo?',
      type: 'boolean',
      operators: ['equal'],
      valueSources: ['value'],
    },
  }
};

// You can load query value from your backend storage (for saving see `Query.onChange()`)
const queryValue = {"id": QbUtils.uuid(), "type": "group"};


class DemoQueryBuilder extends Component {
  state = {
    tree: QbUtils.loadTree(queryValue),
    config: config
  };
  
  render = () => (
    <div>
      <Query
        {...config} 
        value={this.state.tree}
        onChange={this.onChange}
        renderBuilder={this.renderBuilder}
      />
      {this.renderResult(this.state)}
    </div>
  )

  renderBuilder = (props) => (
    <div className="query-builder-container" style={{padding: '10px'}}>
      <div className="query-builder qb-lite">
        <Builder {...props} />
      </div>
    </div>
  )

  renderResult = ({tree: immutableTree, config}) => (
    <div className="query-builder-result">
      <div>Query string: <pre>{JSON.stringify(QbUtils.queryString(immutableTree, config))}</pre></div>
      <div>MongoDb query: <pre>{JSON.stringify(QbUtils.mongodbFormat(immutableTree, config))}</pre></div>
      <div>SQL where: <pre>{JSON.stringify(QbUtils.sqlFormat(immutableTree, config))}</pre></div>
      <div>JsonLogic: <pre>{JSON.stringify(QbUtils.jsonLogicFormat(immutableTree, config))}</pre></div>
    </div>
  )
  
  onChange = (immutableTree, config) => {
    // Tip: for better performance you can apply `throttle` - see `packages/examples/src/demo`
    this.setState({tree: immutableTree, config: config});

    const jsonTree = QbUtils.getTree(immutableTree);
    console.log(jsonTree);
    // `jsonTree` can be saved to backend, and later loaded to `queryValue`
  }
}
export default DemoQueryBuilder;

Minimal TypeScript example with function component

import React, { useState, useCallback } from "react";

// >>> 
import type { JsonGroup, Config, ImmutableTree, BuilderProps } from '@react-awesome-query-builder/ui';
import { Utils as QbUtils, Query, Builder, BasicConfig } from '@react-awesome-query-builder/ui';
import '@react-awesome-query-builder/ui/css/styles.css';
// or import '@react-awesome-query-builder/ui/css/compact_styles.css';
const InitialConfig = BasicConfig;
// <<<

// You need to provide your own config. See below 'Config format'
const config: Config = {
  ...InitialConfig,
  fields: {
    qty: {
      label: "Qty",
      type: "number",
      fieldSettings: {
        min: 0
      },
      valueSources: ["value"],
      preferWidgets: ["number"]
    },
    price: {
      label: "Price",
      type: "number",
      valueSources: ["value"],
      fieldSettings: {
        min: 10,
        max: 100
      },
      preferWidgets: ["slider", "rangeslider"]
    },
    name: {
      label: 'Name',
      type: 'text',
    },
    color: {
      label: "Color",
      type: "select",
      valueSources: ["value"],
      fieldSettings: {
        listValues: [
          { value: "yellow", title: "Yellow" },
          { value: "green", title: "Green" },
          { value: "orange", title: "Orange" }
        ]
      }
    },
    is_promotion: {
      label: "Promo?",
      type: "boolean",
      operators: ["equal"],
      valueSources: ["value"]
    }
  }
};

// You can load query value from your backend storage (for saving see `Query.onChange()`)
const queryValue: JsonGroup = { id: QbUtils.uuid(), type: "group" };

const DemoQueryBuilder: React.FC = () => {
  const [state, setState] = useState({
    tree: QbUtils.loadTree(queryValue),
    config: config
  });

  const onChange = useCallback((immutableTree: ImmutableTree, config: Config) => {
    // Tip: for better performance you can apply `throttle` - see `packages/examples/src/demo`
    setState(prevState => ({ ...prevState, tree: immutableTree, config: config }));

    const jsonTree = QbUtils.getTree(immutableTree);
    console.log(jsonTree);
    // `jsonTree` can be saved to backend, and later loaded to `queryValue`
  }, []);

  const renderBuilder = useCallback((props: BuilderProps) => (
    <div className="query-builder-container" style={{ padding: "10px" }}>
      <div className="query-builder qb-lite">
        <Builder {...props} />
      </div>
    </div>
  ), []);

  return (
    <div>
      <Query
        {...config}
        value={state.tree}
        onChange={onChange}
        renderBuilder={renderBuilder}
      />
      <div className="query-builder-result">
        <div>
          Query string:{" "}
          <pre>
            {JSON.stringify(QbUtils.queryString(state.tree, state.config))}
          </pre>
        </div>
        <div>
          MongoDb query:{" "}
          <pre>
            {JSON.stringify(QbUtils.mongodbFormat(state.tree, state.config))}
          </pre>
        </div>
        <div>
          SQL where:{" "}
          <pre>
            {JSON.stringify(QbUtils.sqlFormat(state.tree, state.config))}
          </pre>
        </div>
        <div>
          JsonLogic:{" "}
          <pre>
            {JSON.stringify(QbUtils.jsonLogicFormat(state.tree, state.config))}
          </pre>
        </div>
      </div>
    </div>
  );
};
export default DemoQueryBuilder;

API

<Query />

Props:

Notes:

<Builder />

Render this component only inside Query.renderBuilder() like in example above:

  renderBuilder = (props) => (
    <div className="query-builder-container">
      <div className="query-builder qb-lite">
          <Builder {...props} />
      </div>
    </div>
  )

Wrapping <Builder /> in div.query-builder is necessary.
Optionally you can add class .qb-lite to it for showing action buttons (like delete rule/group, add, etc.) only on hover, which will look cleaner.
Wrapping in div.query-builder-container is necessary if you put query builder inside scrollable block.

Utils

Save/load tree

getTree

Utils.getTree (immutableValue, light = true, children1AsArray = true) -> Object
Convert query value from internal Immutable format to JS object.
You can use it to save value on backend in onChange callback of <Query>.
Tip: Use light = false in case if you want to store query value in your state in JS format and pass it as value of <Query> after applying loadTree() (which is not recommended because of double conversion). See issue #190

loadTree

Utils.loadTree (jsValue) -> Immutable
Convert query value from JS format to internal Immutable format.
You can use it to load saved value from backend and pass as value prop to <Query>.

Validation utils

isValidTree

Utils.isValidTree (immutableValue, config) -> Boolean
If showErrorMessage in config.settings is true, use this method to check if query has validation errors (presented in UI with red text color under the rule).
Note that incomplete rules or empty groups are not counted as validation errors for this function.
If showErrorMessage is false, this function will always return true.

validateTree

Utils.validateTree (immutableValue, config, options?) -> Array
Validates immutable query value to check it corresponds to the config and has no parts that are invalid or incomplete.
Returns array of errors grouped by item in tree.
Each array element is { itemStr, itemPositionStr, errors, path } (see type ValidationItemErrors).

To present item for user you can use itemStr (string representation of rule eg. Number > 55) and itemPositionStr (eg. Rule #4 (index path: 1, 2)).
Also you can use path to get raw item data with Utils.TreeUtils.getItemByPath(tree, path) (advanced).

errors is an array of objects { str, key, args, side, delta } (see type ValidationError).
str is an error message translated with i18next.t(key, args) (namespace is raqbvalidation).
side can be one of rhs or lhs.
delta can be 0 or 1 for between operator.

You can override/extend translations with:
Utils.i18n.addResources("en", "raqbvalidation", { ...yourTranslations })
See default validation translations.
See i18n for validation.

sanitizeTree

Utils.sanitizeTree (immutableValue, config, options?) -> { fixedTree, fixedErrors, nonFixedErrors }
Validates and modifies immutable query value to ensure it corresponds to the config and has no parts that are invalid or incomplete.
Invalid rules (eg. if field is not found in config) will always be deleted.
Invalid values (eg. value > max or < min, value not passing validateValue() in field config) will be either:

options is an object with keys:

Returns an object with properties:

The format of errors in fixedErrors, nonFixedErrors, allErrors is the same as returned from validateTree.
But error objects per item alongside with str, key, args, side have also the following keys:
fixed (boolean), fixedFrom, fixedTo.

Export utils

queryString

Utils.Export.queryString (immutableValue, config, isForDisplay = false) -> String
Convert query value to custom string representation.
isForDisplay = true can be used to make string more "human readable".

mongodbFormat

Utils.Export.mongodbFormat (immutableValue, config) -> Object
Convert query value to MongoDb query object.

sqlFormat

Utils.Export.sqlFormat (immutableValue, config) -> String
Convert query value to SQL where string.

spelFormat

Utils.Export.spelFormat (immutableValue, config) -> String
Convert query value to Spring Expression Language (SpEL).

elasticSearchFormat

Utils.Export.elasticSearchFormat (immutableValue, config) -> Object
Convert query value to ElasticSearch query object.

jsonLogicFormat

Utils.Export.jsonLogicFormat (immutableValue, config) -> {logic, data, errors}
Convert query value to JsonLogic format.
If there are no errors, logic will be rule object and data will contain all used fields with null values ("template" data).

Import utils

loadFromJsonLogic

Utils.Import.loadFromJsonLogic (jsonLogicObject, config) -> Immutable
Convert query value from JsonLogic format to internal Immutable format.

_loadFromJsonLogic

Utils.Import._loadFromJsonLogic (jsonLogicObject, config) -> [Immutable, errors]

loadFromSpel

Utils.Import.loadFromSpel (string, config) -> [Immutable, errors]
Convert query value from Spring Expression Language (SpEL) format to internal Immutable format.

Save/load config from server

compressConfig

Utils.ConfigUtils.compressConfig (config, baseConfig) -> ZipConfig
Returns compressed config that can be serialized to JSON and saved on server.
ZipConfig is a special format that contains only changes agains baseConfig.
baseConfig is a config you used as a base for constructing config, like InitialConfig in examples above.
It depends on UI framework you choose - eg. if you use @react-awesome-query-builder/mui, please provide MuiConfig to baseConfig.

decompressConfig

Utils.ConfigUtils.decompressConfig (zipConfig, baseConfig, ctx?) -> Config
Converts zipConfig (compressed config you receive from server) to a full config that can be passed to <Query />.
baseConfig is a config to be used as a base for constructing your config, like InitialConfig in examples above.
ctx is optional and can contain your custom functions and custom React components used in your config.
If ctx is provided in 3rd argument, it will inject it to result config, otherwise will copy from basic config in 2nd argument.
See SSR for more info.
Note that you should set config.settings.useConfigCompress = true in order for this function to work.

Config format

This library uses config-driven aproach. Config defines what value types, operators are supported, how they are rendered, imported, exported. At minimum, you need to provide your own set of fields as in basic usage.
See CONFIG for full documentation.

Validation

Useful config settings to manage global validation behaviour:

Useful field config settings to manage validation behaviour per field:

Use Utils.sanitizeTree() to perform validation on tree value and return validation errors and fixed tree value. See the list of validation utils.

See i18n for validation.

i18n

:construction:

This library uses i18next for translations.

Validation translations

Namespace: raqbvalidation. Default translations resource

Example of overriding translations for validation error messages:

Utils.i18n.addResources("en", "raqbvalidation", {
  "INCOMPLETE_LHS": "Incomplete left-hand side",
  "INCOMPLETE_RHS": "Incomplete right-hand side",
});

Example of using custom translations in validateValue in config:

Utils.i18n.addResources("en", "mynamespace", {
  "INVALID_SLIDER_VALUE": "Invalid slider value {{val}}",
});

const config = {
  ...MuiConfig,
  fields: {
    slider: {
      type: "number",
      preferWidgets: ["slider"],
      fieldSettings: {
        validateValue: (val) => {
          return (val < 50 ? null : {
            error: {
              // use `key` and `args` for i18next.t()
              // `key` should have your namespace prefixed with ":"
              key: "mynamespace:INVALID_SLIDER_VALUE",
              args: { val }
            },
            fixedValue: 49
          });
        },
      }
    }
  }
};

// then use <Query {...config} />

See example.

Ternary mode

First you need to configure caseValueField in config.settings. Example to use tags as case values:

const config: Config = {
  ...InitialConfig,
  fields,
  settings: {
    ...InitialConfig.settings,
    caseValueField: {
      type: "select",
      valueSources: ["value"],
      fieldSettings: {
        listValues: [
          { value: "tag1", title: "Tag #1" },
          { value: "tag2", title: "Tag #2" },
        ],
      },
      mainWidgetProps: {
        valueLabel: "Then",
        valuePlaceholder: "Then",
      },
    },
    canRegroupCases: true,
    maxNumberOfCases: 10,
  }
};

You can use other type/widget (including your custom one) to render case values.
Also you can use function (action) by specifying valueSources: ["func"] in caseValueField. You have to add funcs to the config (with returnType equals type in case value field).

Load empty tree in ternary mode:

import { Utils as QbUtils, JsonSwitchGroup } from '@react-awesome-query-builder/ui';
const emptyJson: JsonSwitchGroup = { id: QbUtils.uuid(), type: "switch_group", };
const tree = QbUtils.loadTree(emptyJson);

See example

Screenshot

SSR

You can save and load config from server with help of utils:

You need these utils because you can't just send config as-is to server, as it contains functions that can't be serialized to JSON.
Note that you need to set config.settings.useConfigCompress = true to enable this feature.

To put it simple:

See sandbox_next demo app that demonstrates server-side features.

ctx

Config context is an obligatory part of config starting from version 6.3.0
It is a collection of functions and React components to be used in other parts of config by reference to ctx rather than by reference to imported modules.
The purpose of ctx is to isolate non-serializable part of config.
See config.ctx.

Versions

Version 5 is backward-compatible with 2-4. From version 6 library is divided into packages. It's recommended to update your version to 6.x. You just need to change your imports, see Migration to 6.0.0

Supported versions

VersionSupported
6.x:white_check_mark:
5.x:heavy_check_mark:
4.x:warning:
3.x:x:
2.x:x:
1.x:x:
0.x:x:

Changelog

See CHANGELOG

Migration to 6.5.0

Validation API has been changed:

Migration to 6.4.0

If you want to enable functions in LHS, please add to config.settings:

fieldSources: ["field", "func"],

Migration to 6.3.0

Now config has new ctx property. Make sure you add it to your config.

Typically you just need to copy it from basic config. So if you create config like this, you don't need to make any changes:

import { MuiConfig } from "@react-awesome-query-builder/mui";
const config = {
  ...MuiConfig,
  fields: {
    // your fields
  },
};

But if you create config without destructuring of basic config, please add ctx:

import { MuiConfig } from "@react-awesome-query-builder/mui";

const config = {
  ctx: MuiConfig.ctx, // needs to be added for 6.3.0+
  conjunctions,
  operators,
  widgets,
  types,
  settings,
  fields,
  funcs
};
export default config;

Migration to 6.2.0

If you use treeselect or treemultiselect type (for AntDesign), please rename listValues to treeValues

Migration to 6.0.0

From version 6 library is divided into packages. Please remove package react-awesome-query-builder and install one of:

Library code is backward-compatible with version 2-5. You just need to change your imports.

- import { Utils, Export, Import, BasicFuncs } from 'react-awesome-query-builder';
+ import { Utils, Export, Import, BasicFuncs } from '@react-awesome-query-builder/ui';

- import { Query, Builder, BasicConfig, Widgets, Operators } from 'react-awesome-query-builder';
+ import { Query, Builder, BasicConfig, VanillaWidgets, CustomOperators } from '@react-awesome-query-builder/ui';

- import AntdConfig from 'react-awesome-query-builder/lib/config/antd';
+ import {AntdConfig} from '@react-awesome-query-builder/antd';
- import MuiConfig from 'react-awesome-query-builder/lib/config/mui';
+ import {MuiConfig} from '@react-awesome-query-builder/mui';
- import MaterialConfig from 'react-awesome-query-builder/lib/config/material';
+ import {MaterialConfig} from '@react-awesome-query-builder/material';
- import BootstrapConfig from 'react-awesome-query-builder/lib/config/bootstrap';
+ import {BootstrapConfig} from '@react-awesome-query-builder/bootstrap';

- import 'react-awesome-query-builder/lib/css/styles.css';
+ import '@react-awesome-query-builder/ui/css/styles.css';
- import 'react-awesome-query-builder/lib/css/compact_styles.css';
+ import '@react-awesome-query-builder/ui/css/compact_styles.css'; // instead of styles.css for more compact look

Note that you should import all types and values from a single package. For example, @react-awesome-query-builder/antd if you use AntDesign - it inherits core and ui:

import {Utils, Query, Builder, AntdConfig} from '@react-awesome-query-builder/antd';

You don't need to install and import ui and core packages in this case, just use antd.
Same for styles - please import from antd package:

import '@react-awesome-query-builder/antd/css/styles.css';

instead of

import '@react-awesome-query-builder/ui/css/styles.css';

If you use vanilla widgets, please install, import and use only @react-awesome-query-builder/ui (it inherits core).

One more thing, if you use Bootstrap widgets, now you need to explicitly import CSS:

import "bootstrap/dist/css/bootstrap.min.css";

Migration to 5.2.0

Breaking change: children1 is now an indexed array (instead of object) in result of Utils.getTree() to preserve items order.
Before:

children1: {
  '<id1>': { type: 'rule', properties: ... },
  '<id2>': { type: 'rule', properties: ... }
}

After:

children1: [
  { id: '<id1>', type: 'rule', properties: ... },
  { id: '<id2>', type: 'rule', properties: ... },
]

Utils.loadTree() is backward comatible with children1 being array or object.
But if you rely on previous format (maybe do post-processing of getTree() result), please use Utils.getTree(tree, true, false) - it will behave same as before this change.

Another breaking change: removeIncompleteRulesOnLoad and removeEmptyGroupsOnLoad now default to true, set them to false in your settings to preserve the behaviour before 5.2.0.

Migration to 4.9.0

Version 4.9.0 has a breaking change for operators is_empty and is_not_empty.
Now these operators can be used for text type only (for other types they will be auto converted to is_null/is_not_null during loading of query value created with previous versions).
Changed meaning of is_empty - now it's just strict comparing with empty string.
Before change the meaning was similar to is_null.
If you used is_empty for text types with intention of comparing with null, please replace is_empty -> is_null, is_not_empty -> is_not_null in saved query values.
If you used JsonLogic for saving, you need to replace {"!": {"var": "your_field"}} -> {"==": [{"var": "your_field"}, null]} and {"!!": {"var": "your_field"}} -> {"!=": [{"var": "your_field"}, null]}.

Migration from v1 to v2

From v2.0 of this lib AntDesign is now optional (peer) dependency, so you need to explicitly include antd (4.x) in package.json of your project if you want to use AntDesign UI.
Please import AntdConfig from react-awesome-query-builder/lib/config/antd and use it as base for your config (see below in usage).
Alternatively you can use BasicConfig for simple vanilla UI, which is by default.
Support of other UI frameworks (like Bootstrap) are planned for future, see Other UI frameworks.

Contributing

Code Contributing

This project exists thanks to all the people who contribute. [Contribute]. <a href="https://github.com/ukrbublik/react-awesome-query-builder/graphs/contributors"><img src="https://opencollective.com/react-awesome-query-builder/contributors.svg?width=890&button=false" /></a>

Financial Contributing

Become a financial contributor and help us sustain our community. [Contribute]

If you mention in an GitHub issue that you are a sponsor, we will prioritize helping you.

As a sponsor you can ask to implement a feature that is not in a todo list or motivate for faster implementation.

Individuals

<a href="https://opencollective.com/react-awesome-query-builder"><img src="https://opencollective.com/react-awesome-query-builder/individuals.svg?width=890"></a>

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

<a href="https://opencollective.com/react-awesome-query-builder/organization/0/website"><img src="https://opencollective.com/react-awesome-query-builder/organization/0/avatar.svg"></a> <a href="https://opencollective.com/react-awesome-query-builder/organization/1/website"><img src="https://opencollective.com/react-awesome-query-builder/organization/1/avatar.svg"></a> <a href="https://opencollective.com/react-awesome-query-builder/organization/2/website"><img src="https://opencollective.com/react-awesome-query-builder/organization/2/avatar.svg"></a> <a href="https://opencollective.com/react-awesome-query-builder/organization/3/website"><img src="https://opencollective.com/react-awesome-query-builder/organization/3/avatar.svg"></a> <a href="https://opencollective.com/react-awesome-query-builder/organization/4/website"><img src="https://opencollective.com/react-awesome-query-builder/organization/4/avatar.svg"></a> <a href="https://opencollective.com/react-awesome-query-builder/organization/5/website"><img src="https://opencollective.com/react-awesome-query-builder/organization/5/avatar.svg"></a> <a href="https://opencollective.com/react-awesome-query-builder/organization/6/website"><img src="https://opencollective.com/react-awesome-query-builder/organization/6/avatar.svg"></a> <a href="https://opencollective.com/react-awesome-query-builder/organization/7/website"><img src="https://opencollective.com/react-awesome-query-builder/organization/7/avatar.svg"></a> <a href="https://opencollective.com/react-awesome-query-builder/organization/8/website"><img src="https://opencollective.com/react-awesome-query-builder/organization/8/avatar.svg"></a> <a href="https://opencollective.com/react-awesome-query-builder/organization/9/website"><img src="https://opencollective.com/react-awesome-query-builder/organization/9/avatar.svg"></a>

License

MIT. See also LICENSE.txt

Forked from https://github.com/fubhy/react-query-builder