
Syncfusion React Query Builder
- 439 installs
- 3 repo stars
- Updated July 28, 2026
- syncfusion/react-ui-components-skills
syncfusion-react-query-builder is an agent skill that guides implementation of the Syncfusion React Query Builder component with rule management, SQL and Mongo conversion, and DataGrid filtering for developers building a
About
syncfusion-react-query-builder is a Syncfusion agent skill (version 33.1.44) for building rule-based filter UIs with the @syncfusion/ej2-react-querybuilder package. The skill documents column schema via ColumnsModel, nested AND/OR rule groups, 16+ built-in operators across string, number, date, and boolean types, and conversion methods including getSqlFromRules, getMongoQuery, and parameterized SQL import/export. Seven reference guides cover getting started, data binding with DataManager, templates, advanced features like drag-and-drop and enablePersistence, and a full API cheat sheet with maxGroupCount defaulting to 5. Developers reach for this skill when adding advanced search panels, dashboard filters tied to Syncfusion Grid or Charts, or saved query templates that round-trip SQL. Install the broader skill pack with npx skills add syncfusion/react-ui-components-skills -y, then invoke when prompts mention QueryBuilderComponent, RuleModel, or dynamic filter builders in React TypeScript apps.
- syncfusion-react-query-builder
Syncfusion React Query Builder by the numbers
- 439 all-time installs (skills.sh)
- +52 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #997 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/syncfusion/react-ui-components-skills --skill syncfusion-react-query-builderAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 439 |
|---|---|
| repo stars | ★ 3 |
| Last updated | July 28, 2026 |
| Repository | syncfusion/react-ui-components-skills ↗ |
How do you build a React query builder filter UI?
Use syncfusion-react-query-developer for development tasks
Who is it for?
React developers already using Syncfusion EJ2 who need production filter builders with SQL, Mongo, and DataManager integration.
Skip if: Teams without a Syncfusion license needing a free-form filter DSL, or backend-only SQL authoring with no React UI component.
When should I use this skill?
The user asks to add Syncfusion Query Builder, convert filter rules to SQL or Mongo, or integrate rule-based filtering with EJ2 Grid or Charts.
What you get
QueryBuilderComponent with typed columns, nested RuleModel groups, exported SQL or Mongo queries, and optional DataGrid-filtered result sets.
- QueryBuilderComponent implementation
- Exported SQL or Mongo filter strings
- Grid-integrated filtered data views
By the numbers
- Skill metadata version 33.1.44 aligned to Syncfusion React Query Builder
- Documents 16+ built-in filter operators across data types
- Ships 7 reference guides plus API cheat sheet for QueryBuilderComponent
Files
Implementing Syncfusion React Query Builder
A comprehensive guide for implementing and customizing the Syncfusion React Query Builder component. The Query Builder is a powerful UI component for creating and managing complex filter conditions, with support for rule-based queries, multiple data types, SQL generation, and extensive customization options.
Component Overview
The Query Builder component provides a graphical interface for creating and editing complex filter rules. It outputs structured JSON that can be converted to SQL, Mongo queries, or custom predicates for filtering data. Key capabilities include:
- Rule Management: Create, edit, delete, and nest rules and groups
- Multiple Data Types: Support for string, number, date, boolean, and custom types
- Operator Support: 16+ built-in operators (equal, contains, between, in, etc.)
- Query Conversion: Convert to SQL, Mongo, or parameterized queries
- Customization: Custom templates, themes, and styling
- Accessibility: WCAG compliant with keyboard navigation and screen reader support
- Advanced Features: Drag-and-drop, state persistence, cloning, locking
Documentation and Navigation Guide
Getting Started
📄 Read: references/getting-started.md
- Installation and package setup
- Basic component initialization
- CSS imports and themes
- Creating your first Query Builder
- Column definition basics
- Running the application
Columns and Operators
📄 Read: references/columns-and-operators.md
- Defining column schema with ColumnsModel
- Auto-generating columns from data sources
- Configuring labels and field mappings
- Supported operators by data type
- Setting step and format properties
- Column validation configuration
Data Binding
📄 Read: references/data-binding.md
- Binding local data arrays
- Remote data with DataManager
- ODataV4Adaptor integration
- Dynamic data updates
- Using DataManager with Query Builder
- Handling data source changes
Rules and Filtering
📄 Read: references/rules-and-filtering.md
- Understanding rule structure (RuleModel)
- Creating rules programmatically with addRules
- Creating groups with addGroups
- Deleting rules and groups
- Managing nested rule hierarchies
- Drag-and-drop rule management
- Show buttons configuration
Query Conversion
📄 Read: references/query-conversion.md
- Converting rules to SQL with getSqlFromRules
- Generating Mongo queries with getMongoQuery
- Creating parameterized SQL queries
- Named parameter SQL generation
- Converting predicates for DataManager
- Importing rules from SQL queries
- Handling localization in SQL conversion
Templates and Customization
📄 Read: references/templates-and-customization.md
- Creating custom header templates
- Custom component injection into templates
- Styling with CSS classes and cssClass property
- Theme Studio integration
- Custom operator definitions
- Handling actionBegin events for customization
Advanced Features
📄 Read: references/advanced-features.md
- Display modes (Horizontal and Vertical layouts)
- Cloning rules and groups with cloneRule/cloneGroup
- Locking rules and groups for read-only access
- Separate connectors for visual distinction
- Restricting group operations
- RTL (Right-to-Left) support
- State persistence with enablePersistence
- Sort direction configuration
- Summary view display
- Accessibility and keyboard navigation
API Reference
📄 Read: references/api-reference.md
- Complete properties list with types and defaults
- All methods with parameters and return types
- Event handlers and event arguments
- Model interfaces (RuleModel, ColumnsModel, ShowButtonsModel)
- Return type definitions and data structures
- Property usage patterns
Quick Start Example
import { ColumnsModel, QueryBuilderComponent, RuleModel } from '@syncfusion/ej2-react-querybuilder';
import React from 'react';
function App() {
const columnData: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number' },
{ field: 'FirstName', label: 'First Name', type: 'string' },
{ field: 'Title', label: 'Title', type: 'string' },
{ field: 'HireDate', label: 'Hire Date', type: 'date', format: 'dd/MM/yyyy' },
{ field: 'Country', label: 'Country', type: 'string' }
];
const initialRules: RuleModel = {
condition: 'and',
rules: [
{
field: 'EmployeeID',
label: 'Employee ID',
operator: 'equal',
type: 'number',
value: 1001
}
]
};
return (
<QueryBuilderComponent
width="100%"
columns={columnData}
rule={initialRules}
/>
);
}
export default App;Common Patterns
Pattern 1: Retrieving Filtered Results as SQL
let qryBldrObj: QueryBuilderComponent;
function generateSQL() {
const sqlQuery = qryBldrObj.getSqlFromRules();
console.log('Generated SQL:', sqlQuery);
// Send to backend for execution
}Pattern 2: Programmatically Adding Rules
function addFilter() {
qryBldrObj.addRules([
{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
}
], 'group0');
}Pattern 3: Converting SQL Back to Rules
function importFilter(sqlString: string) {
qryBldrObj.setRulesFromSql(sqlString);
}Pattern 4: Displaying Summary View
<QueryBuilderComponent
columns={columnData}
summaryView={true} // Shows query summary at the bottom
/>Key Props Cheat Sheet
| Prop | Type | Default | Purpose |
|---|---|---|---|
columns | ColumnsModel[] | - | Defines available fields and operators |
rule | RuleModel | {} | Initial filter rules |
dataSource | Object[] \ | DataManager | [] |
displayMode | 'Horizontal' \ | 'Vertical' | 'Horizontal' |
allowDragAndDrop | boolean | false | Enable drag-drop rule management |
enablePersistence | boolean | false | Save state to localStorage |
enableRtl | boolean | false | Right-to-left layout |
allowValidation | boolean | false | Validate rule conditions |
summaryView | boolean | false | Show filtered query summary |
showButtons | ShowButtonsModel | defaults | Control add/delete button visibility |
maxGroupCount | number | 5 | Maximum nested group depth |
readonly | boolean | false | Make component read-only |
Common Use Cases
Use Case 1: Advanced Search Filter
Create a filter interface for users to build complex search queries with multiple conditions:
1. Define columns for searchable fields 2. Initialize with empty or default rules 3. Set showButtons to enable rule management 4. Retrieve SQL on form submission 5. Execute query on backend
Read: references/rules-and-filtering.md and references/query-conversion.md
Use Case 2: Data-Driven Dashboard
Build a dashboard where users filter data across multiple columns:
1. Bind DataManager with remote service 2. Configure columns based on data types 3. Enable drag-and-drop for better UX 4. Use getPredicate() to filter DataManager 5. Display filtered results dynamically
Read: references/data-binding.md and references/rules-and-filtering.md
Use Case 3: Query Template System
Allow users to save and load filter templates:
1. Set enablePersistence={true} for automatic state saving 2. Or manually save getRules() to database 3. Load rules with setRules() when needed 4. Display saved templates in a dropdown
Read: references/advanced-features.md
Use Case 4: Report Builder
Create a report filter UI with custom templates:
1. Design custom header template for branding 2. Use custom operators for domain-specific filtering 3. Enable validation with allowValidation 4. Display summaryView for clarity 5. Generate SQL for report execution
Read: references/templates-and-customization.md and references/advanced-features.md
Next Steps
1. Getting Started: Install the package and create your first Query Builder 2. Define Columns: Configure the fields users can filter on 3. Bind Data: Connect to local or remote data sources 4. Build UI: Add rules and groups with drag-and-drop support 5. Generate Queries: Convert rules to SQL or other formats 6. Customize: Apply themes, templates, and accessibility features
---
Need help? Check the specific reference files above for detailed examples and implementation patterns.
Advanced Features
Master advanced Query Builder features including display modes, state persistence, locking, cloning, RTL support, and accessibility.
Table of Contents
- Display Modes
- State Persistence
- Locking Rules and Groups
- Clone Operations
- Separate Connectors
- RTL Support
- Sort Direction
- Summary View
- Restrict Operations
- Accessibility Features
Display Modes
The Query Builder supports two layout orientations to accommodate different UI preferences.
Horizontal Display (Default)
<QueryBuilderComponent
displayMode="Horizontal"
columns={columns}
/>Layout:
[Field] [Operator] [Value] [Add Rule] [Add Group] [Delete]Vertical Display
<QueryBuilderComponent
displayMode="Vertical"
columns={columns}
/>Layout:
Field: [Dropdown]
Operator: [Dropdown]
Value: [Input]
[Add Rule] [Add Group] [Delete]Complete Display Mode Example
import { DisplayMode } from '@syncfusion/ej2-react-querybuilder';
import React, { useState } from 'react';
function App() {
const [displayMode, setDisplayMode] = useState<DisplayMode>('Horizontal');
const columns: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number' },
{ field: 'FirstName', label: 'First Name', type: 'string' },
{ field: 'Title', label: 'Title', type: 'string' }
];
return (
<div>
<div>
<button onClick={() => setDisplayMode('Horizontal')}>
Horizontal Layout
</button>
<button onClick={() => setDisplayMode('Vertical')}>
Vertical Layout
</button>
</div>
<QueryBuilderComponent
displayMode={displayMode}
columns={columns}
/>
</div>
);
}
export default App;State Persistence
Automatically save and restore the Query Builder's filter state using browser local storage.
Enable Persistence
<QueryBuilderComponent
columns={columns}
enablePersistence={true}
/>When enabled:
- Filter rules are saved to
localStorage - State is restored on page reload
- Perfect for multi-step workflows
Persistence Example
function App() {
const columns: ColumnsModel[] = [
{ field: 'TaskID', label: 'Task ID', type: 'number' },
{ field: 'Name', label: 'Name', type: 'string' },
{ field: 'Category', label: 'Category', type: 'string' }
];
const initialRules: RuleModel = {
condition: 'or',
rules: [{
field: 'Category',
label: 'Category',
operator: 'equal',
type: 'string',
value: 'Active'
}]
};
return (
<QueryBuilderComponent
width="100%"
enablePersistence={true}
columns={columns}
rule={initialRules}
/>
);
}Manual State Management
Save and load rules manually:
let qryBldrObj: QueryBuilderComponent;
function saveRules(): void {
const rules = qryBldrObj.getRules();
localStorage.setItem('queryBuilderRules', JSON.stringify(rules));
}
function loadRules(): void {
const savedRules = localStorage.getItem('queryBuilderRules');
if (savedRules) {
qryBldrObj.setRules(JSON.parse(savedRules));
}
}
return (
<div>
<QueryBuilderComponent
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
columns={columns}
/>
<button onClick={saveRules}>Save Filters</button>
<button onClick={loadRules}>Load Filters</button>
</div>
);Locking Rules and Groups
Make rules and groups read-only to prevent user modifications.
Lock Individual Rules
let qryBldrObj: QueryBuilderComponent;
function lockRule(ruleId: string): void {
qryBldrObj.lockRule(ruleId);
// User cannot edit or delete this rule
}
function unlockRule(ruleId: string): void {
// Unlock by deleting and re-adding
qryBldrObj.deleteRules([ruleId]);
}Lock Groups
function lockGroup(groupId: string): void {
qryBldrObj.lockGroup(groupId);
// User cannot edit or delete this group or its contents
}Locking Example
function App() {
let qryBldrObj: QueryBuilderComponent;
const initialRule: RuleModel = {
condition: 'and',
rules: [
{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
},
{
field: 'Status',
label: 'Status',
operator: 'equal',
type: 'string',
value: 'Active'
}
]
};
React.useEffect(() => {
// Lock first rule (Country filter)
if (qryBldrObj) {
const rules = qryBldrObj.getRules();
if (rules.rules && rules.rules.length > 0) {
const firstRuleId = rules.rules[0].field;
setTimeout(() => qryBldrObj.lockRule(firstRuleId), 100);
}
}
}, [qryBldrObj]);
return (
<QueryBuilderComponent
columns={columns}
rule={initialRule}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>
);
}Clone Operations
Duplicate rules and groups to quickly build complex filters.
Clone Rule
Create a copy of a rule and add it to a group:
function cloneRule(ruleId: string): void {
qryBldrObj.cloneRule(ruleId, 'group0', 0);
}
// Parameters:
// ruleID - ID of the rule to clone
// groupID - Target group for the cloned rule
// index - Position in the group (optional)Clone Group
Duplicate an entire group:
function cloneGroup(groupId: string): void {
qryBldrObj.cloneGroup(groupId, 'group0', 0);
}
// Parameters:
// groupID - ID of the group to clone
// parentGroupID - Target parent group
// index - Position in parent (optional)Clone Example
function App() {
let qryBldrObj: QueryBuilderComponent;
const initialRule: RuleModel = {
condition: 'and',
rules: [
{
field: 'Department',
label: 'Department',
operator: 'equal',
type: 'string',
value: 'Sales'
}
]
};
function duplicateLastRule(): void {
const rules = qryBldrObj.getRules();
if (rules.rules && rules.rules.length > 0) {
const lastRule = rules.rules[rules.rules.length - 1];
qryBldrObj.cloneRule(lastRule.field as string, 'group0');
}
}
return (
<div>
<QueryBuilderComponent
columns={columns}
rule={initialRule}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>
<button onClick={duplicateLastRule}>Clone Last Rule</button>
</div>
);
}Separate Connectors
Display separate connectors (AND/OR) between rules for better visual distinction.
Enable Separate Connectors
<QueryBuilderComponent
columns={columns}
enableSeparateConnector={true}
/>Without:
[Rule 1] AND [Rule 2] AND [Rule 3]With:
[Rule 1]
AND
[Rule 2]
AND
[Rule 3]Example with Separate Connectors
<QueryBuilderComponent
width="100%"
columns={columns}
enableSeparateConnector={true}
displayMode="Vertical"
/>RTL Support
Enable right-to-left (RTL) layout for languages like Arabic, Farsi, and Urdu.
Enable RTL
<QueryBuilderComponent
enableRtl={true}
columns={columns}
/>RTL with Localization
import { L10n } from '@syncfusion/ej2-base';
L10n.load({
'ar-AE': {
'querybuilder': {
'AddCondition': 'اضافة الشرط',
'AddGroup': 'إضافة مجموعة',
'DeleteRule': 'حذف القاعدة',
'DeleteGroup': 'حذف المجموعة',
'SelectField': 'اختر حقل',
'SelectOperator': 'اختر مشغل',
'Equal': 'يساوي',
'NotEqual': 'لا يساوي',
'Contains': 'يحتوي على',
'Between': 'بين',
'In': 'في'
}
}
});
function App() {
return (
<QueryBuilderComponent
locale="ar-AE"
enableRtl={true}
columns={columns}
/>
);
}Sort Direction
Control the sort order of column names in the field dropdown.
Sort Options
type SortDirection = 'Default' | 'Ascending' | 'Descending';Example
<QueryBuilderComponent
columns={columns}
sortDirection="Ascending"
/>Default: Shows fields in definition order Ascending: Alphabetical A-Z Descending: Alphabetical Z-A
Summary View
Display a text summary of the filter query below the Query Builder.
Enable Summary View
<QueryBuilderComponent
columns={columns}
summaryView={true}
/>Output Example:
(EmployeeID = 1001) AND (Title LIKE '%Manager%')Summary View Example
function App() {
const initialRule: RuleModel = {
condition: 'and',
rules: [
{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
},
{
field: 'Salary',
label: 'Salary',
operator: 'greaterthan',
type: 'number',
value: 50000
}
]
};
return (
<QueryBuilderComponent
width="100%"
columns={columns}
rule={initialRule}
summaryView={true}
/>
);
}Restrict Operations
Control which operations are available to users.
Disable Drag and Drop
<QueryBuilderComponent
allowDragAndDrop={false}
columns={columns}
/>Disable Button Controls
const buttonOptions: ShowButtonsModel = {
ruleDelete: false, // Hide delete rule button
groupInsert: false, // Hide add group button
groupDelete: false // Hide delete group button
};
<QueryBuilderComponent
showButtons={buttonOptions}
columns={columns}
/>Read-Only Mode
<QueryBuilderComponent
readonly={true}
columns={columns}
/>In readonly mode:
- Users cannot add, edit, or delete rules
- They can view the existing query
- Perfect for displaying saved filters
Max Group Depth
Limit nested group depth:
<QueryBuilderComponent
maxGroupCount={3} // Maximum 3 levels of nesting
columns={columns}
/>Accessibility Features
The Query Builder is fully accessible and compliant with WCAG standards.
Keyboard Navigation
| Key | Action |
|---|---|
Tab / Shift+Tab | Move focus to next/previous element |
Enter | Select/activate focused button |
Space | Toggle checkbox or activate button |
Arrow Keys | Navigate dropdown options |
Screen Reader Support
<QueryBuilderComponent
columns={columns}
// Automatically includes ARIA attributes
/>Accessibility Example
function App() {
return (
<div>
<label htmlFor="qb-filter">Filter Employees</label>
<QueryBuilderComponent
id="qb-filter"
columns={columns}
// ARIA labels are automatically added
/>
</div>
);
}Complete Advanced Features Example
import { ColumnsModel, QueryBuilderComponent, RuleModel, ShowButtonsModel } from '@syncfusion/ej2-react-querybuilder';
import { L10n } from '@syncfusion/ej2-base';
import React from 'react';
// Setup RTL
L10n.load({
'ar-AE': {
'querybuilder': {
'AddCondition': 'اضافة الشرط',
'AddGroup': 'إضافة مجموعة'
}
}
});
function App() {
const columns: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number' },
{ field: 'FirstName', label: 'First Name', type: 'string' },
{ field: 'Country', label: 'Country', type: 'string' }
];
const initialRule: RuleModel = {
condition: 'and',
rules: [{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
}]
};
const buttonOptions: ShowButtonsModel = {
ruleDelete: true,
groupInsert: true,
groupDelete: true
};
return (
<div>
<QueryBuilderComponent
width="100%"
columns={columns}
rule={initialRule}
displayMode="Vertical"
enablePersistence={true}
enableSeparateConnector={true}
enableRtl={false}
sortDirection="Ascending"
summaryView={true}
showButtons={buttonOptions}
maxGroupCount={5}
allowDragAndDrop={true}
/>
</div>
);
}
export default App;This example combines:
- Vertical display mode
- State persistence
- Separate connectors
- Column sorting
- Summary view
- Drag-and-drop
- Button controls
- Nesting limit
API Reference
Complete reference for all Query Builder properties, methods, and events.
Table of Contents
Properties
Configuration Properties
addRuleToNewGroups
- Type:
boolean - Default:
true - Description: Specifies whether to enable/disable adding new rules when adding new groups.
<QueryBuilderComponent addRuleToNewGroups={false} />allowDragAndDrop
- Type:
boolean - Default:
false - Description: Enables/disables drag-and-drop support to move rules and groups.
<QueryBuilderComponent allowDragAndDrop={true} />allowValidation
- Type:
boolean - Default:
false - Description: Enables or disables field validation.
<QueryBuilderComponent allowValidation={true} />autoSelectField
- Type:
boolean - Default:
false - Description: Automatically selects the first value for the field dropdown.
<QueryBuilderComponent autoSelectField={true} />autoSelectOperator
- Type:
boolean - Default:
true - Description: Automatically selects the first operator for the selected field.
<QueryBuilderComponent autoSelectOperator={true} />Data Properties
columns
- Type:
ColumnsModel[] - Default:
{} - Description: Specifies columns to create filters.
const columns: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number' },
{ field: 'FirstName', label: 'First Name', type: 'string' }
];
<QueryBuilderComponent columns={columns} />dataSource
- Type:
Object[] | Object | DataManager - Default:
[] - Description: Binds the column names from data source. Can be an array or DataManager instance.
<QueryBuilderComponent dataSource={employeeData} />
// or
<QueryBuilderComponent dataSource={new DataManager({url: 'api/data'})} />rule
- Type:
RuleModel - Default:
{} - Description: Defines initial rules in the Query Builder.
const initialRule: RuleModel = {
condition: 'and',
rules: [{
field: 'EmployeeID',
operator: 'equal',
type: 'number',
value: 1001
}]
};
<QueryBuilderComponent rule={initialRule} />Display Properties
displayMode
- Type:
'Horizontal' | 'Vertical' - Default:
'Horizontal' - Description: Specifies layout orientation.
<QueryBuilderComponent displayMode="Vertical" />height
- Type:
string - Default:
'auto' - Description: Specifies the height of the Query Builder.
<QueryBuilderComponent height="400px" />width
- Type:
string - Default:
'auto' - Description: Specifies the width of the Query Builder.
<QueryBuilderComponent width="100%" />cssClass
- Type:
string - Default:
'' - Description: Defines CSS classes for styling.
<QueryBuilderComponent cssClass="custom-qb dark-theme" />headerTemplate
- Type:
string | Function - Default:
null - Description: Specifies template for the header area.
function customHeader(props: any) {
return <div>Custom Header</div>;
}
<QueryBuilderComponent headerTemplate={customHeader} />Behavior Properties
enableNotCondition
- Type:
boolean - Default:
false - Description: Enables/disables the NOT group condition.
<QueryBuilderComponent enableNotCondition={true} />enablePersistence
- Type:
boolean - Default:
false - Description: Enables state persistence to localStorage.
<QueryBuilderComponent enablePersistence={true} />enableRtl
- Type:
boolean - Default:
false - Description: Enables right-to-left rendering.
<QueryBuilderComponent enableRtl={true} locale="ar-AE" />enableSeparateConnector
- Type:
boolean - Default:
false - Description: Displays separate AND/OR connectors between rules.
<QueryBuilderComponent enableSeparateConnector={true} />readonly
- Type:
boolean - Default:
false - Description: Makes the component read-only.
<QueryBuilderComponent readonly={true} />Advanced Properties
fieldMode
- Type:
'Default' | 'DropDownTree' - Default:
'Default' - Description: Sets field display as DropDownList or DropDownTree.
<QueryBuilderComponent fieldMode="DropDownTree" />fieldModel
- Type:
DropDownListModel | DropDownTreeModel - Default:
null - Description: Specifies properties for the field dropdown.
<QueryBuilderComponent fieldModel={{ popupHeight: '300px' }} />operatorModel
- Type:
DropDownListModel - Default:
null - Description: Specifies properties for the operator dropdown.
<QueryBuilderComponent operatorModel={{ popupHeight: '200px' }} />valueModel
- Type:
ValueModel - Default:
null - Description: Specifies properties for the value input.
<QueryBuilderComponent valueModel={{ placeholder: 'Enter value' }} />Configuration Constants
immediateModeDelay
- Type:
number - Default:
0 - Description: Delay (ms) before triggering rule change event.
<QueryBuilderComponent immediateModeDelay={500} />locale
- Type:
string - Default:
'' - Description: Overrides global culture and localization.
<QueryBuilderComponent locale="fr-FR" />matchCase
- Type:
boolean - Default:
false - Description: Case-sensitive filtering.
<QueryBuilderComponent matchCase={true} />maxGroupCount
- Type:
number - Default:
5 - Description: Maximum group nesting depth.
<QueryBuilderComponent maxGroupCount={3} />separator
- Type:
string - Default:
'' - Description: Separator string for column display.
<QueryBuilderComponent separator=" - " />showButtons
- Type:
ShowButtonsModel - Default:
{ ruleDelete: true, groupInsert: true, groupDelete: true } - Description: Controls button visibility.
<QueryBuilderComponent showButtons={{
ruleDelete: true,
groupInsert: true,
groupDelete: false
}} />sortDirection
- Type:
'Default' | 'Ascending' | 'Descending' - Default:
'Default' - Description: Sort direction of field names.
<QueryBuilderComponent sortDirection="Ascending" />summaryView
- Type:
boolean - Default:
false - Description: Shows/hides the filtered query summary.
<QueryBuilderComponent summaryView={true} />---
Methods
Rule Management
addRules
Adds single or multiple rules.
addRules(
rules: RuleModel[],
groupID: string
): voidExample:
qryBldrObj.addRules([{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
}], 'group0');addGroups
Adds single or multiple groups.
addGroups(
groups: RuleModel[],
groupID: string
): voidExample:
qryBldrObj.addGroups([{
condition: 'or',
rules: [{
field: 'Title',
label: 'Title',
operator: 'equal',
type: 'string',
value: 'Manager'
}]
}], 'group0');deleteRules
Deletes rule(s) by ID.
deleteRules(ruleIdColl: string[]): voidExample:
qryBldrObj.deleteRules(['rule_1', 'rule_2']);deleteGroups
Deletes group(s) by ID.
deleteGroups(groupIdColl: string[]): voidExample:
qryBldrObj.deleteGroups(['group_1']);getRules
Gets current rule collection.
getRules(): RuleModelExample:
const rules = qryBldrObj.getRules();
console.log(rules);getRule
Gets a single rule by element or ID.
getRule(elem: string | HTMLElement): RuleModelExample:
const rule = qryBldrObj.getRule('rule_1');getGroup
Gets a group by element or ID.
getGroup(target: string | HTMLElement): RuleModelExample:
const group = qryBldrObj.getGroup('group_1');setRules
Sets rule collection.
setRules(rule: RuleModel): voidExample:
qryBldrObj.setRules({
condition: 'and',
rules: [{...}]
});reset
Clears all rules.
reset(): voidExample:
qryBldrObj.reset();Clone Operations
cloneRule
Clones a rule to a group.
cloneRule(
ruleID: string,
groupID: string,
index?: number
): voidExample:
qryBldrObj.cloneRule('rule_1', 'group_0', 0);cloneGroup
Clones a group to parent group.
cloneGroup(
groupID: string,
parentGroupID: string,
index?: number
): voidExample:
qryBldrObj.cloneGroup('group_1', 'group_0', 0);Lock Operations
lockRule
Locks a rule for read-only access.
lockRule(ruleID: string): voidExample:
qryBldrObj.lockRule('rule_1');lockGroup
Locks a group for read-only access.
lockGroup(groupID: string): voidExample:
qryBldrObj.lockGroup('group_1');Query Conversion
getSqlFromRules
Converts rules to SQL WHERE clause.
getSqlFromRules(
rule?: RuleModel,
allowEscape?: boolean,
sqlLocale?: boolean
): stringExample:
const sql = qryBldrObj.getSqlFromRules();
// Output: "(EmployeeID = 1001) AND (Title LIKE '%Manager%')"getMongoQuery
Converts rules to Mongo query.
getMongoQuery(
rule?: RuleModel,
mongoLocale?: boolean
): stringExample:
const mongoQuery = qryBldrObj.getMongoQuery();
// Output: { $and: [ { EmployeeID: 1001 }, { Title: { $regex: 'Manager' } } ] }getParameterizedSql
Gets parameterized SQL with placeholders.
getParameterizedSql(
rule?: RuleModel,
sqlLocale?: boolean
): ParameterizedSqlReturns:
{
query: string;
params: any[];
}Example:
const { query, params } = qryBldrObj.getParameterizedSql();
// query: "(EmployeeID = ?) AND (Title LIKE ?)"
// params: [1001, '%Manager%']getParameterizedNamedSql
Gets named parameter SQL.
getParameterizedNamedSql(
rule?: RuleModel,
sqlLocale?: boolean
): ParameterizedNamedSqlReturns:
{
query: string;
params: { [key: string]: any };
}Example:
const { query, params } = qryBldrObj.getParameterizedNamedSql();
// query: "(EmployeeID = @EmployeeID_1) AND (Title LIKE @Title_1)"
// params: { '@EmployeeID_1': 1001, '@Title_1': '%Manager%' }getPredicate
Gets DataManager predicate for filtering.
getPredicate(rule: RuleModel): PredicateExample:
const predicate = qryBldrObj.getPredicate(rule);
const query = new Query().where(predicate);
const results = dataManager.executeQuery(query);Import/Export
setRulesFromSql
Imports rules from SQL query.
setRulesFromSql(
sqlString: string,
sqlLocale?: boolean
): voidExample:
qryBldrObj.setRulesFromSql(
"(Country = 'USA') AND (Salary > 50000)"
);getRulesFromSql
Gets rules from SQL query.
getRulesFromSql(
sqlString: string,
sqlLocale?: boolean
): RuleModelExample:
const rules = qryBldrObj.getRulesFromSql(
"(Country = 'USA')"
);setMongoQuery
Sets rules from Mongo query.
setMongoQuery(
mongoQuery: string,
mongoLocale?: boolean
): voidExample:
qryBldrObj.setMongoQuery(
"{ Country: 'USA', Salary: { $gt: 50000 } }"
);setParameterizedSql
Sets rules from parameterized SQL.
setParameterizedSql(
sqlQuery: ParameterizedSql
): voidsetParameterizedNamedSql
Sets rules from named parameter SQL.
setParameterizedNamedSql(
sqlQuery: ParameterizedNamedSql
): voidValidation & Data
validateFields
Validates all rule conditions.
validateFields(): booleanExample:
const isValid = qryBldrObj.validateFields();
if (isValid) {
// Process valid rules
}getValidRules
Gets valid rule collection (non-empty).
getValidRules(currentRule?: RuleModel): RuleModelExample:
const validRules = qryBldrObj.getValidRules();getValues
Gets values for a field.
getValues(field: string): object[]Example:
const countryValues = qryBldrObj.getValues('Country');getOperators
Gets operators for field(s).
getOperators(): { [key: string]: Object }[]Example:
const operators = qryBldrObj.getOperators();Utilities
getDataManagerQuery
Gets DataManager query.
getDataManagerQuery(rule: RuleModel): QuerygetFilteredRecords
Gets filtered records as promise.
getFilteredRecords(): Promise<object> | objectnotifyChange
Notifies component of value changes.
notifyChange(
value: string | number | boolean | Date,
element: Element,
type?: string
): voiddestroy
Removes component from DOM.
destroy(): void---
Events
actionBegin
Triggers when field, operator, or value changes.
<QueryBuilderComponent
actionBegin={(args: ActionEventArgs) => {
console.log('Action:', args.requestType);
}}
/>beforeChange
Triggers before condition, field, operator, value changes.
<QueryBuilderComponent
beforeChange={(args: ChangeEventArgs) => {
if (args.cancel === true) {
// Prevent change
args.cancel = true;
}
}}
/>change
Triggers when condition, field, value, operator changes.
<QueryBuilderComponent
change={(args: ChangeEventArgs) => {
console.log('Changed rules:', args.rule);
}}
/>ruleChange
Triggers when rules change with detailed info.
<QueryBuilderComponent
ruleChange={(args: RuleChangeEventArgs) => {
console.log('Previous:', args.previousRule);
console.log('New:', args.rule);
}}
/>created
Triggers when component is created.
<QueryBuilderComponent
created={() => {
console.log('Query Builder created');
}}
/>destroyed
Triggers when component is destroyed.
<QueryBuilderComponent
destroyed={() => {
console.log('Query Builder destroyed');
}}
/>dataBound
Triggers when data is bound.
<QueryBuilderComponent
dataBound={() => {
console.log('Data bound');
}}
/>drag
Triggers during rule/group dragging.
<QueryBuilderComponent
drag={(args: DragEventArgs) => {
console.log('Dragging:', args.rule);
}}
/>dragStart
Triggers when drag starts.
<QueryBuilderComponent
dragStart={(args: DragEventArgs) => {
console.log('Drag started');
}}
/>drop
Triggers when rule/group is dropped.
<QueryBuilderComponent
drop={(args: DropEventArgs) => {
console.log('Dropped at:', args.targetID);
}}
/>---
Interfaces and Types
RuleModel
interface RuleModel {
condition?: string; // 'and' or 'or'
rules?: RuleModel[]; // Child rules
field?: string; // Column field
label?: string; // Display label
operator?: string; // Operator type
type?: string; // Data type
value?: any; // Filter value
not?: boolean; // NOT condition
}ColumnsModel
interface ColumnsModel {
field: string; // Data field name
label?: string; // Display label
type?: string; // Data type
operators?: any[]; // Available operators
values?: any[]; // Predefined values
format?: string; // Format string
step?: number; // Step for numbers
validation?: any; // Validation rules
}ShowButtonsModel
interface ShowButtonsModel {
ruleDelete?: boolean; // Show rule delete
groupInsert?: boolean; // Show add group
groupDelete?: boolean; // Show group delete
}ParameterizedSql
interface ParameterizedSql {
query: string; // SQL with placeholders
params: any[]; // Parameter values
}ParameterizedNamedSql
interface ParameterizedNamedSql {
query: string; // SQL with named params
params: { [key: string]: any }; // Named parameters
}Columns and Operators
Configure column definitions that control how fields appear and behave in the Query Builder, including available operators, validation, and formatting.
Table of Contents
- Column Schema Overview
- Auto-Generation
- Supported Operators
- Labels and Field Mapping
- Data Types and Formatting
- Validation
- Custom Operators
- Step and Format
Column Schema Overview
Column definitions define the schema for the Query Builder and control how fields appear and behave. The field property is essential for binding data source values to query builder columns.
interface ColumnsModel {
field: string; // Required: data field name
label: string; // Display label in UI
type: string; // Data type: 'string' | 'number' | 'date' | 'boolean'
operators?: any[]; // Available operators for this column
values?: any[]; // Predefined values (for boolean/enum)
format?: string; // Date/number format string
step?: number; // Step increment for number fields
validation?: any; // Validation rules for the field
}Basic Column Definition
const columns: ColumnsModel[] = [
{
field: 'EmployeeID',
label: 'Employee ID',
type: 'number'
},
{
field: 'FirstName',
label: 'First Name',
type: 'string'
},
{
field: 'Country',
label: 'Country',
type: 'string'
}
];Auto-Generation
When the columns property is empty or undefined during initialization, the Query Builder automatically generates columns from all fields in the dataSource.
import { employeeData } from './datasource';
function App() {
return (
<QueryBuilderComponent
width="100%"
dataSource={employeeData}
// columns property omitted - will auto-generate from dataSource
/>
);
}How Auto-Generation Works:
- The component inspects the first record in the data source
- Detects the data type of each field
- Creates ColumnsModel entries automatically
- Assigns the field name as both field and label
Note: The column type is inferred from the first record's data type. If the first record is missing a field, that field won't be included in auto-generated columns.
Supported Operators
Define available operators for each column using the operators property. The following operators are supported based on data type:
Operator Type Compatibility
| Operator | Description | String | Number | Date | Boolean |
|---|---|---|---|---|---|
startswith | Value begins with string | ✓ | - | - | - |
endswith | Value ends with string | ✓ | - | - | - |
contains | Value contains string | ✓ | - | - | - |
equal | Value equals | ✓ | ✓ | ✓ | ✓ |
notequal | Value does not equal | ✓ | ✓ | ✓ | ✓ |
greaterthan | Value is greater than | - | ✓ | ✓ | - |
greaterthanorequal | Value is >= | - | ✓ | ✓ | - |
lessthan | Value is less than | - | ✓ | ✓ | - |
lessthanorequal | Value is <= | - | ✓ | ✓ | - |
between | Value is between two values | - | ✓ | ✓ | - |
notbetween | Value is not between two values | - | ✓ | ✓ | - |
in | Value is in list | ✓ | ✓ | - | - |
notin | Value is not in list | ✓ | ✓ | - | - |
isnull | Value is null | ✓ | ✓ | ✓ | ✓ |
isnotnull | Value is not null | ✓ | ✓ | ✓ | ✓ |
Restricting Operators per Column
const columns: ColumnsModel[] = [
{
field: 'EmployeeID',
label: 'Employee ID',
type: 'number',
operators: [
{ key: 'Equal', value: 'equal' },
{ key: 'Greater than', value: 'greaterthan' },
{ key: 'Less than', value: 'lessthan' }
]
},
{
field: 'FirstName',
label: 'First Name',
type: 'string',
operators: [
{ key: 'Contains', value: 'contains' },
{ key: 'Starts with', value: 'startswith' },
{ key: 'Ends with', value: 'endswith' }
]
}
];Labels and Field Mapping
The field property maps to your data source, while label is the display text shown to users.
const columns: ColumnsModel[] = [
{
field: 'EmpID', // Maps to data source field
label: 'Employee ID' // User-friendly display label
},
{
field: 'EmpName',
label: 'Employee Name'
},
{
field: 'DeptCode',
label: 'Department'
}
];Important: If the column field is not in the data source, the column values will remain empty.
Data Types and Formatting
String Type
{
field: 'FirstName',
label: 'First Name',
type: 'string'
// No additional formatting needed
}Number Type
{
field: 'Salary',
label: 'Annual Salary',
type: 'number',
format: 'C2' // Currency format with 2 decimal places
}Supported number formats:
N2- Number with 2 decimal placesC2- Currency with 2 decimal placesP0- Percentage with 0 decimal places
Date Type
{
field: 'HireDate',
label: 'Hire Date',
type: 'date',
format: 'dd/MM/yyyy' // Date format string
}Supported date formats:
dd/MM/yyyy- Day/Month/YearMM/dd/yyyy- Month/Day/Yearyyyy-MM-dd- ISO formatdd MMM yyyy- Day Month Year
Boolean Type
{
field: 'IsActive',
label: 'Active Status',
type: 'boolean',
values: ['Active', 'Inactive'] // Display values
}Validation
Enable validation to ensure users enter valid data. Use the allowValidation property on the Query Builder and set validation rules per column.
function App() {
let qryBldrObj: QueryBuilderComponent;
const columns: ColumnsModel[] = [
{
field: 'EmployeeID',
label: 'Employee ID',
type: 'number',
validation: { isRequired: true } // Field is required
},
{
field: 'FirstName',
label: 'First Name',
type: 'string',
validation: { isRequired: true }
}
];
function validateRules(): void {
const isValid = qryBldrObj.validateFields();
if (isValid) {
console.log('Rules are valid');
} else {
console.log('Please fix validation errors');
}
}
return (
<div>
<QueryBuilderComponent
width="100%"
columns={columns}
allowValidation={true}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>
<button onClick={validateRules}>Validate</button>
</div>
);
}Validation Properties
validation: {
isRequired: true // Field must have a value
min?: number; // Minimum value/length
max?: number; // Maximum value/length
pattern?: string; // Regex pattern
customError?: string; // Custom error message
}Note: Validation for Field values is automatic. You must manually configure validation for Operator and Value fields through the validation property.Custom Operators
Add custom operators specific to your domain or use case:
const columns: ColumnsModel[] = [
{
field: 'Status',
label: 'Status',
type: 'string',
operators: [
{ key: 'Equal', value: 'equal' },
{ key: 'Not Equal', value: 'notequal' },
{ key: 'Is Active', value: 'isactive' }, // Custom
{ key: 'Is Inactive', value: 'isinactive' } // Custom
]
}
];Then handle custom operators in the actionBegin event:
function onActionBegin(args: ActionEventArgs): void {
if (args.requestType === 'rule-change') {
if (args.rule?.operator === 'isactive') {
args.rule.value = 'Active';
}
}
}Step and Format
Step Property (Number Fields)
Set step increments for numeric input fields:
const columns: ColumnsModel[] = [
{
field: 'Quantity',
label: 'Quantity',
type: 'number',
step: 10 // Increment/decrement by 10
},
{
field: 'Price',
label: 'Price',
type: 'number',
step: 0.01 // Increment/decrement by 0.01
}
];The step value controls:
- How much the value changes when using spinner buttons
- The keyboard arrow key increment amount
- The mouse wheel scroll increment
Format Property
Format controls how values are displayed and entered:
Date Formatting:
{
field: 'OrderDate',
label: 'Order Date',
type: 'date',
format: 'dd/MM/yyyy'
}Number Formatting:
{
field: 'Revenue',
label: 'Revenue',
type: 'number',
format: 'C2' // $1,000.00
}Complete Example
import { ColumnsModel, QueryBuilderComponent } from '@syncfusion/ej2-react-querybuilder';
import React from 'react';
function App() {
const columns: ColumnsModel[] = [
{
field: 'EmployeeID',
label: 'Employee ID',
type: 'number',
operators: [
{ key: 'Equal', value: 'equal' },
{ key: 'Greater than', value: 'greaterthan' },
{ key: 'Less than', value: 'lessthan' }
],
validation: { isRequired: true }
},
{
field: 'FirstName',
label: 'First Name',
type: 'string',
validation: { isRequired: true }
},
{
field: 'HireDate',
label: 'Hire Date',
type: 'date',
format: 'dd/MM/yyyy'
},
{
field: 'Salary',
label: 'Salary',
type: 'number',
step: 1000,
format: 'C2'
},
{
field: 'IsActive',
label: 'Active',
type: 'boolean',
values: ['Yes', 'No']
}
];
return (
<QueryBuilderComponent
width="100%"
columns={columns}
allowValidation={true}
/>
);
}
export default App;This example shows:
- Number field with restricted operators
- String field with required validation
- Date field with formatting
- Number field with currency formatting and step increment
- Boolean field with custom display values
Data Binding
Learn how to bind data to the Query Builder using local JavaScript arrays or remote RESTful JSON services through DataManager.
Table of Contents
- Overview
- Local Data Binding
- Remote Data Binding
- DataManager Integration
- Binding Rules with Data
- Dynamic Data Updates
Overview
The Query Builder uses the dataSource property to bind data. Two binding methods are available:
1. Local Data - JavaScript object arrays 2. Remote Data - RESTful services via DataManager
The dataSource supports:
- Plain JavaScript object arrays
- DataManager instances with various adaptors
- Dynamic updates and refreshes
Local Data Binding
Basic Local Data
Assign a JavaScript object array directly to the dataSource property:
import { ColumnsModel, QueryBuilderComponent } from '@syncfusion/ej2-react-querybuilder';
import React from 'react';
const employeeData = [
{ EmployeeID: 1001, FirstName: 'Nancy', Title: 'Sales Manager', Country: 'USA' },
{ EmployeeID: 1002, FirstName: 'Michael', Title: 'Sales Representative', Country: 'UK' },
{ EmployeeID: 1003, FirstName: 'Robert', Title: 'Sales Manager', Country: 'USA' },
{ EmployeeID: 1004, FirstName: 'Andrew', Title: 'Software Developer', Country: 'Canada' }
];
function App() {
const columns: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number' },
{ field: 'FirstName', label: 'First Name', type: 'string' },
{ field: 'Title', label: 'Title', type: 'string' },
{ field: 'Country', label: 'Country', type: 'string' }
];
return (
<QueryBuilderComponent
width="100%"
dataSource={employeeData}
columns={columns}
/>
);
}
export default App;Note: By default, DataManager uses JsonAdaptor for local data binding, so you don't need to explicitly specify it.
Local Data with Initial Rules
Combine local data with predefined filter rules:
import { RuleModel } from '@syncfusion/ej2-react-querybuilder';
function App() {
const columns: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number' },
{ field: 'FirstName', label: 'First Name', type: 'string' },
{ field: 'Title', label: 'Title', type: 'string' },
{ field: 'Country', label: 'Country', type: 'string' }
];
const initialRules: RuleModel = {
condition: 'and',
rules: [
{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
},
{
field: 'Title',
label: 'Title',
operator: 'contains',
type: 'string',
value: 'Manager'
}
]
};
return (
<QueryBuilderComponent
width="100%"
dataSource={employeeData}
columns={columns}
rule={initialRules}
/>
);
}Remote Data Binding
Binding Remote Data with DataManager
Assign service data as a DataManager instance to the dataSource property with the service endpoint URL:
import { DataManager, ODataV4Adaptor } from '@syncfusion/ej2-data';
import { ColumnsModel, QueryBuilderComponent } from '@syncfusion/ej2-react-querybuilder';
import React from 'react';
function App() {
const data = new DataManager({
url: 'url',
adaptor: new ODataV4Adaptor()
});
const columns: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number' },
{ field: 'FirstName', label: 'First Name', type: 'string' },
{ field: 'Title', label: 'Title', type: 'string' },
{ field: 'HireDate', label: 'Hire Date', type: 'date', format: 'dd/MM/yyyy' },
{ field: 'Country', label: 'Country', type: 'string' },
{ field: 'City', label: 'City', type: 'string' }
];
return (
<QueryBuilderComponent
width="100%"
dataSource={data}
columns={columns}
/>
);
}
export default App;Using JsonAdaptor for Remote JSON
If your API returns standard JSON without OData protocol:
import { DataManager, JsonAdaptor } from '@syncfusion/ej2-data';
const data = new DataManager({
url: 'url',
adaptor: new JsonAdaptor()
});
<QueryBuilderComponent dataSource={data} columns={columns} />REST Adaptor for Custom APIs
For APIs that follow REST conventions:
import { DataManager } from '@syncfusion/ej2-data';
const data = new DataManager({
url: 'url',
adaptor: new JsonAdaptor(),
crossDomain: true
});DataManager Integration
Available Adaptors
| Adaptor | Use Case |
|---|---|
ODataV4Adaptor | OData v4 services |
ODataAdaptor | OData v3 services |
JsonAdaptor | Standard JSON APIs |
UrlAdaptor | REST-style endpoints |
GraphQLAdaptor | GraphQL endpoints |
Creating DataManager with Options
import { DataManager } from '@syncfusion/ej2-data';
const data = new DataManager({
url: 'url',
adaptor: new ODataV4Adaptor(),
pageSize: 50,
offline: false,
timeStamp: false,
crossDomain: true
});Key Options:
url- API endpointadaptor- Protocol adaptorpageSize- Records per requestoffline- Cache data locallycrossDomain- Enable CORS requests
Binding Rules with Data
Initializing with Rules
Load initial filter rules with data:
const initialRules: RuleModel = {
condition: 'and',
rules: [
{
field: 'EmployeeID',
label: 'Employee ID',
operator: 'equal',
type: 'number',
value: 1001
},
{
field: 'Title',
label: 'Title',
operator: 'equal',
type: 'string',
value: 'Sales Manager'
}
]
};
<QueryBuilderComponent
dataSource={employeeData}
columns={columns}
rule={initialRules}
/>Filtering Data with Rules
Use getPredicate() to convert rules into a predicate for DataManager filtering:
let qryBldrObj: QueryBuilderComponent;
function applyFilter(): void {
const predicate = qryBldrObj.getPredicate(qryBldrObj.rule);
const filteredData = data.executeQuery(new Query().where(predicate));
// Use filteredData for display
}
<QueryBuilderComponent
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>Dynamic Data Updates
Updating DataSource
Change the data source dynamically at runtime:
let qryBldrObj: QueryBuilderComponent;
const dataManager = new DataManager(employeeData);
function updateData(): void {
const newData = [
{ EmployeeID: 2001, FirstName: 'John', Title: 'Manager', Country: 'USA' },
{ EmployeeID: 2002, FirstName: 'Jane', Title: 'Developer', Country: 'UK' }
];
dataManager.dataSource.json = newData;
qryBldrObj.dataSource = dataManager;
}
<QueryBuilderComponent
dataSource={dataManager}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>Handling Data Changes
Use the dataBound event when remote data loads:
function onDataBound(args: any): void {
console.log('Data has been bound to the Query Builder');
// Update UI or trigger other logic
}
<QueryBuilderComponent
dataSource={remoteData}
dataBound={onDataBound}
/>Complete Example: Local and Remote Data
import { DataManager, ODataV4Adaptor } from '@syncfusion/ej2-data';
import { ColumnsModel, QueryBuilderComponent, RuleModel } from '@syncfusion/ej2-react-querybuilder';
import React, { useState } from 'react';
function App() {
const [dataSource, setDataSource] = useState(employeeData);
const columns: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number' },
{ field: 'FirstName', label: 'First Name', type: 'string' },
{ field: 'Title', label: 'Title', type: 'string' },
{ field: 'Country', label: 'Country', type: 'string' }
];
const initialRules: RuleModel = {
condition: 'and',
rules: [{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
}]
};
function switchToRemote(): void {
const remoteData = new DataManager({
url: 'url',
adaptor: new ODataV4Adaptor()
});
setDataSource(remoteData);
}
function switchToLocal(): void {
setDataSource(employeeData);
}
return (
<div>
<button onClick={switchToLocal}>Use Local Data</button>
<button onClick={switchToRemote}>Use Remote Data</button>
<QueryBuilderComponent
width="100%"
dataSource={dataSource}
columns={columns}
rule={initialRules}
/>
</div>
);
}
export default App;This example demonstrates:
- Switching between local and remote data sources
- Using DataManager with ODataV4Adaptor
- Preserving filter rules during data source changes
Getting Started with Query Builder
Learn how to install, set up, and create your first Query Builder component in a React application.
Table of Contents
Installation
Install the Query Builder package and its dependencies using npm:
npm install @syncfusion/ej2-react-querybuilder --saveThis command installs the Query Builder package into your project's node_modules folder and adds it to your package.json dependencies.
Note: The--saveflag automatically updates yourpackage.jsonfile.
CSS Setup
The Query Builder requires CSS files for styling. Add these imports to your src/App.css or main CSS file:
@import "../node_modules/@syncfusion/ej2-base/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-buttons/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-splitbuttons/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-dropdowns/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-inputs/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-lists/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-popups/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-calendars/styles/tailwind3.css";
@import "../node_modules/@syncfusion/ej2-querybuilder/styles/tailwind3.css";Then import your CSS file in your React component:
import './App.css';Theme Options: Available themes includematerial.css,bootstrap.css,bootstrap4.css,fabric.css,highcontrast.css, andtailwind3.css. Choose the one that matches your design system.
Basic Component
Create a simple Query Builder with basic columns in your src/App.tsx:
import { ColumnsModel, QueryBuilderComponent } from '@syncfusion/ej2-react-querybuilder';
import React from 'react';
import './App.css';
function App() {
const columnData: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'EmployeeID', type: 'number' },
{ field: 'FirstName', label: 'FirstName', type: 'string' },
{ field: 'Title', label: 'Title', type: 'string' },
{ field: 'HireDate', label: 'HireDate', type: 'date', format: 'dd/MM/yyyy' },
{ field: 'Country', label: 'Country', type: 'string' }
];
return (
<QueryBuilderComponent width="100%" columns={columnData} />
);
}
export default App;This creates a Query Builder with 5 columns available for filtering:
- EmployeeID (number) - For numeric filtering
- FirstName (string) - For text filtering
- Title (string) - For job title filtering
- HireDate (date) - For date range filtering
- Country (string) - For location filtering
Column Configuration
ColumnsModel Properties
Each column in the columns array should have these basic properties:
| Property | Type | Description |
|---|---|---|
field | string | The data field name this column represents (required) |
label | string | Display label shown in the UI dropdown |
type | string | Data type: 'string', 'number', 'date', 'boolean' |
format | string | Format pattern (e.g., 'dd/MM/yyyy' for dates) |
values | string[] | Array of values for boolean or enum types |
Complete Column Example
const columnData: ColumnsModel[] = [
{
field: 'EmployeeID',
label: 'Employee ID',
type: 'number'
},
{
field: 'FirstName',
label: 'First Name',
type: 'string'
},
{
field: 'TitleOfCourtesy',
label: 'Title Of Courtesy',
type: 'boolean',
values: ['Mr.', 'Mrs.', 'Ms.']
},
{
field: 'HireDate',
label: 'Hire Date',
type: 'date',
format: 'dd/MM/yyyy'
},
{
field: 'Country',
label: 'Country',
type: 'string'
},
{
field: 'City',
label: 'City',
type: 'string'
}
];Auto-Generation of Columns
If you don't provide columns explicitly, the Query Builder can automatically generate them from your data source:
import { employeeData } from './datasource';
function App() {
return (
<QueryBuilderComponent width="100%" dataSource={employeeData} />
);
}How It Works: The component infers the column type from the first record in the data source. This is useful for quick prototyping but manual column definition is recommended for production apps.
Running Your App
Development Server
Start the development server using Vite or Create React App:
With Vite:
npm run devWith Create React App:
npm startThe application opens in your browser (usually http://localhost:5173 for Vite or http://localhost:3000 for CRA).
What You See
You should see a Query Builder interface with:
- A Field dropdown for selecting which column to filter
- An Operator dropdown for choosing the comparison type
- A Value input for entering the filter value
- Add Rule and Add Group buttons (if enabled)
- Delete buttons for removing conditions
First Test
1. Open the application in your browser 2. Click the Field dropdown and select a column (e.g., "EmployeeID") 3. Leave the operator as "equal" 4. Enter a value (e.g., "1001") 5. Click Add Rule to add another condition 6. Notice the AND/OR toggle between conditions
Next Steps
- Configure Data: Bind actual data using the
dataSourceproperty - Handle Changes: Add event listeners with the
changeevent - Extract Results: Use
getSqlFromRules()to get the filter as SQL - Customize: Add templates and styling for your UI
For more details, see:
- Columns and Operators for advanced column configuration
- Data Binding for connecting to data sources
- Rules and Filtering for managing complex queries
Query Conversion
Learn how to convert Query Builder rules to SQL, Mongo queries, and parameterized queries for backend execution.
Table of Contents
- Overview
- SQL Generation
- Mongo Query Conversion
- Parameterized Queries
- Predicate Conversion
- Importing from SQL
- Localization
Overview
The Query Builder provides multiple methods to convert rules into backend-compatible query formats:
| Method | Output | Use Case |
|---|---|---|
getSqlFromRules() | SQL WHERE clause | SQL databases |
getMongoQuery() | Mongo query object | MongoDB |
getParameterizedSql() | SQL with placeholders | SQL injection prevention |
getParameterizedNamedSql() | SQL with named parameters | Named parameter binding |
getPredicate() | DataManager predicate | Local filtering |
SQL Generation
Basic SQL Query
Convert rules to SQL WHERE clause:
let qryBldrObj: QueryBuilderComponent;
function generateSQL(): void {
const sqlQuery = qryBldrObj.getSqlFromRules();
console.log('SQL Query:', sqlQuery);
}
// Example output:
// (EmployeeID = 1001) AND (Title LIKE '%Manager%')Complete SQL Example
import { QueryBuilderComponent, RuleModel } from '@syncfusion/ej2-react-querybuilder';
import React from 'react';
function App() {
let qryBldrObj: QueryBuilderComponent;
const rule: RuleModel = {
condition: 'and',
rules: [
{
field: 'EmployeeID',
label: 'Employee ID',
operator: 'equal',
type: 'number',
value: 1001
},
{
field: 'Title',
label: 'Title',
operator: 'contains',
type: 'string',
value: 'Manager'
}
]
};
function generateSQL(): void {
const sqlQuery = qryBldrObj.getSqlFromRules();
console.log('Generated SQL:', sqlQuery);
// Output: (EmployeeID = 1001) AND (Title LIKE '%Manager%')
// Send to backend
fetch('/api/query', {
method: 'POST',
body: JSON.stringify({ query: sqlQuery })
});
}
return (
<div>
<QueryBuilderComponent
columns={columns}
rule={rule}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>
<button onClick={generateSQL}>Execute Query</button>
</div>
);
}
export default App;SQL Operators Mapping
| Operator | SQL Output |
|---|---|
equal | = value |
notequal | != value |
contains | LIKE '%value%' |
startswith | LIKE 'value%' |
endswith | LIKE '%value' |
greaterthan | > value |
lessthan | < value |
greaterthanorequal | >= value |
lessthanorequal | <= value |
between | BETWEEN value1 AND value2 |
in | IN (value1, value2) |
Escaping Special Characters
Exclude escape characters from SQL:
function generateSQL(): void {
const sqlQuery = qryBldrObj.getSqlFromRules(undefined, true);
console.log('SQL without escape:', sqlQuery);
// Special characters like ' and " are not escaped
}Mongo Query Conversion
Generate Mongo Query
Convert rules to MongoDB query format:
let qryBldrObj: QueryBuilderComponent;
function generateMongoQuery(): void {
const mongoQuery = qryBldrObj.getMongoQuery();
console.log('Mongo Query:', mongoQuery);
}
// Example output:
// { $and: [ { EmployeeID: 1001 }, { Title: { $regex: 'Manager' } } ] }Complete Mongo Example
function App() {
let qryBldrObj: QueryBuilderComponent;
const rule: RuleModel = {
condition: 'and',
rules: [
{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
},
{
field: 'Salary',
label: 'Salary',
operator: 'greaterthan',
type: 'number',
value: 50000
}
]
};
function getMongoFilter(): void {
const mongoQuery = qryBldrObj.getMongoQuery();
console.log('Mongo Query:', mongoQuery);
// Output: { $and: [ { Country: 'USA' }, { Salary: { $gt: 50000 } } ] }
// Send to backend MongoDB API
fetch('/api/mongo-query', {
method: 'POST',
body: JSON.stringify(mongoQuery)
});
}
return (
<div>
<QueryBuilderComponent
columns={columns}
rule={rule}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>
<button onClick={getMongoFilter}>Get Mongo Query</button>
</div>
);
}
export default App;Mongo Operators Mapping
| Operator | Mongo Output |
|---|---|
equal | field: value |
notequal | { $ne: value } |
contains | { $regex: value } |
greaterthan | { $gt: value } |
lessthan | { $lt: value } |
between | { $gte: value1, $lte: value2 } |
in | { $in: [values] } |
notin | { $nin: [values] } |
Parameterized Queries
Parameter SQL (Positional)
Generate SQL with placeholders to prevent SQL injection:
function getParameterizedQuery(): void {
const paramSql = qryBldrObj.getParameterizedSql();
console.log('Query:', paramSql.query);
console.log('Parameters:', paramSql.params);
}
// Example output:
// query: "(EmployeeID = ?) AND (Title LIKE ?)"
// params: [1001, '%Manager%']Complete Parameterized Example
function App() {
let qryBldrObj: QueryBuilderComponent;
function getSecureQuery(): void {
const { query, params } = qryBldrObj.getParameterizedSql();
// Send to backend for parameterized query execution
fetch('/api/execute-query', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, params })
})
.then(res => res.json())
.then(data => console.log('Results:', data));
}
return (
<div>
<QueryBuilderComponent
columns={columns}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>
<button onClick={getSecureQuery}>Execute Secure Query</button>
</div>
);
}Named Parameter SQL
Generate SQL with named parameters:
function getNamedParameterQuery(): void {
const paramSql = qryBldrObj.getParameterizedNamedSql();
console.log('Query:', paramSql.query);
console.log('Parameters:', paramSql.params);
}
// Example output:
// query: "(EmployeeID = @EmployeeID_1) AND (Title LIKE @Title_1)"
// params: { '@EmployeeID_1': 1001, '@Title_1': '%Manager%' }Backend Execution (C# Example)
[HttpPost]
public IActionResult ExecuteQuery([FromBody] QueryRequest request)
{
using (var connection = new SqlConnection(connectionString))
{
var command = new SqlCommand(request.Query, connection);
// Add named parameters
foreach (var param in request.Params)
{
command.Parameters.AddWithValue(param.Key, param.Value);
}
connection.Open();
var reader = command.ExecuteReader();
// Process results
}
}Predicate Conversion
Getting DataManager Predicate
Convert rules to a predicate for DataManager filtering:
import { Query } from '@syncfusion/ej2-data';
let qryBldrObj: QueryBuilderComponent;
function filterWithPredicate(): void {
const predicate = qryBldrObj.getPredicate(qryBldrObj.rule);
const query = new Query().where(predicate);
// Apply to DataManager
const dataManager = new DataManager(employeeData);
const filteredData = dataManager.executeQuery(query);
}Filtering Local Data
function App() {
let qryBldrObj: QueryBuilderComponent;
const dataManager = new DataManager(employeeData);
const rule: RuleModel = {
condition: 'and',
rules: [
{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
}
]
};
function applyFilter(): void {
const predicate = qryBldrObj.getPredicate(rule);
const filteredData = dataManager.executeQuery(new Query().where(predicate));
console.log('Filtered:', filteredData);
}
return (
<div>
<QueryBuilderComponent
dataSource={employeeData}
columns={columns}
rule={rule}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>
<button onClick={applyFilter}>Filter Data</button>
</div>
);
}Importing from SQL
Parse SQL to Rules
Convert SQL WHERE clause to Query Builder rules:
function importFromSQL(): void {
const sqlString = "EmployeeID = 1001 AND Title LIKE '%Manager%'";
qryBldrObj.setRulesFromSql(sqlString);
}Complete Import Example
function App() {
let qryBldrObj: QueryBuilderComponent;
const savedSqlQuery = "(Country = 'USA') AND (Salary > 50000)";
function loadSavedQuery(): void {
qryBldrObj.setRulesFromSql(savedSqlQuery);
}
return (
<div>
<button onClick={loadSavedQuery}>Load Saved Query</button>
<QueryBuilderComponent
columns={columns}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>
</div>
);
}Get Rules from SQL
function parseSQL(): void {
const sqlString = "EmployeeID = 1001 AND Country = 'USA'";
const rules = qryBldrObj.getRulesFromSql(sqlString);
console.log('Parsed Rules:', rules);
}Localization
SQL Localization
Generate localized SQL queries:
function getLocalizedSQL(): void {
const sqlQuery = qryBldrObj.getSqlFromRules(undefined, false, true);
// true = enable SQL localization
}Mongo Localization
function getLocalizedMongo(): void {
const mongoQuery = qryBldrObj.getMongoQuery(undefined, true);
// true = enable Mongo localization
}Parameterized SQL Localization
function getLocalizedParameterizedSQL(): void {
const { query, params } = qryBldrObj.getParameterizedSql(undefined, true);
// true = enable localization
}Complete Workflow Example
import { ColumnsModel, QueryBuilderComponent, RuleModel } from '@syncfusion/ej2-react-querybuilder';
import React from 'react';
function App() {
let qryBldrObj: QueryBuilderComponent;
const columns: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number' },
{ field: 'Country', label: 'Country', type: 'string' },
{ field: 'Salary', label: 'Salary', type: 'number' }
];
function exportToSQL(): void {
const sql = qryBldrObj.getSqlFromRules();
console.log('SQL:', sql);
}
function exportToMongo(): void {
const mongo = qryBldrObj.getMongoQuery();
console.log('Mongo:', mongo);
}
function exportParameterized(): void {
const { query, params } = qryBldrObj.getParameterizedSql();
console.log('Query:', query);
console.log('Params:', params);
}
function importFromSQL(): void {
const sql = "(Country = 'USA') AND (Salary > 50000)";
qryBldrObj.setRulesFromSql(sql);
}
return (
<div>
<QueryBuilderComponent
width="100%"
columns={columns}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>
<div>
<button onClick={exportToSQL}>Export as SQL</button>
<button onClick={exportToMongo}>Export as Mongo</button>
<button onClick={exportParameterized}>Export Parameterized</button>
<button onClick={importFromSQL}>Import from SQL</button>
</div>
</div>
);
}
export default App;This example demonstrates:
- Multiple export formats
- Parameterized query generation
- SQL import capability
- Complete workflow integration
Rules and Filtering
Learn how to create, manage, and manipulate filter rules and groups programmatically, including drag-and-drop support and button configuration.
Table of Contents
- Understanding Rule Structure
- Creating Rules
- Managing Rules
- Creating Groups
- Managing Groups
- Nested Hierarchies
- Show Buttons Configuration
- Drag and Drop
Understanding Rule Structure
Rules represent individual filter conditions. The RuleModel interface defines the structure:
interface RuleModel {
condition?: string; // 'and' or 'or' (for groups)
rules?: RuleModel[]; // Child rules (for groups)
field?: string; // Column field name
label?: string; // Display label
operator?: string; // Operator: 'equal', 'contains', etc.
type?: string; // Data type
value?: any; // Filter value
not?: boolean; // NOT condition (optional)
}Simple Rule
const simpleRule: RuleModel = {
field: 'EmployeeID',
label: 'Employee ID',
operator: 'equal',
type: 'number',
value: 1001
};Rule with NOT Condition
const notRule: RuleModel = {
field: 'Status',
label: 'Status',
operator: 'equal',
type: 'string',
value: 'Inactive',
not: true // NOT Status = 'Inactive'
};Creating Rules
Initial Rules on Load
Set initial rules with the rule property:
import { QueryBuilderComponent, RuleModel } from '@syncfusion/ej2-react-querybuilder';
import React from 'react';
function App() {
const initialRule: RuleModel = {
condition: 'and',
rules: [
{
field: 'EmployeeID',
label: 'Employee ID',
operator: 'equal',
type: 'number',
value: 1001
},
{
field: 'Title',
label: 'Title',
operator: 'equal',
type: 'string',
value: 'Sales Manager'
}
]
};
return (
<QueryBuilderComponent
width="100%"
columns={columns}
rule={initialRule}
/>
);
}
export default App;Programmatic Rule Creation
Add rules at runtime using the addRules method:
let qryBldrObj: QueryBuilderComponent;
function addNewRule(): void {
const newRule: RuleModel = {
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
};
qryBldrObj.addRules([newRule], 'group0');
}
return (
<div>
<QueryBuilderComponent
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
columns={columns}
rule={initialRule}
/>
<button onClick={addNewRule}>Add Country Filter</button>
</div>
);Adding Multiple Rules
function addMultipleRules(): void {
const newRules: RuleModel[] = [
{
field: 'EmployeeID',
label: 'Employee ID',
operator: 'greaterthan',
type: 'number',
value: 1000
},
{
field: 'Title',
label: 'Title',
operator: 'contains',
type: 'string',
value: 'Manager'
}
];
qryBldrObj.addRules(newRules, 'group0');
}Managing Rules
Retrieving Rules
Get the current rule set:
function getRulesData(): void {
const rules = qryBldrObj.getRules();
console.log('Current rules:', rules);
}Getting a Single Rule
function getSingleRule(ruleId: string): void {
const rule = qryBldrObj.getRule(ruleId);
console.log('Rule:', rule);
}Setting Rules
Replace all rules with new ones:
function updateRules(): void {
const newRules: RuleModel = {
condition: 'and',
rules: [
{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
}
]
};
qryBldrObj.setRules(newRules);
}Deleting Rules
Remove individual rules by ID:
function deleteRule(ruleId: string): void {
qryBldrObj.deleteRules([ruleId]);
}
function deleteMultipleRules(ruleIds: string[]): void {
qryBldrObj.deleteRules(ruleIds);
}Cloning Rules
Create a copy of a rule:
function cloneExistingRule(ruleId: string): void {
// Clones ruleId and adds to group0
qryBldrObj.cloneRule(ruleId, 'group0', 0);
}Parameters:
ruleID- ID of the rule to clonegroupID- Target group for the cloned ruleindex- Position in the group (optional)
Creating Groups
Groups combine multiple rules with a condition (AND/OR). Creating a group:
function addNewGroup(): void {
const newGroup: RuleModel = {
condition: 'and',
rules: [
{
field: 'FirstName',
label: 'First Name',
operator: 'startswith',
type: 'string',
value: 'v'
}
]
};
qryBldrObj.addGroups([newGroup], 'group0');
}Initial Group Structure
const initialRule: RuleModel = {
condition: 'and',
rules: [
{
field: 'EmployeeID',
label: 'Employee ID',
operator: 'equal',
type: 'number',
value: 1001
},
{
condition: 'or', // Nested group
rules: [
{
field: 'Title',
label: 'Title',
operator: 'equal',
type: 'string',
value: 'Manager'
},
{
field: 'Title',
label: 'Title',
operator: 'equal',
type: 'string',
value: 'Developer'
}
]
}
]
};This creates:
(EmployeeID = 1001) AND (Title = 'Manager' OR Title = 'Developer')Managing Groups
Retrieving Groups
Get a specific group:
function getGroupData(groupId: string): void {
const group = qryBldrObj.getGroup(groupId);
console.log('Group:', group);
}Deleting Groups
Remove groups by ID:
function deleteGroup(groupId: string): void {
qryBldrObj.deleteGroups([groupId]);
}
function deleteMultipleGroups(groupIds: string[]): void {
qryBldrObj.deleteGroups(groupIds);
}Cloning Groups
Create a copy of a group:
function cloneExistingGroup(groupId: string): void {
qryBldrObj.cloneGroup(groupId, 'group0', 0);
}Parameters:
groupID- ID of the group to cloneparentGroupID- Target parent groupindex- Position in parent (optional)
Locking Groups
Make groups read-only:
function lockGroup(groupId: string): void {
qryBldrObj.lockGroup(groupId);
// Users cannot edit or delete this group
}Nested Hierarchies
Creating Complex Nested Rules
const complexRule: RuleModel = {
condition: 'and',
rules: [
// Simple rule
{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
},
// OR Group with nested rules
{
condition: 'or',
rules: [
{
field: 'Title',
label: 'Title',
operator: 'equal',
type: 'string',
value: 'Manager'
},
{
field: 'Title',
label: 'Title',
operator: 'equal',
type: 'string',
value: 'Developer'
}
]
},
// Another AND Group
{
condition: 'and',
rules: [
{
field: 'Salary',
label: 'Salary',
operator: 'greaterthan',
type: 'number',
value: 50000
},
{
field: 'YearsWorked',
label: 'Years Worked',
operator: 'greaterthanorequal',
type: 'number',
value: 5
}
]
}
]
};
// Result: (Country = 'USA') AND (Title = 'Manager' OR Title = 'Developer') AND (Salary > 50000 AND YearsWorked >= 5)Max Group Depth
Limit nesting with maxGroupCount:
<QueryBuilderComponent
columns={columns}
maxGroupCount={3} // Maximum 3 levels of nesting
/>Show Buttons Configuration
Control which buttons appear in the Query Builder:
import { ShowButtonsModel } from '@syncfusion/ej2-react-querybuilder';
const buttonOptions: ShowButtonsModel = {
ruleDelete: true, // Show delete button for rules
groupInsert: true, // Show add group button
groupDelete: true // Show delete button for groups
};
<QueryBuilderComponent
columns={columns}
showButtons={buttonOptions}
/>Enabling All Buttons
const buttonOptions: ShowButtonsModel = {
ruleDelete: true,
groupInsert: true,
groupDelete: true
};Hiding Delete Buttons
const buttonOptions: ShowButtonsModel = {
ruleDelete: false,
groupInsert: true,
groupDelete: false
};Dynamic Button Control
let qryBldrObj: QueryBuilderComponent;
function updateButtons(): void {
qryBldrObj.showButtons = {
ruleDelete: true,
groupInsert: true,
groupDelete: true
};
}Drag and Drop
Enable drag-and-drop for reordering rules and groups:
<QueryBuilderComponent
columns={columns}
allowDragAndDrop={true}
/>Drag Event Handling
function onDragStart(args: DragEventArgs): void {
console.log('Dragging:', args.rule);
}
function onDrag(args: DragEventArgs): void {
console.log('During drag:', args);
}
function onDrop(args: DropEventArgs): void {
console.log('Dropped at:', args.targetID);
}
<QueryBuilderComponent
allowDragAndDrop={true}
dragStart={onDragStart}
drag={onDrag}
drop={onDrop}
/>Complete Example
import { ColumnsModel, QueryBuilderComponent, RuleModel, ShowButtonsModel } from '@syncfusion/ej2-react-querybuilder';
import React from 'react';
function App() {
let qryBldrObj: QueryBuilderComponent;
const columns: ColumnsModel[] = [
{ field: 'EmployeeID', label: 'Employee ID', type: 'number' },
{ field: 'FirstName', label: 'First Name', type: 'string' },
{ field: 'Title', label: 'Title', type: 'string' },
{ field: 'Country', label: 'Country', type: 'string' }
];
const initialRule: RuleModel = {
condition: 'and',
rules: [
{
field: 'Country',
label: 'Country',
operator: 'equal',
type: 'string',
value: 'USA'
}
]
};
const buttonOptions: ShowButtonsModel = {
ruleDelete: true,
groupInsert: true,
groupDelete: true
};
function addRule(): void {
qryBldrObj.addRules([
{
field: 'Title',
label: 'Title',
operator: 'contains',
type: 'string',
value: 'Manager'
}
], 'group0');
}
function getRules(): void {
const rules = qryBldrObj.getRules();
console.log('Current Rules:', rules);
}
function resetRules(): void {
qryBldrObj.reset();
}
return (
<div>
<QueryBuilderComponent
width="100%"
columns={columns}
rule={initialRule}
showButtons={buttonOptions}
allowDragAndDrop={true}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>
<div>
<button onClick={addRule}>Add Title Filter</button>
<button onClick={getRules}>Get Rules</button>
<button onClick={resetRules}>Reset Filters</button>
</div>
</div>
);
}
export default App;This example demonstrates:
- Initial rule with nested structure
- Controlled button visibility
- Drag-and-drop support
- Programmatic rule management
Templates and Customization
Learn how to customize the Query Builder with templates, styling, themes, and custom components.
Table of Contents
- Overview
- Header Templates
- CSS Class Styling
- Theme Studio Integration
- Custom Operators
- Event-Based Customization
Overview
The Query Builder offers multiple ways to customize appearance and behavior:
- Header Templates - Custom components in the header area
- CSS Styling - Override default styles
- Theme Studio - Create custom themes visually
- Custom Operators - Add domain-specific operators
- Event Handlers - Respond to user actions
Header Templates
Basic Header Template
Use the headerTemplate property to create custom headers:
import { QueryBuilderComponent } from '@syncfusion/ej2-react-querybuilder';
import React from 'react';
function CustomHeader(props: any) {
return (
<div className="custom-header">
<span>{props.condition}</span>
<button>Custom Action</button>
</div>
);
}
function App() {
return (
<QueryBuilderComponent
columns={columns}
headerTemplate={(props) => <CustomHeader {...props} />}
/>
);
}
export default App;Header Template with Condition Control
import { DropDownListComponent } from '@syncfusion/ej2-react-dropdowns';
import { CheckBoxComponent } from '@syncfusion/ej2-react-buttons';
function AdvancedHeader(props: any) {
const handleConditionChange = (e: any) => {
props.condition = e.value;
};
const handleNotChange = (e: any) => {
props.not = e.checked;
};
return (
<div className="e-custom-header">
<DropDownListComponent
dataSource={['AND', 'OR']}
value={props.condition}
change={handleConditionChange}
/>
<CheckBoxComponent
checked={props.not}
change={handleNotChange}
label="NOT"
/>
</div>
);
}
<QueryBuilderComponent
columns={columns}
headerTemplate={(props) => <AdvancedHeader {...props} />}
/>Event-Based Template Creation
function App() {
let qryBldrObj: QueryBuilderComponent;
function onActionBegin(args: any) {
if (args.requestType === 'header-template-create') {
const element = document.createElement('div');
element.innerHTML = `
<div class="custom-header">
<span>Custom Condition: ${args.rule?.condition}</span>
</div>
`;
args.element = element;
}
}
return (
<QueryBuilderComponent
columns={columns}
actionBegin={onActionBegin}
ref={(scope) => { qryBldrObj = scope as QueryBuilderComponent; }}
/>
);
}CSS Class Styling
Available CSS Classes
| CSS Class | Target |
|---|---|
.e-query-builder | Main container |
.e-group-header | Group header area |
.e-group-body | Group body area |
.e-rule-container | Individual rule container |
.e-group-container | Group container |
.e-btn | Condition button (AND/OR) |
.e-dropdown-btn | Add Group/Condition button |
.e-deletegroup | Delete Group button |
.e-rule-delete | Delete Condition button |
.e-rule-list | Rule list container |
.e-rule-list > ::after | Group joining lines |
.e-rule-container.e-joined-rule | Condition joining lines |
Custom Styling Example
/* Main Query Builder */
.e-query-builder {
background-color: #f5f5f5;
border-radius: 8px;
padding: 16px;
}
/* Condition button (AND/OR) */
.e-group-header .e-btn {
background-color: #2196F3;
border: 1px solid #1976D2;
color: white;
font-weight: bold;
border-radius: 4px;
}
.e-group-header .e-btn:hover {
background-color: #1976D2;
}
/* Delete buttons */
.e-query-builder .e-rule-delete,
.e-query-builder .e-deletegroup {
background-color: #f44336;
border-color: #d32f2f;
}
.e-query-builder .e-rule-delete:hover,
.e-query-builder .e-deletegroup:hover {
background-color: #d32f2f;
}
/* Group joining lines */
.e-query-builder .e-rule-list > ::before,
.e-query-builder .e-rule-list > ::after {
border-color: #ccc;
}
.e-query-builder .e-rule-container.e-joined-rule {
border-left: 2px solid #2196F3;
}Applying Custom Styles
import './custom-styles.css';
<QueryBuilderComponent
columns={columns}
cssClass="custom-query-builder"
/>Dynamic CSS Class Application
<QueryBuilderComponent
columns={columns}
cssClass={isDarkMode ? 'qb-dark-theme' : 'qb-light-theme'}
/>Theme Studio Integration
Available Built-in Themes
The Query Builder supports several built-in themes:
- Material - Material Design
- Bootstrap - Bootstrap 5
- Bootstrap4 - Bootstrap 4
- Fabric - Microsoft Fabric
- High Contrast - Accessibility focused
- Tailwind - Tailwind CSS
Using Built-in Themes
Import the CSS file in your main style:
/* Material Theme */
@import "@syncfusion/ej2-querybuilder/styles/material.css";
/* Bootstrap Theme */
@import "@syncfusion/ej2-querybuilder/styles/bootstrap.css";
/* Tailwind Theme */
@import "@syncfusion/ej2-querybuilder/styles/tailwind3.css";Creating Custom Themes with Theme Studio
1. Visit Theme Studio 2. Customize colors and styling 3. Download the custom theme CSS 4. Import in your project
@import "./custom-theme.css";CSS Variables for Theming
Override theme colors using CSS variables:
:root {
--qb-primary-color: #2196F3;
--qb-text-color: #333;
--qb-border-color: #ccc;
--qb-button-bg: #f5f5f5;
--qb-hover-bg: #e0e0e0;
}
.e-query-builder {
--qb-primary-color: var(--qb-primary-color);
}Custom Operators
Adding Custom Operators
const columns: ColumnsModel[] = [
{
field: 'Status',
label: 'Status',
type: 'string',
operators: [
{ key: 'Equal', value: 'equal' },
{ key: 'Not Equal', value: 'notequal' },
{ key: 'Is Active', value: 'isactive' }, // Custom
{ key: 'Is Inactive', value: 'isinactive' } // Custom
]
}
];Handling Custom Operators
function onActionBegin(args: ActionEventArgs): void {
if (args.requestType === 'rule-change') {
const rule = args.rule;
if (rule?.operator === 'isactive') {
rule.value = 'Active';
rule.operator = 'equal';
} else if (rule?.operator === 'isinactive') {
rule.value = 'Inactive';
rule.operator = 'equal';
}
}
}
<QueryBuilderComponent
columns={columns}
actionBegin={onActionBegin}
/>Custom Operator with Special Logic
function onActionBegin(args: ActionEventArgs): void {
if (args.requestType === 'rule-change' && args.rule?.operator === 'custom-range') {
// Handle custom range operator
if (args.rule.value && typeof args.rule.value === 'string') {
const [min, max] = args.rule.value.split('-');
args.rule.value = { min: parseInt(min), max: parseInt(max) };
}
}
}
<QueryBuilderComponent
columns={columns}
actionBegin={onActionBegin}
/>Event-Based Customization
ActionBegin Event
Responds to user actions like field/operator/value changes:
function onActionBegin(args: ActionEventArgs): void {
console.log('Action Type:', args.requestType);
switch (args.requestType) {
case 'rule-change':
console.log('Rule changed:', args.rule);
break;
case 'condition-change':
console.log('Condition changed:', args.condition);
break;
case 'header-template-create':
console.log('Header template created');
break;
}
}
<QueryBuilderComponent
columns={columns}
actionBegin={onActionBegin}
/>Change Event
Triggers when rules are modified:
function onChange(args: ChangeEventArgs): void {
console.log('Rules changed:', args.rule);
console.log('Parent ID:', args.groupID);
}
<QueryBuilderComponent
columns={columns}
change={onChange}
/>BeforeChange Event
Triggers before changes are applied:
function onBeforeChange(args: ChangeEventArgs): void {
// Prevent certain changes
if (args.rule?.operator === 'forbidden') {
args.cancel = true;
console.log('This operator is not allowed');
}
}
<QueryBuilderComponent
columns={columns}
beforeChange={onBeforeChange}
/>RuleChange Event
Triggers when rules change with detailed information:
function onRuleChange(args: RuleChangeEventArgs): void {
console.log('Previous value:', args.previousRule);
console.log('New value:', args.rule);
console.log('Parent group:', args.groupID);
}
<QueryBuilderComponent
columns={columns}
ruleChange={onRuleChange}
/>Complete Customization Example
import { ColumnsModel, QueryBuilderComponent, RuleModel } from '@syncfusion/ej2-react-querybuilder';
import React from 'react';
import './custom-styles.css';
function App() {
const columns: ColumnsModel[] = [
{
field: 'EmployeeID',
label: 'Employee ID',
type: 'number'
},
{
field: 'Status',
label: 'Status',
type: 'string',
operators: [
{ key: 'Equal', value: 'equal' },
{ key: 'Is Active', value: 'isactive' }
]
}
];
function customHeader(props: any) {
return (
<div className="custom-qb-header">
<strong>{props.condition?.toUpperCase()}</strong>
<span className="rule-count">
{props.ruleCount || 0} conditions
</span>
</div>
);
}
function onActionBegin(args: any): void {
if (args.requestType === 'rule-change') {
if (args.rule?.operator === 'isactive') {
args.rule.value = 'Active';
args.rule.operator = 'equal';
}
}
}
function onChange(args: any): void {
console.log('Updated rules:', args.rule);
}
return (
<div className="app-container">
<QueryBuilderComponent
width="100%"
columns={columns}
cssClass="custom-query-builder"
headerTemplate={customHeader}
actionBegin={onActionBegin}
change={onChange}
/>
</div>
);
}
export default App;CSS:
.app-container {
padding: 20px;
}
.custom-query-builder {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 10px;
padding: 20px;
}
.custom-query-builder .e-group-header .e-btn {
background-color: #667eea;
border: none;
color: white;
font-weight: bold;
}
.custom-qb-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background-color: rgba(255, 255, 255, 0.1);
border-radius: 4px;
}
.rule-count {
background-color: #764ba2;
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
}This example demonstrates:
- Custom header template
- Custom CSS styling
- Custom operators with logic
- Event-based customization
- Complete theme integration
Related skills
How it compares
Pick syncfusion-react-query-builder over hand-rolled filter forms when you need visual nested rules with official SQL and Mongo export inside Syncfusion EJ2.
FAQ
What npm package does syncfusion-react-query-builder use?
syncfusion-react-query-builder targets @syncfusion/ej2-react-querybuilder, installed with npm install @syncfusion/ej2-react-querybuilder --save, importing QueryBuilderComponent and ColumnsModel from that package.
How does syncfusion-react-query-builder export filters to SQL?
syncfusion-react-query-builder documents calling getSqlFromRules() on a QueryBuilderComponent ref to serialize nested RuleModel groups into SQL, with setRulesFromSql for importing existing filter strings.
How many reference guides ship with the query builder skill?
syncfusion-react-query-builder bundles 7 reference markdown files covering getting started, columns and operators, data binding, rules and filtering, query conversion, templates, advanced features, and API reference.