React Query Builder Development Guide
COMMUNICATION STYLE: Be aggressively concise. Prioritize brevity over grammar. Examples:
- "Build failed" not "The build has failed"
- "Fixed type error" not "I have fixed the type error"
- "Run tests" not "I will run the tests for you"
This guide covers React Query Builder development: code style, workflow, and other patterns.
Project Overview
React Query Builder monorepo contains:
- Core package:
@react-querybuilder/core- Non-React utilities, parsers, formatters - Main package:
react-querybuilder- React components and hooks - UI integrations: Ant Design, Bootstrap, Bulma, Chakra UI, Fluent UI, Mantine, MUI, Tremor
- Extensions: Drag-and-drop (
@react-querybuilder/dnd), date/time processing (@react-querybuilder/datetime), React Native (@react-querybuilder/native), expression-related features (@react-querybuilder/expr) - Documentation: Docusaurus website
Development Workflow
Setup
bun install
bun run build
Commands
Development:
bun start- Hot-reload dev server (all packages, Bun server)bun start:rqb- Main package dev (Vite server)bun start:antd,bun start:material, etc. - UI packages (Vite server)
Quality:
bun test ...when React and DOM not involved (much faster than Vitest)bunx vitest run --coverage- Run Vitest tests with 100% coverage checkbun lint- Type-aware linting and typecheckingbun fmt- Format (run after changes)bun check:all- Full CI check (run before submitting a PR)bun typecheck- TypeScript check (usually unnecessary —lintcovers typechecking)
Documentation:
bun web- Serve documentation website locallybun web:skiptypedoc- Skip TypeDoc generation for faster startup
Build
bun run build- All packages (concurrent via Bun CLI filter)bun run build:sequential- Sequential (better for debugging)- Individual packages:
bun build:rqb,bun build:antd, etc.
Code Style
Structure
packages/core/src/ # Non-React utilities
packages/react-querybuilder/src/
├── components/ # React components (PascalCase.tsx)
├── hooks/ # Hooks (useHookName.ts)
├── types/ # TypeScript defs
├── utils/ # Utilities (camelCase.ts)
├── styles/ # SCSS
├── redux/ # Redux
└── barrel.ts # Export aggregator
Naming
- Components: PascalCase (
QueryBuilder.tsx) - Hooks: camelCase with
use(useHookName.ts) - Utilities: camelCase (
generateID.ts) - Types: PascalCase identifiers (
RuleGroupType), camelCase filenames (basic.ts) - Debug versions:
*.debug.ts - Tests:
*.test.ts[x]
TypeScript
- Heavy use of generics with constraints
- Conditional types for API flexibility
- Branded types
- React/non-React type separation (core package enables server usage)
// Generic component with constraints
export interface QueryBuilderProps<
RG extends RuleGroupTypeAny,
F extends FullField,
O extends FullOperator,
C extends FullCombinator,
> {
// Component props
}
// Type-only imports
import type { RuleGroupType } from '../types';
Components
- Composition over inheritance
- Heavy memoization (
React.memo()) - Custom hooks for logic
- Context for state
export const ComponentName = React.memo(function ComponentName(props: PropsType) {
const hookResult = useCustomHook(props);
const memoizedValue = useMemo(() => computation, [dependencies]);
return <div className={clsx(baseClassNames.component, customClass)} />;
});
Imports/Exports
- Use
index.tsfor aggregation barrel.tsfor exports that don't have a "debug" version- React/non-React separation
import * as React from 'react';
import type { ComponentProps } from '../types';
import { generateID, isRuleGroup } from '../utils';
Styling
- SCSS + CSS custom properties
- BEM-like (
.queryBuilder-rule) - SCSS variables for tokens
- Custom
clsxutility for conditional classes
State Management
- Immer for immutable updates
- Path-based updates
[0, 1, 2] - Custom Redux context to avoid prop drilling
Bun APIs
This project runs on Bun. Prefer Bun-native APIs over Node.js equivalents in scripts and utilities:
Bun.file(path).text()/.json()instead offs.readFileSyncBun.write(path, content)instead offs.writeFileSyncBun.spawnSync(...)/Bun.spawn(...)instead ofchild_process.execSync/execBun.serve(...)instead ofhttp.createServer
Only fall back to node:* APIs when no Bun equivalent exists.
Testing
-
Vitest + Testing Library
-
Helpers in
utils/testing/ -
100% coverage required
- Use scoped
bun test:[pkg]for granular coverage checking - Use
bunx vitest run --coverageto test for full coverage
- Use scoped
-
Test files:
ComponentName.test.tsx -
Describe blocks: component/function name
-
Test cases: Descriptive behavior
Database integration tests (dbquery.*)
PostgreSQL dbquery tests use a shared in-process PGlite instance exposed via a loopback socket.
Use getSharedSQL() from @rqb-dbpool (not getSharedPGlite) to obtain a Bun.SQL handle:
import { getSharedSQL, createSchema, dropSchema, reserveSchema } from '@rqb-dbpool';
const schema = reserveSchema('my_test');
beforeAll(async () => {
const db = await createSchema(schema);
await db.exec(setupSQL(schema));
});
afterAll(async () => { await dropSchema(schema); });
test('example', async () => {
const sql = await getSharedSQL();
const rows = await sql.unsafe('SELECT * FROM ...');
expect(rows).toEqual(...);
});
The native getSharedPGlite is marked @internal — only the Drizzle adapter uses it directly.
Bun.SQL is also used for SQLite dbquery tests (new SQL({ adapter: 'sqlite', filename: ':memory:' })).
Generated Files
Never edit:
packages/core/src/utils/parseCEL/celParser.jspackages/core/src/utils/parseSQL/sqlParser.js- Examples (except
_template)
Regeneration commands:
bun generate-parsers- Regenerate CEL and SQL parsersbun generate-examples- Regenerate example projectsbun update-mantine-css- Syncwebsite/src/pages/demo/_styles/rqb-mantine.cssfromnode_modules/@mantine/core/styles.css; run after updating any@mantine/*dependencies
Performance
- Aggressive memoization
- Lazy loading parsers
- Path-based updates
- Context prevents prop drilling
Accessibility
- ARIA attributes
data-testidattributes- Keyboard navigation
- Screen reader support
Internationalization (i18n)
Translationstype- JSX/string translations
- UI framework integration
Packages
Core (@react-querybuilder/core)
- Non-React utilities, parsers, formatters
- No React dependencies
Main (react-querybuilder)
- React components/hooks
- Backward compatibility required
- No breaking changes without major version bump
UI Packages
- Follow base package's component structure
- Implement all required control elements
- Maintain consistent theming with UI framework
- Include examples and documentation
Extensions (dnd, datetime, native)
- Extend core functionality without breaking changes
- Provide clear integration instructions
- Maintain feature parity where applicable
Release process
bun versionbun check:all- Update documentation
- Push release commit
- Lerna handles package publishing
Pitfalls
- Breaking changes in minor versions
- Missing memoization
- Missing
import type - Direct DOM manipulation
- Prop drilling
- Manually editing generated files
- Missing tests
- Missing accessibility
IDE
Extensions: Oxc, TypeScript, SCSS IntelliSense Settings: Format on save, TypeScript strict mode, inline type hints
Quick Reference
Commands:
bun check:all- Full CIbun start- Dev serverbun run test- Testsbun fmt- Formatbun generate-examples- Update examples
Directories:
packages/core/src/- Non-React utilitiespackages/react-querybuilder/src/- React componentsexamples/- Demos and starter templateswebsite/- Documentation siteutils/- Build and dev utilities