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
Features
- Highly configurable
- Fields can be of type:
- simple (string, number, bool, date/time/datetime, list)
- structs (will be displayed in selectbox as tree)
- custom type (dev should add its own widget component in config for this)
- Comparison operators can be:
- binary (== != < > ..)
- unary (is empty, is null)
- 'between' (for numbers, dates, times)
- complex operators like 'proximity'
- Values of fields can be compared with:
- values
- another fields (of same type)
- function (arguments also can be values/fields/funcs)
- Reordering (drag-n-drop) support for rules and groups of rules
- Themes:
- Ant Design
- Material-UI
- Bootstrap
- Fluent UI
- vanilla (Using another UI framework and custom widgets is possible, see below)
- Export to MongoDb, SQL, JsonLogic, SpEL, ElasticSearch or your custom format
- Import from JsonLogic, SpEL
- TypeScript support (see types and demo in TS)
- Query value and config can be saved/loaded from server
Getting started
From v6 library is divided into packages:
@react-awesome-query-builder/core
- has core functionality to import/export/store query, provides utils@react-awesome-query-builder/ui
- has core React components like<Query>
<Builder>
and CSS, provides config with basic (vanilla) widgets@react-awesome-query-builder/antd
- provides config with Ant Design widgets@react-awesome-query-builder/mui
- provides config with MUI widgets@react-awesome-query-builder/material
- provides config with Material-UI v4 widgets (deprecated)@react-awesome-query-builder/bootstrap
- provides config with Bootstrap widgets@react-awesome-query-builder/fluent
- provides config with Fluent UI widgets
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
pnpm start
- demo app with hot reload of demo code and local library code, uses TS, uses complex config to demonstrate anvanced usage, uses all supported UI frameworks.pnpm sandbox-ts
- simple demo app, built with Vite, uses TS, uses MUI widgets.pnpm sandbox-js
- simplest demo app, built with Vite, not uses TS, uses vanilla widgets.pnpm sandbox-next
- advanced demo app with server side, built with Next.js, uses TS, uses MUI widgets, has API to save/load query value and query config from storage.
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.checkTree(QbUtils.loadTree(queryValue), config),
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 `examples/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.checkTree(QbUtils.loadTree(queryValue), config),
config: config
});
const onChange = useCallback((immutableTree: ImmutableTree, config: Config) => {
// Tip: for better performance you can apply `throttle` - see `examples/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:
{...config}
- destructuredCONFIG
value
- query value in internal Immutable formatonChange
- callback when query value changed. Params:value
(in Immutable format),config
,actionMeta
(details about action which led to the change, seeActionMeta
inindex.d.ts
).renderBuilder
- function to render query builder itself. Takes 1 paramprops
you need to pass into<Builder {...props} />
.
Notes:
- Please apply
useCallback
foronChange
andrenderBuilder
for performance reason - If you put query builder component inside Material-UI's
<Dialog />
or<Popover />
, please:- use prop
disableEnforceFocus={true}
for dialog or popver - set css
.MuiPopover-root, .MuiDialog-root { z-index: 900 !important; }
(or 1000 for AntDesign v3)
- use prop
- If you put query builder component inside Fluent-UI's
<Panel />
, please:- set css
.ms-Layer.ms-Layer--fixed.root-119 { z-index: 900 !important; }
- set css
props
arg inrenderBuilder
haveactions
anddispatch
you can use to run actions programmatically (for list of actions seeActions
inindex.d.ts
).
<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:
Convert query value from internal Immutable format to JS object. You can use it to save value on backend in getTree (immutableValue, light = true, children1AsArray = true) -> Object
onChange
callback of<Query>
.
Tip: Uselight = false
in case if you want to store query value in your state in JS format and pass it asvalue
of<Query>
after applyingloadTree()
(which is not recommended because of double conversion). See issue #190Convert query value from JS format to internal Immutable format. You can use it to load saved value from backend and pass as loadTree (jsValue) -> Immutable
value
prop to<Query>
(don't forget to also applycheckTree()
).Validate query value corresponding to config. Invalid parts of query (eg. if field was removed from config) will be always deleted. Invalid values (values not passing checkTree (immutableValue, config) -> Immutable
validateValue
in config, bad ranges) will be deleted ifshowErrorMessage
is false OR marked with errors ifshowErrorMessage
is true.If isValidTree (immutableValue) -> Boolean
showErrorMessage
in config.settings is true, use this method to check is query has bad values. - Export:
Convert query value to custom string representation. queryString (immutableValue, config, isForDisplay = false) -> String
isForDisplay
= true can be used to make string more "human readable".Convert query value to MongoDb query object. mongodbFormat (immutableValue, config) -> Object
Convert query value to SQL where string. sqlFormat (immutableValue, config) -> String
Convert query value to spelFormat (immutableValue, config) -> StringSpring Expression Language (SpEL).
Convert query value to ElasticSearch query object. elasticSearchFormat (immutableValue, config) -> Object
Convert query value to jsonLogicFormat (immutableValue, config) -> {logic, data, errors}JsonLogic format. If there are no
errors
,logic
will be rule object anddata
will contain all used fields with null values ("template" data). - Import:
Convert query value from loadFromJsonLogic (jsonLogicObject, config) -> ImmutableJsonLogic format to internal Immutable format.
_loadFromJsonLogic (jsonLogicObject, config) -> [Immutable, errors]
Convert query value from loadFromSpel (string, config) -> [Immutable, errors]Spring Expression Language (SpEL) format to internal Immutable format.
- Save/load config from server:
Returns compressed config that can be serialized to JSON and saved on server. compressConfig(config, baseConfig) -> ZipConfig
ZipConfig
is a special format that contains only changes againsbaseConfig
.
baseConfig
is a config you used as a base for constructingconfig
, likeInitialConfig
in examples above.
It depends on UI framework you choose - eg. if you use@react-awesome-query-builder/mui
, please provideMuiConfig
tobaseConfig
.Converts decompressConfig(zipConfig, baseConfig, ctx?) -> Config
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, likeInitialConfig
in examples above.
ctx
is optional and can contain your custom functions and custom React components used in your config.
Ifctx
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 setconfig.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.
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:
ZipConfig
type is a JSON that contains only changes against basic config (differences). At minimum it contains yourfields
. It does not containctx
.Utils.decompressConfig()
will mergeZipConfig
to basic config (and addctx
if passed).
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
Version | Supported |
---|---|
6.x | |
5.x | |
4.x | |
3.x | |
2.x | |
1.x | |
0.x |
Changelog
See CHANGELOG
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:
@react-awesome-query-builder/ui
@react-awesome-query-builder/antd
@react-awesome-query-builder/bootstrap
@react-awesome-query-builder/mui
@react-awesome-query-builder/material
(deprecated)@react-awesome-query-builder/fluent
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].
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
Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]
License
MIT. See also LICENSE.txt
Forked from https://github.com/fubhy/react-query-builder