JavaScript query builder
MIT · no dependencies · ~23 KB gz · TypeScript
@qnot/dom, default builder, live on this page. Edit a rule and watch the exports.
pnpm add @qnot/core @qnot/domA JavaScript query builder in two files.
Each node subscribes to its own id and patches only its own elements, with a keyed reconcile per group, so the vanilla builder keeps the same one-rule-one-update behaviour as the framework bindings. bindDragHandle, bindDropTarget and bindGroupDropZone wire drag and drop into your own markup.
Web components, Alpine, htmx, Rails or Django templates: anything that hands you an element can host it. See the vanilla guide.
What you get.
- Typed schemadefineFields · QueryOf
Declare fields once. Rule values narrow by field and operator, and validated queries come back typed.
- Export anywhereSQL · Mongo · JsonLogic
Parameterized SQL in five dialects, MongoDB, JsonLogic, Elasticsearch, SpEL and plain English.
- Validationissues
Per-rule issues from the schema: required, min/max, patterns, options and your own checks.
- Undo and redohistory
Every edit is a step, typing merges into one. The demo above has the buttons wired up.
- Drag and dropdnd
Move rules and groups anywhere. The JavaScript binding ships the drag handle and drop target helpers.
- Any languagelabels · messages
Every UI string and validation message is an override away. No i18n library required.
- Same query on the servervalidate · compile
Share fields.ts with your API. Validate the query, then run it as bound SQL or filter rows in memory.
- Fast at 1,000 rulesper-node subscriptions
Editing one rule updates one rule. A keystroke costs the same at ten rules or a thousand.
How it compares.
- qnot vs jQuery QueryBuilderrender 117 vs 868 ms
The classic jQuery plugin with Bootstrap styling and SQL/Mongo plugins.
- qnot vs Syncfusion QueryBuilderrender 117 vs 6,491 ms
A commercial component in the Syncfusion suite, with wrappers for most frameworks.
- qnot vs DevExtreme FilterBuilderrender 117 vs 141 ms
A commercial filter builder in DevExtreme, made to feed its DataGrid.
Questions people ask
- Is it free?
- Yes. @qnot/dom and @qnot/core are MIT licensed. No license key, no paid tier.
- Does it need jQuery or a framework?
- No. @qnot/dom renders with plain DOM APIs. The whole benchmark page, including the SQL export, is about 23 KB of gzipped JavaScript.
- Does it work in server-rendered apps (Rails, Django, Laravel) or web components?
- Yes. mountQueryBuilder takes any element and returns a dispose function, so it fits a Stimulus controller, an Alpine component, a custom element or a plain script tag that your bundler builds. @qnot/dom depends only on @qnot/core.
- How do I read the query?
- store.getQuery() returns the query as JSON, and store.subscribe(fn) calls you after every change. Encode it with any codec: SQL with bound parameters, MongoDB, JsonLogic, Elasticsearch, SpEL or plain English.Formats
- Is it a replacement for jQuery QueryBuilder?
- It can be. It reads and writes jQuery QueryBuilder's getRules() JSON through the jqqb format, so saved rules keep working, and it renders 1,000 rules in about an eighth of the time in our benchmark. If your app is built on jQuery and Bootstrap and the plugin does what you need, there's no pressing reason to switch.qnot vs jQuery QueryBuilder
- Can I customize the controls?
- Yes. mountQueryBuilder takes components, labels, className, showNot and showLock. A custom value editor is a factory returning { el, update(props), destroy }; it is created once and patched in place as the rule changes.Vanilla guide