An open API service indexing awesome lists of open source software.

https://github.com/nandhakumare/react-querybuilder-lite

Headless React query builder with compound components, render props, TypeScript & drag-and-drop. Works with any design system.
https://github.com/nandhakumare/react-querybuilder-lite

compound-components drag-and-drop filter-builder headless-ui query-builder react react-hooks render-props typescript

Last synced: 5 months ago
JSON representation

Headless React query builder with compound components, render props, TypeScript & drag-and-drop. Works with any design system.

Awesome Lists containing this project

README

          

# react-querybuilder-lite

[![npm version](https://img.shields.io/npm/v/react-querybuilder-lite.svg)](https://www.npmjs.com/package/react-querybuilder-lite)
![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)
![React](https://img.shields.io/badge/React-16.8%2B-61dafb)
[![License](https://img.shields.io/npm/l/react-querybuilder-lite)](https://github.com/NandhakumarE/react-visual-querybuilder/blob/main/LICENSE)
[![Storybook](https://img.shields.io/badge/Storybook-Live%20Demo-ff4785)](https://nandhakumare.github.io/react-querybuilder-lite/)
[![CodeSandbox](https://img.shields.io/badge/CodeSandbox-Try%20It-000?logo=codesandbox)](https://codesandbox.io/p/sandbox/hmkk77)

A lightweight, headless React query builder with drag-and-drop support. Build complex filter UIs with any design system — zero styling opinions.

## Why This Library?

Most query builders ship with opinionated styles or are tightly coupled to specific UI libraries. This library provides:

- **Complete UI freedom** — Use MUI, Chakra, Ant Design, Tailwind, or vanilla HTML
- **Inversion of control** — You own the markup, we handle the logic
- **Type safety** — Full TypeScript inference for queries, operators, and fields
- **Lightweight** — ~18KB minified + gzipped, including drag-and-drop support

## Features

| Feature | Description |
|---------|-------------|
| Headless | No styles, no markup — bring your own components |
| Compound Components | Clean `` composition API |
| Render Props | Full control via `renderRule` and `renderGroup` |
| Drag & Drop | Optional reordering via dnd-kit integration |
| Immutable Updates | Predictable state with structural sharing |
| Type Inference | Operators auto-filter based on field type |
| Nested Groups | Recursive AND/OR groups with `maxDepth` control |
| Lock Protection | Prevent modification of locked rules/groups |
| Slot Actions | Pre-wired handlers for add, remove, clone, lock |

## Installation

```bash
npm install react-querybuilder-lite
```

```bash
yarn add react-querybuilder-lite
```

```bash
pnpm add react-querybuilder-lite
```

**Peer Dependencies:** React 16.8+

## Quick Start

```tsx
import { useState } from 'react';
import { QueryBuilder, type Query } from 'react-querybuilder-lite';

const fields = [
{ label: 'Name', value: 'name', type: 'string' },
{ label: 'Age', value: 'age', type: 'number' },
];

const initialQuery: Query = {
id: 'root',
combinator: 'and',
rules: [],
};

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

return (

(


onChange({ field: e.target.value })}
>
Select field
{fields.map((f) => (
{f.label}
))}

onChange({ operator: e.target.value })}
>
{operators.map((op) => (
{op.name}
))}

onChange({ value: e.target.value })}
/>

Remove


)}
renderGroup={({ group, children, onChange, slots }) => (

onChange({ combinator: e.target.value })}
>
AND
OR

+ Rule
+ Group

{children}


)}
/>

);
}
```

## With Drag & Drop

Use `QueryBuilder.BuilderWithDnD` to enable drag-and-drop reordering.

```tsx
import { QueryBuilder, type Query } from 'react-querybuilder-lite';

(


{/* Drag handle - spread slots.dragHandles on any element */}

onChange({ field: e.target.value })}>
{fields.map((f) => {f.label})}

{/* ... rest of your UI */}


)}
renderGroup={({ group, children, onChange, slots }) => (

onChange({ combinator: e.target.value })}>
AND
OR

+ Rule
+ Group

{children}


)}
/>

```

## API Reference

### ``

Root component that provides state management context.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `Query` | Yes | The query state |
| `onChange` | `(query: Query) => void` | Yes | Called when query changes |
| `maxDepth` | `number` | No | Maximum nesting depth. `1` = no nesting, `2` = one level, etc. |
| `children` | `ReactNode` | Yes | Must contain `Builder` or `BuilderWithDnD` |

### ``

Renders the query tree without drag-and-drop.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `fields` | `Field[]` | Yes | Available fields for rules |
| `renderRule` | `(props: RuleRenderProps) => ReactNode` | Yes | Render function for rules |
| `renderGroup` | `(props: GroupRenderProps) => ReactNode` | Yes | Render function for groups |
| `operatorsByFieldType` | `Record` | No | Custom operator mapping |

### ``

Same props as `Builder`, plus optional drag preview customization.

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `renderDragPreview` | `(props: DragPreviewProps) => ReactNode` | No | Custom drag overlay |

### Render Props

#### `RuleRenderProps`

```typescript
interface RuleRenderProps {
rule: Rule; // Current rule data
path: number[]; // Position in tree (e.g., [0, 1])
depth: number; // Nesting level
fields: Field[]; // Available fields
operators: Operator[]; // Operators for selected field type
selectedField?: Field; // Currently selected field
selectedOperator?: Operator; // Currently selected operator
slots: {
onRemove: () => void; // Remove this rule
onClone: () => void; // Duplicate this rule
onToggleLock: () => void; // Toggle lock state
dragHandles: DragHandleType; // Spread on drag handle element
};
onChange: (updates: Partial) => void; // Update rule
}
```

#### `GroupRenderProps`

```typescript
interface GroupRenderProps {
group: RuleGroup; // Current group data
path: number[]; // Position in tree
depth: number; // Nesting level
children: ReactNode; // Rendered child rules/groups
slots: {
onAddRule: () => void; // Add rule to this group
onAddGroup: () => void; // Add nested group
onRemove: () => void; // Remove this group
onClone: () => void; // Duplicate this group
onToggleLock: () => void; // Toggle lock state
dragHandles: DragHandleType; // Spread on drag handle element
};
onChange: (updates: Partial) => void; // Update group
}
```

## Terminology

| Term | Description |
|------|-------------|
| **Field** (or Column) | The data attribute you want to filter on. For example, "First Name", "Age", "Created Date" are fields. |
| **Operator** | The comparison operation like "equals", "contains", "greater than". |
| **Field Type** | Category of the field that determines available operators. Default types: `string`, `number`, `boolean`, `date`. You can define custom types. |
| **Combinator** | Logical operator to combine rules: `AND` or `OR`. |

## Types

### Core Types

```typescript
// The root query structure
type Query = RuleGroup;

interface RuleGroup {
id: string;
combinator: 'and' | 'or';
rules: Array;
isLocked?: boolean;
}

interface Rule {
id: string;
field: string;
operator: OperatorKey;
value?: Value;
isLocked?: boolean;
}

interface Field {
label: string;
value: string;
type: string; // 'string' | 'number' | 'boolean' | 'date' or any custom type
}
```

### Operators

Built-in operators organized by type:

| Type | Operators |
|------|-----------|
| Unary | `is_empty`, `is_not_empty`, `is_true`, `is_false` |
| Binary | `equal`, `not_equal`, `less`, `less_or_equal`, `greater`, `greater_or_equal`, `contains`, `starts_with`, `ends_with` |
| Range | `between`, `not_between` |
| List | `in`, `not_in` |

Operators are automatically filtered by field type:

| Field Type | Available Operators |
|------------|---------------------|
| `string` | `is_empty`, `is_not_empty`, `equal`, `not_equal`, `contains`, `starts_with`, `ends_with`, `in`, `not_in` |
| `number` | `is_empty`, `is_not_empty`, `equal`, `not_equal`, `less`, `less_or_equal`, `greater`, `greater_or_equal`, `between`, `not_between`, `in`, `not_in` |
| `boolean` | `is_empty`, `is_not_empty`, `is_true`, `is_false` |
| `date` | `is_empty`, `is_not_empty`, `equal`, `not_equal`, `less`, `greater`, `between`, `not_between`, `in`, `not_in` |

## Custom Field Types

You're not limited to the default field types. Define your own types with custom operators:

```typescript
import { QueryBuilder, type Query, type Operator } from 'react-querybuilder-lite';

// Define fields with custom types
const fields = [
{ label: 'Name', value: 'name', type: 'string' },
{ label: 'Email', value: 'email', type: 'email' }, // Custom type
{ label: 'Created', value: 'createdAt', type: 'datetime' }, // Custom type
{ label: 'Price', value: 'price', type: 'currency' }, // Custom type
];

// Provide operators for your custom types
const operatorsByFieldType: Record = {
string: [
{ name: 'Equals', value: 'equal', type: 'binary' },
{ name: 'Contains', value: 'contains', type: 'binary' },
],
email: [
{ name: 'Is', value: 'equal', type: 'binary' },
{ name: 'Contains', value: 'contains', type: 'binary' },
{ name: 'Ends With', value: 'ends_with', type: 'binary' },
],
datetime: [
{ name: 'Before', value: 'less', type: 'binary' },
{ name: 'After', value: 'greater', type: 'binary' },
{ name: 'Between', value: 'between', type: 'range' },
],
currency: [
{ name: 'Equals', value: 'equal', type: 'binary' },
{ name: 'Greater Than', value: 'greater', type: 'binary' },
{ name: 'Less Than', value: 'less', type: 'binary' },
{ name: 'Between', value: 'between', type: 'range' },
],
};

```

## Localization (i18n)

Full internationalization support. You control all user-facing text:

- **`fields`** — Translated field labels
- **`operatorsByFieldType`** — Translated operator names
- **`renderRule` / `renderGroup`** — Your components, your language. Full control over buttons, placeholders, and combinators
- **`dragDropAccessibility`** — Translated screen reader announcements

See the **[Localization story in Storybook →](https://nandhakumare.github.io/react-querybuilder-lite/?path=/docs/core-localization-i18n--docs)** for examples in Spanish, Japanese, and French. Need another language? Easy to configure refer to the comprehensive documentation in the story.

## Live Demos

**[📚 View Storybook →](https://nandhakumare.github.io/react-querybuilder-lite/)**

Interactive examples showcasing all components with different configurations.

## Design Decisions

| Decision | Rationale |
|----------|-----------|
| Headless architecture | Maximum flexibility, framework agnostic |
| Compound components | Implicit state sharing without prop drilling |
| Path-based operations | O(depth) updates with structural sharing |
| Render props over slots | Full control vs. limited customization |
| Cascading lock state | UX: locked parent = locked children |
| Optional DnD entry point | Respects bundle budgets |

## Contributing

Contributions are welcome! Please open an issue or submit a PR.

## License

MIT