Migrating from jQuery
This guide helps you transition from jQuery to bQuery.js. The mental model is similar — query elements, chain methods, handle events — but bQuery adds reactivity, type safety, and modern APIs.
Quick comparison
js
// jQuery
$('#name').text('Ada');
$('.items').addClass('active');
$('#btn').on('click', () => alert('clicked'));
// bQuery
$('#name').text('Ada');
$$('.items').addClass('active');
$('#btn').on('click', () => alert('clicked'));The syntax is intentionally familiar. The key differences:
$()returns a single element wrapper (BQueryElement) and throws if not found$$()returns a collection wrapper (BQueryCollection) and never throws- Everything is TypeScript-first with full type inference
- HTML-writing methods sanitize content by default
Selectors
Single element: $()
jQuery's $() returns a collection that may be empty. bQuery's $() returns exactly one element or throws.
js
// jQuery — silently returns empty collection
const maybeEl = $('#maybe-exists'); // maybeEl.length might be 0
// bQuery — throws if not found
const mustEl = $('#must-exist'); // guaranteed to exist
// bQuery — safe alternative for optional elements
const els = $$('#maybe-exists'); // empty collection if not foundMultiple elements: $$()
js
// jQuery
$('.card').each(function () {
$(this).addClass('visible');
});
// bQuery — operates on the whole collection without a callback
$$('.card').addClass('visible');Context-scoped queries
js
// jQuery
$('.item', '#container');
// bQuery
$('#container').find('.item');
$$('#container .item');DOM Manipulation
Getting and setting text
js
// jQuery
$('#title').text('Hello');
const title = $('#title').text();
// bQuery — identical API
$('#title').text('Hello');
const title = $('#title').text();Getting and setting HTML
js
// jQuery — no sanitization
$('#content').html('<b>Bold</b>');
// bQuery — sanitized by default (strips dangerous tags)
$('#content').html('<b>Bold</b>');
// bQuery — raw HTML when you trust the source
$('#content').raw.innerHTML = trustedHtml;Adding and removing classes
js
// jQuery
$('#el').addClass('active');
$('#el').removeClass('hidden');
$('#el').toggleClass('open');
$('#el').hasClass('active');
// bQuery — same API
$('#el').addClass('active');
$('#el').removeClass('hidden');
$('#el').toggleClass('open');
$('#el').hasClass('active');CSS styles
js
// jQuery
$('#el').css('color', 'red');
$('#el').css({ color: 'red', fontSize: '18px' });
// bQuery — same API
$('#el').css('color', 'red');
$('#el').css({ color: 'red', fontSize: '18px' });Attributes and data
js
// jQuery
$('#el').attr('aria-label', 'Close');
$('#el').data('id', 42);
// bQuery
$('#el').attr('aria-label', 'Close');
$('#el').data('id', 42);Append, prepend, and remove
js
// jQuery
$('#list').append('<li>New item</li>');
$('#list').prepend('<li>First item</li>');
$('#item').remove();
// bQuery — same API, with automatic sanitization
$('#list').append('<li>New item</li>');
$('#list').prepend('<li>First item</li>');
$('#item').remove();Events
Basic event handling
js
// jQuery
const handleClick = (e) => console.log('clicked');
$('#btn').on('click', handleClick);
$('#btn').off('click', handleClick);
// bQuery — keep a reference to the handler you want to remove
const handleBqClick = (e) => console.log('clicked');
$('#btn').on('click', handleBqClick);
$('#btn').off('click', handleBqClick);Event delegation
js
// jQuery
$(document).on('click', '.dynamic-btn', handler);
// bQuery
$('#container').delegate('click', '.dynamic-btn', handler);Keyboard and input events
js
// jQuery
$('#input').on('keydown', (e) => {
if (e.key === 'Enter') submit();
});
// bQuery — same API
$('#input').on('keydown', (e) => {
if (e.key === 'Enter') submit();
});AJAX → Reactive Data
This is where bQuery diverges most from jQuery. Instead of callback-based AJAX, bQuery provides signal-based async primitives.
Simple fetch
js
// jQuery
$.ajax({
url: '/api/users',
success: (data) => renderUsers(data),
error: (xhr) => showError(xhr.statusText),
});
// bQuery
import { effect, useFetch } from '@bquery/bquery/reactive';
const { data, error, pending } = useFetch('/api/users');
effect(() => {
if (pending.value) showSpinner();
else if (error.value) showError(error.value.message);
else renderUsers(data.value);
});POST requests
js
// jQuery
$.post('/api/users', { name: 'Ada' }, (response) => {
console.log(response);
});
// bQuery
import { http } from '@bquery/bquery/reactive';
const response = await http.post('/api/users', {
body: JSON.stringify({ name: 'Ada' }),
});Polling
js
// jQuery — manual setInterval
const timer = setInterval(async () => {
const data = await $.get('/api/status');
$('#status').text(data.message);
}, 5000);
// bQuery — declarative polling
import { $ } from '@bquery/bquery/core';
import { effect, usePolling } from '@bquery/bquery/reactive';
const { data } = usePolling('/api/status', { interval: 5000 });
effect(() => {
if (data.value) $('#status').text(data.value.message);
});WebSocket
js
// jQuery — manual WebSocket management
const ws = new WebSocket('wss://example.com/chat');
ws.onmessage = (e) => {
const msg = JSON.parse(e.data);
$('#messages').append(`<p>${msg.text}</p>`);
};
// bQuery — reactive WebSocket
import { $ } from '@bquery/bquery/core';
import { effect, useWebSocket } from '@bquery/bquery/reactive';
type ChatMessage = { text: string };
const { data, send } = useWebSocket<ChatMessage, ChatMessage>('wss://example.com/chat');
effect(() => {
if (data.value) {
$('#messages').append(`<p>${data.value.text}</p>`);
}
});
send({ text: 'Hello!' });Animations
Basic animations
js
// jQuery
$('#card').fadeIn(200);
$('#card').slideUp(300);
// bQuery — Web Animations API
import { animate, keyframePresets } from '@bquery/bquery/motion';
const card = document.querySelector('#card')!;
await animate(card, {
keyframes: keyframePresets.fadeIn(),
options: { duration: 200 },
});View transitions (no jQuery equivalent)
ts
import { $ } from '@bquery/bquery/core';
import { transition } from '@bquery/bquery/motion';
await transition(() => {
$('#content').html(newContent);
});What bQuery adds beyond jQuery
These features have no jQuery equivalent:
Reactive state
ts
import { $ } from '@bquery/bquery/core';
import { signal, computed, effect } from '@bquery/bquery/reactive';
const count = signal(0);
const doubled = computed(() => count.value * 2);
effect(() => {
$('#output').text(`${count.value} × 2 = ${doubled.value}`);
});Declarative view bindings
html
<div id="app">
<input bq-model="name" />
<p bq-text="'Hello, ' + name + '!'"></p>
</div>ts
import { mount, signal } from '@bquery/bquery/view';
mount('#app', { name: signal('World') });Web Components
ts
import { component, html } from '@bquery/bquery/component';
component('user-card', {
props: {
name: { type: String, default: '' },
avatar: { type: String, default: '' },
},
render({ props }) {
return html`
<img src="${props.avatar}" alt="${props.name}" />
<h3>${props.name}</h3>
`;
},
});Reactive forms
ts
import { createForm, required, email } from '@bquery/bquery/forms';
const form = createForm({
fields: {
email: { initialValue: '', validators: [required(), email()] },
},
onSubmit: async (values) => {
await fetch('/api/register', { method: 'POST', body: JSON.stringify(values) });
},
});Client-side routing
ts
import { effect } from '@bquery/bquery/reactive';
import { createRouter, currentRoute, navigate } from '@bquery/bquery/router';
createRouter({
routes: [
{ path: '/', component: () => showHome() },
{ path: '/about', component: () => showAbout() },
],
});
// bQuery keeps routing and rendering separate, so render the matched view reactively.
effect(() => {
const component = currentRoute.value.matched?.component;
if (!component) return;
const result = component();
if (result instanceof Promise) {
void result.catch((error) => console.error('Route render failed', error));
}
});State management
ts
import { createStore } from '@bquery/bquery/store';
const counter = createStore({
id: 'counter',
state: () => ({ count: 0 }),
getters: {
doubled: (state) => state.count * 2,
},
actions: {
increment() {
this.count++;
},
},
});Migration checklist
Use this as a step-by-step migration plan:
- [ ] Replace
<script src="jquery.js">with<script type="module">and bQuery CDN import - [ ] Replace
$()selector calls — use$()for required elements,$$()for optional/collections - [ ] Replace
$.ajax()calls withuseFetch(),http, oruseSubmit() - [ ] Replace
$.each()with nativefor...of,$$().each(...), or iteration over$$().elements - [ ] Replace jQuery animations with
animate(),transition(), or CSS transitions - [ ] Replace
$(document).ready()— ES modules run deferred by default, no wrapper needed - [ ] Add reactive state where you previously had manual DOM updates
- [ ] Add TypeScript for better autocompletion and error checking (optional but recommended)
$(document).ready() is not needed
ES modules (type="module") are deferred by default, so your code already runs after the DOM is ready:
html
<!-- jQuery -->
<script>
$(document).ready(function () {
// DOM is ready
});
</script>
<!-- bQuery — just write your code -->
<script type="module">
import { $ } from 'https://cdn.jsdelivr.net/npm/@bquery/bquery@1/+esm';
// DOM is already ready
$('#app').text('Hello!');
</script>Need help?
- Getting Started — full setup instructions
- Core API — complete DOM API reference
- Reactive — signals, effects, and async data
- FAQ & Troubleshooting — common issues and solutions