Skip to main content

Options

react-querybuilder@8.4.1
Dependencies
npm install react-querybuilder @react-querybuilder/bootstrap bootstrap bootstrap-icons
App.tsx
import { useState } from 'react';
import type { RuleGroupType } from 'react-querybuilder';
import { QueryBuilder } from 'react-querybuilder';
import { fields } from './fields';
import 'react-querybuilder/dist/query-builder.css';
import './styles.css';
import { QueryBuilderBootstrap } from '@react-querybuilder/bootstrap';
import 'bootstrap-icons/font/bootstrap-icons.scss';
import 'bootstrap/scss/bootstrap.scss';

const initialQuery: RuleGroupType = { combinator: 'and', rules: [] };

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

return (
<QueryBuilderBootstrap>
<QueryBuilder fields={fields} query={query} onQueryChange={setQuery} />
</QueryBuilderBootstrap>
);
};
Styles
styles.css
.svg-font-color svg > path {
fill: var(--ifm-font-color-base);
}

.donut-hole {
overflow-y: unset !important;
}

.queryBuilder {
min-width: 420px;
}

/* Styles for when "Use validation" option is selected */
.validateQuery .queryBuilder {
& .ruleGroup.queryBuilder-invalid {
/* Color invalid groups */
background-color: color-mix(in srgb, rebeccapurple, transparent 60%);

& .ruleGroup-addRule {
/* Bold the "+ Rule" button if the group has no child rules or groups */
font-weight: bold !important;
}

& > .ruleGroup-header::after {
/* Message to user about empty groups */
content: "Empty groups are considered invalid. Avoid them by using addRuleToNewGroups.";
color: white;
}
}

& .rule.queryBuilder-invalid .rule-value {
/* Purple background for empty text fields */
background-color: color-mix(in srgb, rebeccapurple, transparent 60%);

&::placeholder {
color: color-mix(in srgb, rebeccapurple, black 30%);
}
}
}

/* Styles for when "Use validation" option is selected (dark theme variations) */
html[data-theme="dark"]
.validateQuery
.queryBuilder
.rule.queryBuilder-invalid
.rule-value::placeholder {
color: color-mix(in srgb, rebeccapurple, white 30%);
}

/* Styles for when "Justified layout" option is selected */
.justifiedLayout {
& .queryBuilder {
/* Push the clone, lock, and remove buttons to the right edge */
& .ruleGroup-addGroup {
& + button.ruleGroup-cloneGroup,
& + button.ruleGroup-lock,
& + button.ruleGroup-remove {
margin-left: auto !important;
}
}
& .rule-operators,
& .rule-value,
& .control,
& .chakra-select__wrapper,
& .mantine-InputWrapper-root + input[type="hidden"] {
& + button.rule-cloneRule,
& + button.rule-lock,
& + button.rule-remove {
margin-left: auto !important;
}
}
}
}
Other files
fields.ts
import type { Field, RuleType } from 'react-querybuilder';
import { defaultOperators, toFullOption } from 'react-querybuilder';
import { musicalInstruments } from './musicalInstruments';

export const validator = (r: RuleType) => !!r.value;

export const fields = (
[
{
name: 'firstName',
label: 'First Name',
placeholder: 'Enter first name',
validator,
},
{
name: 'lastName',
label: 'Last Name',
placeholder: 'Enter last name',
defaultOperator: 'beginsWith',
validator,
},
{ name: 'age', label: 'Age', inputType: 'number', validator },
{
name: 'isMusician',
label: 'Is a musician',
valueEditorType: 'checkbox',
operators: defaultOperators.filter((op) => op.name === '='),
defaultValue: false,
},
{
name: 'instrument',
label: 'Primary instrument',
valueEditorType: 'select',
values: musicalInstruments,
// This must be commented out to properly demonstrate `autoSelectValue={false}`
// defaultValue: 'Cowbell',
operators: defaultOperators.filter((op) => op.name === '='),
},
{
name: 'alsoPlays',
label: 'Also plays',
valueEditorType: 'multiselect',
values: musicalInstruments,
defaultValue: 'More cowbell',
operators: defaultOperators.filter((op) => op.name === 'in'),
},
{
name: 'gender',
label: 'Gender',
operators: defaultOperators.filter((op) => op.name === '='),
valueEditorType: 'radio',
values: [
{ name: 'M', label: 'Male' },
{ name: 'F', label: 'Female' },
{ name: 'O', label: 'Other' },
],
},
{ name: 'height', label: 'Height', validator },
{ name: 'job', label: 'Job', validator },
{ name: 'description', label: 'Description', valueEditorType: 'textarea' },
{
name: 'birthdate',
label: 'Birth Date',
inputType: 'date',
datatype: 'date',
},
{
name: 'datetime',
label: 'Show Time',
inputType: 'datetime-local',
datatype: 'timestamp with time zone',
},
{ name: 'alarm', label: 'Daily Alarm', inputType: 'time' },
{
name: 'groupedField1',
label: 'Grouped Field 1',
comparator: 'groupNumber',
groupNumber: 'group1',
valueSources: ['field', 'value'],
},
{
name: 'groupedField2',
label: 'Grouped Field 2',
comparator: 'groupNumber',
groupNumber: 'group1',
valueSources: ['field', 'value'],
},
{
name: 'groupedField3',
label: 'Grouped Field 3',
comparator: 'groupNumber',
groupNumber: 'group1',
valueSources: ['field', 'value'],
},
{
name: 'groupedField4',
label: 'Grouped Field 4',
comparator: 'groupNumber',
groupNumber: 'group1',
valueSources: ['field', 'value'],
},
] satisfies Field[]
).map((o) => toFullOption(o));
musicalInstruments.ts
// Adapted from https://en.wikipedia.org/wiki/List_of_musical_instruments
import type { OptionGroup } from 'react-querybuilder';

export const musicalInstruments: OptionGroup[] = [
{
label: 'Percussion instruments',
instruments: [
'Clapstick',
'Cowbell',
'Cymbal',
'Gong',
'Maraca',
'Marimba',
'More cowbell',
'Spoon',
'Steelpan',
'Tambourine',
'Triangle',
'Vibraphone',
'Washboard',
'Wood block',
'Wooden fish',
'Xylophone',
],
},
{
label: 'Membranophones',
instruments: [
'Barrel drum',
'Bass drum',
'Bongo drums',
'Conga',
'Drum',
'Drum kit',
"Jew's harp",
'Octaban',
'Samphor',
'Snare drum',
'Timpani',
'Tom-tom',
],
},
{
label: 'Wind instruments',
instruments: [
'Accordion',
'Air horn',
'Bagpipe',
'Baritone horn',
'Bassoon',
'Bazooka',
'Beatboxing',
'Blown bottle',
'Bugle',
'Clarinet',
'Conch',
'Cornet',
'Didgeridoo',
'Double bell euphonium',
'Doulophone',
'English horn',
'Euphonium',
'Flugelhorn',
'Flute',
'French horn',
'Harmonica',
'Irish flute',
'Jug',
'Kazoo',
'Melodeon',
'Mezzo-soprano',
'Oboe',
'Ocarina',
'Pan flute',
'Piccolo',
'Pipe organ',
'Recorder',
'Saxophone',
'Slide whistle',
'Sousaphone',
'Trombone',
'Trumpet',
'Tuba',
'Whistle',
],
},
{
label: 'Stringed instruments',
instruments: [
'Aeolian harp',
'Bandolin',
'Banjo ukulele',
'Cello',
'Chapman stick',
'Clavichord',
'Clavinet',
'Double bass',
'Dulcimer',
'Fiddle',
'Guitar',
'Hammered dulcimer',
'Harp',
'Harpsichord',
'Lute',
'Lyre',
'Maguhu',
'Mandola',
'Mandolin',
'Octobass',
'Piano',
'Sitar',
'Ukulele',
'Viol',
'Violin',
'Washtub bass',
],
},
{
label: 'Electronic instruments',
instruments: [
'AlphaSphere',
'Audiocubes',
'Bass pedals',
'Continuum Fingerboard',
'Croix Sonore',
"Denis d'or",
'Dubreq stylophone',
'Drum machine',
'Eigenharp',
'Electric guitar',
'Electronic keyboard',
'Electronic organ',
'EWI',
'Fingerboard synthesizer',
'Hammond organ',
'Keyboard',
'Keytar',
'Kraakdoos',
'Laser harp',
'Mellotron',
'MIDI keyboard',
'Omnichord',
'Ondes Martenot',
'Otamatone',
'Sampler',
'Seaboard music instrument',
'Skoog',
'Synclavier',
'Synthesizer',
'Teleharmonium',
'Tenori-on',
'Theremin',
'trautonium',
'Turntablism',
'Turntable',
],
},
].map(({ label, instruments }) => ({
label,
options: instruments.map((s) => ({ name: s, label: s })),
}));