Hi everyone! π
bQuery.js started last autumn with a question I couldn't stop chewing on: why was jQuery actually invented?
Strip away two decades of memes and "jQuery is dead" hot takes, and the answer is pretty simple. jQuery existed to make developer life easier under the conditions of its time β inconsistent browsers, a painful DOM API, no fetch, no querySelector, no module system worth speaking of. It papered over the platform because the platform needed papering.
So I started asking the modern version of that question: if you sat down today and tried to do for 2025+ what jQuery did for 2006, what would you actually build?
Not "jQuery, but updated". Not "jQuery, but TypeScript". An honest answer to: what are today's pain points, and what would a library look like that fixes them without adding new ones?
That question is where bQuery comes from. A few months later, it's not really a library anymore β it's a full-stack framework. And to celebrate that, the docs have officially moved into their own home:
π₯ bquery.js.org β your new bookmark for everything bQuery.
Let me tell you how we got here.
π Chapter 1 β Three Non-Negotiables
When I started sketching this thing last autumn, three principles were locked in from day one. Everything else had to bend around them.
1. Zero mandatory build step. You should be able to drop a single <script type="module"> into a plain HTML file and write something useful in 12 lines. No node_modules. No bundler config. No vibes-based tsconfig.json you cargo-culted from a tutorial. The platform is good now β the library shouldn't gatekeep you behind a toolchain.
2. Single import, with or without CDN. One entry point. You either install from npm/bun/pnpm, or you point at unpkg/jsDelivr. Either way, the API is identical. No "CDN edition" vs. "module edition" mind games.
3. Real tree-shaking. Importing bQuery shouldn't drag in the whole framework if you only wanted $. Every module is its own explicit entry point. You pay for exactly what you import β nothing more.
Those three rules made the design decisions almost automatic. They're also the reason it grew the way it did: when the API is honest about cost, you can keep adding without bloating anyone's bundle.
The first version looked exactly like you'd expect:
import { $, $$ } from '@bquery/bquery/core';
$('#button').on('click', () => {
$$('.card').addClass('hovered');
});
That's it. A typed, chainable, modern DOM helper. Zero build step required. Zero runtime dependencies. Roughly the energy of "jQuery, but it understands what unknown means".
It scratched the itch. People used it. The story could have ended there.
It didn't.
π± Chapter 2 β "Hm, this could use signals"
The first time the project stopped being a library was the day I added signals.
Once you have reactive primitives, suddenly $('#counter').text(...) isn't just a one-shot DOM write β it's an expression of state. And once you have that, you want components. And once you have components, you want a router. And once you have a router, you want SSR. And once you have SSRβ¦
You see where this is going.
Crucially: none of that broke the three rules. Tree-shaking meant new modules didn't tax the people who only wanted $. The single-import contract held. The CDN entry still worked. Every release tried to keep the same jQuery-ish hand feel, but the surface kept growing:
- Signals & effects for reactive state
- Components built on the native Web Components API β no virtual DOM, no shipping a runtime renderer
-
Views & directives (
bq-on,bq-html-safe,bq-memo,bq-cloakβ¦) for declarative templates without JSX - Forms with validation, dirty/touched state, the whole thing
-
Router with guards, lazy routes, navigation results, and a proper
useNavigation()composable - i18n with locale negotiation, RTL detection, relative time, and list formatting
-
A11y helpers β live regions, focus management,
prefersReducedMotion, forced colors, scroll-lock, the lot - HTTP / fetch composables that plug straight into signals β polling, pagination, REST helpers, WebSocket and SSE
- Drag & drop, including sortables, grid snapping, keyboard handling, viewport bounds
- SSR with streaming, boundaries, edge handlers, response caching
-
Server β yes, a proper server runtime with middleware, cookies, SSE, streaming responses,
app.listen() - Devtools, testing utilities, plugin system, media composablesβ¦
That's not a library. That's a framework. It took me a while to admit it out loud.
π οΈ Chapter 3 β The Three Rules, Still Holding
Here's the part I'm proudest of: bQuery grew into a framework without breaking the original promises.
You can still drop a single <script type="module"> into a plain HTML file and write 12 lines of code. You can also build a server-rendered app with streaming SSR, a typed router, signal-driven data fetching, and Web Components β using the same API surface. Same single import. Same tree-shaking. Same "no build step required, but works great if you want one".
// Minimal β just the DOM helpers
import { $, $$ } from '@bquery/bquery/core';
// Reactive
import { signal, computed, effect } from '@bquery/bquery/reactive';
// Whole kitchen sink
import {
$, signal, component, registerDefaultComponents, defineBqueryConfig,
} from '@bquery/bquery';
Explicit entry points. TypeScript-first. Truly tree-shakeable. Same vocabulary across the whole framework.
The CDN entry isn't a marketing afterthought β it's a core feature, in the same way the build-based install is. The moment you force people into a toolchain, you've already lost the "I just want to make a thing" crowd. And that crowd is sacred.
<script type="module">
import { $, signal } from 'https://unpkg.com/@bquery/bquery@1/dist/full.es.mjs';
const count = signal(0);
$('#btn').on('click', () => count.value++);
effect(() => $('#out').text(`Count: ${count.value}`));
</script>
That's a reactive counter. No bundler. No node_modules. No vibes-based config file. Just the platform.
π Quick Aside: Security That Isn't Optional
If you remember the jQuery era, you also remember .html(userInput) shipping XSS into production. Hard pass.
bQuery sanitizes DOM writes by default and ships first-class helpers for Trusted Types and CSP. There's a bq-html-safe directive for templates that need to render markup explicitly. The defaults are the safe ones β you have to opt in to the dangerous thing, not opt out. That's the way it should have been all along.
π Chapter 4 β Welcome to bquery.js.org
Okay. The actual reason for this post.
The docs used to live wherever I could shove them β README, GitHub Pages under a long URL, scattered Markdown files. That worked when the surface was "here are 8 functions and a chain API". It does not work when the surface is a full framework with 20+ modules, hundreds of composables, SSR, a server runtime, and a plugin system.
So I did the obvious thing. The docs got their own home:
π bquery.js.org
What you'll find there:
- A proper getting started path β from "drop a
<script>tag" to "scaffold a full SSR app" - Module-by-module reference with every signal, composable, directive, and helper
- A searchable API reference generated straight from the source
- Migration notes if you've been riding the 1.x train
- A changelog that doesn't try to hide breaking changes in a footnote
The domain change is also a small but real symbolic thing. bquery.js.org is on the js.org registry β a community-run home for JavaScript projects. It feels right. It's where bQuery belongs now that it's stopped pretending to be small.
π¦ Where We Are Today (v1.14.1)
For the people who like a quick state-of-the-union:
| Area | What's in it |
|---|---|
| Core |
$, $$, chainable DOM API, sane defaults |
| Reactive | Signals, computed, effects, stores |
| Components | Web Components-based, no VDOM runtime |
| Views | Directives, bq-on modifier system, memoization |
| Router | Guards, lazy routes, useNavigation, dynamic routes |
| Forms | Validation, dirty/touched, field-level signals |
| HTTP | Fetch composables, polling, pagination, REST, WebSocket, SSE |
| i18n | Locale negotiation, RTL, relative time, list & display name formatting |
| A11y | Live regions, focus-visible, forced-colors, reduced-motion, scroll-lock |
| Drag & Drop | Sortables, grids, keyboard, viewport bounds |
| Media | 25+ reactive composables for preferences, page state, observers, pointer/scroll, clipboard |
| SSR | Streaming, boundaries, edge handlers, response caching |
| Server | HTTP runtime, middleware, cookies, SSE, streaming responses |
| Plugin system | Hook bus, DI container, namespaced directives, lifecycle |
| Devtools | Timeline ring buffer, signal/store diffs, traces, snapshots, browser bridge |
| Testing | Auto cleanup, userEvent, shadow-DOM-aware queries, module mocks, a11y helpers |
All of that, and the 1.x API surface is still backwards compatible going back to 1.0. I'm a bit smug about that one ^^ .
π€ But Is It Stillβ¦ jQuery-y?
Yes. That's the whole point.
You can absolutely write a 2026 app using nothing but $('#thing').addClass(...).on('click', ...). The framework parts are opt-in. If you want to write tiny scripts, write tiny scripts. If you want to ship an SSR app with reactive forms and a typed server, that's the same install. Same imports. Same API conventions.
The whole thesis goes back to that original question β what would jQuery look like if it were designed today, for today's pain points? The platform is genuinely good now. We shouldn't have to choose between "ergonomic" and "uses what the browser already gives you". And we shouldn't have to choose between "powerful framework" and "drop a script tag and it works". bQuery is the bet that you can have all of it, in one import.
π Contribute, Star, Yell at Me
bQuery is open source under the same spirit as everything else I publish. If you want to help:
- β Star the GitHub repo β it does more than you'd think
- π Open issues when things break
- β¨ PRs are welcome, especially for the parts you actually use
- π Docs improvements over at bquery.js.org are very welcome β even a typo fix is a typo someone else won't trip over
If you'd previously written off "jQuery-style" as a museum piece, this might be a good moment to look again. The vibes are familiar. The internals are not.
π― TL;DR
- bQuery.js started last autumn with one question: what would jQuery look like if you designed it today, for today's pain points?
- Three rules locked in from day one: zero mandatory build step, single import (CDN or npm), real tree-shaking
- It has since grown into a full-stack framework β signals, components, SSR, routing, a server runtime, forms, i18n, a11y, devtools, testing utilities β without breaking any of those rules
- π New docs home: bquery.js.org β go bookmark it
- Latest release is v1.14.1, backwards compatible all the way back to 1.0
The jQuery for the modern web platform is no longer just jQuery for the modern web platform.
Cheers, and see you on bquery.js.org. π₯
β Josun
Top comments (0)