Skip to main content
Version: Next

Framework adapter API

Refer to the TypeScript reference page for information about the types and interfaces referenced below.

@react-querybuilder/core contains every derivation the <QueryBuilder /> component performs, with no React dependency. A framework port—Solid, Svelte, Vue, or anything else—can render an equivalent UI by calling these functions directly instead of reimplementing the precedence rules for fields, operators, values, class names, and history.

info

The exports listed on this page are public API covered by semantic versioning. They will not be removed or have their signatures changed outside a major release.

The full runtime export surface of @react-querybuilder/core (type-only exports are not covered) is locked by a test (packages/core/src/__tests__/publicApi.test.ts) that fails on both accidental removal and accidental addition, so every change to the surface requires a deliberate changelog entry.

The /derivations subpath

Everything on this page is available from the package root, and—except for QueryManager, formatQuery, and the parsers—also from @react-querybuilder/core/derivations:

import { deriveRuleContext, shouldCoalesce } from '@react-querybuilder/core/derivations';

The subpath contains the same bindings as the root, minus QueryManager and the query formatter. Importing from it guarantees that neither ends up in the bundle. That matters for adapters that own their own state and never construct a QueryManager: importing the same names from the root leaves their elimination up to the bundler, which is a property worth having as a contract rather than as an optimization that may or may not happen.

bun check-derivations-purity gates the guarantee against the built output, so an import added to a derivation module cannot quietly undo it.

The parsers (parseSQL, parseCEL, and the rest) have subpaths of their own and are not exported from the root, so they are excluded from /derivations as well.

Option list preparation

FunctionPurpose
prepareRuleGroupNormalizes a query object, assigning ids where missing.
prepareOptionListNormalizes any option list prop into a FullOptionList, applying placeholders.
toFlatOptionArrayFlattens option groups into a single array.
resolveOperatorListThe operator list for a field, applying the same precedence as <QueryBuilder />.
resolveDefaultOperatorThe default operator for a field.
resolveValueListThe value list for a field/operator pair.
resolveValueEditorTypeThe value editor type for a field/operator pair.
getValueSourcesUtilThe value sources available for a field/operator pair.
getMatchModesUtilThe match modes available for a field.
getRuleDefaultValueThe default value for a rule.
getFieldDataLooks a field up in a flattened field map, with a minimal fallback.

Rule and group derivations

FunctionPurpose
deriveRuleContextEverything a rule's UI needs: field data, operators, value editor type, etc.
deriveRuleGroupContextThe equivalent derivation for a group.
deriveQueryBuilderClassNamesThe resolved class names for every element.
generateAccessibleDescriptionThe accessible description for a group.
resolveCandidateQueryResolves a controlled/uncontrolled query prop against the current state.

Query manipulation

createRule, createRuleGroup, createQueryActions, and the query tools add, remove, update, move, insert, and group. See Query management for full signatures.

Path helpers: findPath, findID, getPathOfID, pathIsDisabled, pathIsDisabledByPaths, exceedsMaxLevels.

Every query tool accepts freeze: false, which skips the deep freeze applied to the returned query. Frameworks that wrap state in proxies (Vue reactive, Solid stores) need that; see Freezing. QueryManager accepts an option of the same name, and setAutoFreeze is re-exported from immer as a process-wide switch.

History

ExportPurpose
signatureOfDescribes what changed between two queries.
structuralSignature / unchangedSignatureSentinel signatures: shape changed / nothing observable changed.
shouldCoalesceWhether a change should be absorbed into the current history entry.
defaultCoalesceMs / defaultMaxHistoryDefaults for the coalescing window and stack depth.

shouldCoalesce is the same predicate QueryManager applies internally, so a port that owns its own past/future stacks records history identically without duplicating the rule:

import { shouldCoalesce, signatureOf, unchangedSignature } from '@react-querybuilder/core';
import type { RuleGroupTypeAny } from '@react-querybuilder/core';

let past: RuleGroupTypeAny[] = [];
let future: RuleGroupTypeAny[] = [];
let lastSig = '';
let lastAt = 0;

const record = (prev: RuleGroupTypeAny, next: RuleGroupTypeAny) => {
const sig = signatureOf(prev, next);
// A change with no observable difference is never recorded at all.
if (sig === unchangedSignature) return;
if (!shouldCoalesce(lastSig, sig, lastAt, Date.now())) {
past.push(prev);
future = [];
}
lastSig = sig;
lastAt = Date.now();
};

Configuration

ExportPurpose
optionsEqualWhether two QueryManagerOptions objects describe the same configuration.
valuesEqualThe underlying comparison, for individual option values.
SubscriptionChangeThe payload passed to QueryManager subscribers (type-only).

Both comparisons treat arrays and plain objects as values—descending into nested objects such as history and translations—and everything else, functions included, by identity. That split is what lets an options object rebuilt on every render compare equal as long as its data did not change, which every adapter needs: an identity-only comparison makes the effect that calls reconfigure self-perpetuating.

QueryManager#reconfigure applies this gate itself, so an adapter can call it unconditionally. optionsEqual is exported for adapters that want to skip building the merged options object in the first place.

import { optionsEqual } from '@react-querybuilder/core';

// Only builds the merged object when something actually changed.
if (!optionsEqual(manager.getOptions(), nextOptions)) manager.reconfigure(nextOptions);

Controls

ExportPurpose
controlKeysThe name of every query builder control.
controlPropKeysThe complete set of prop names each control receives.
controlKindWhich bulk override (actionElement / valueSelector) applies to each control, if any.

controlPropKeys lets a port's default controls declare every prop they receive explicitly, instead of relying on framework-specific attribute fall-through. controlKind replaces name-suffix sniffing such as key.endsWith('Action'), which silently misclassifies controls whose names happen to end the same way.

Validation and export

defaultValidator and formatQuery are also framework-agnostic. See Validation and Export. defaultValidator is in the /derivations subpath; formatQuery is not, since it is by far the largest thing in the package and most adapters never call it. Import it from the package root or from @react-querybuilder/core/formatQuery.