Blaze 3.1.0-beta.0 is out!

The alpha shipped on June 25 and then sat there long enough that people started asking whether Blaze was still breathing. It is: blaze@3.1.0-beta.0 is on Atmosphere, published by the core team today :partying_face:

Here is everything that landed in 3.1 since 3.0, what the beta adds on top of the alpha, and what changed on the Meteor core side :+1:

What is in 3.1

jQuery is now optional (feat: dual-mode JQuery/native DOM backend by dupontbertrand · Pull Request #493 · meteor/blaze · GitHub, Tests: jquery + native DOM backends by jankapunkt · Pull Request #497 · meteor/blaze · GitHub). Blaze ships a native DOM backend and only falls back to jQuery when jQuery is present. Event delegation, teardown and the test matrix all cover both modes. If you keep jquery, nothing changes for you. If you remove it, one API changes shape: TemplateInstance.$() returns an array of elements instead of a jQuery object, while find and findAll behave the same in both modes. Blaze 4.0 will drop jQuery entirely, so 3.1 is where you find out whether your app is ready.

No npm dependencies left in the shipped packages (refactor: remove all external npm dependencies by dupontbertrand · Pull Request #489 · meteor/blaze · GitHub). The lodash helpers and uglify-js in the Spacebars compiler are gone, replaced by native JavaScript. One consequence to know about: generated template code is no longer pretty-printed.

Fewer redundant DOM writes (https://github.com/meteor/blaze/pull/478). Attribute updates are now skipped when nothing changed, and URL protocol detection is cached.

Modern source (refactor: convert ES5 syntax to ES2015+ by dupontbertrand · Pull Request #488 · meteor/blaze · GitHub). The Blaze packages are ES2015+ throughout.

Better error reporting (Fix #314: Use console.error for reporting template exceptions by x19216888-stack · Pull Request #495 · meteor/blaze · GitHub). Template exceptions go through console.error in the fallback path instead of disappearing.

Documentation (docs: migrate to vitepress by jankapunkt · Pull Request #494 · meteor/blaze · GitHub, docs: improvement of architecture documentation by jankapunkt · Pull Request #483 · meteor/blaze · GitHub, docs: fix badges and links by jankapunkt · Pull Request #487 · meteor/blaze · GitHub). The site moved to VitePress, the API and guide pages were expanded, the architecture documentation was rewritten, and the README links and badges were fixed.

What dropping jQuery in Blaze 4.0 will mean

Blaze 4.0 removes jQuery from Blaze, not from your app. The jquery package stays available, so code of yours that calls $('...') keeps working exactly as today, as long as you add the package yourself.

What changes is the one jQuery-shaped API Blaze hands you: TemplateInstance.$() (also this.$() in helpers and event handlers) returns an array of elements instead of a jQuery object, so .data(), .addClass(), .on() and friends are no longer available on its result. find and findAll are unaffected.

Blaze 3.1 already prints a notice at startup when jQuery is present:

[Blaze] jQuery 3.7.1 detected as DOM backend. Native DOM backend is available — remove jquery to enable native DOM backend. jQuery support will be removed in Blaze 4.0.

That notice tells you Blaze is using jQuery; it cannot tell you where your own templates rely on it. The cheapest check is to grep your client code for this.$(, template.$(, instance.$( and .$( chained with a jQuery method, and to try the app once with meteor remove jquery before 4.0 arrives.

From jQuery to native, in practice

Most handlers never needed $ in the first place: the event already carries the element.

// before
'change .filter'(event) {
  const value = $(event.currentTarget).val();
}

// after
'change .filter'(event) {
  const value = event.currentTarget.value;
}

When you do need to reach into the template, find replaces a single-element $:

// before
'click .save'(event, instance) {
  const name = instance.$('#name').val().trim();
  instance.$('.status').addClass('saved').text('Saved');
}

// after
'click .save'(event, instance) {
  const name = instance.find('#name').value.trim();
  const status = instance.find('.status');
  status.classList.add('saved');
  status.textContent = 'Saved';
}

And findAll replaces a multi-element $, returning a real array:

// before
onRendered() {
  this.$('.row').removeClass('active');
}

// after
onRendered() {
  this.findAll('.row').forEach(row => row.classList.remove('active'));
}

The translations you will need most often:

jQuery Native
.val() .value
.text('x') .textContent = 'x'
.html() .innerHTML
.addClass('a') / .removeClass('a') / .toggleClass('a') .classList.add('a') / .remove('a') / .toggle('a')
.attr('href') / .attr('href', v) .getAttribute('href') / .setAttribute('href', v)
.data('moo') .dataset.moo
.hide() / .show() .style.display = 'none' / ''
.on('click', fn) .addEventListener('click', fn)
$(el).closest('.card') el.closest('.card')

What the beta adds over the alpha

The alpha was announced in the Blaze usergroup thread on June 26: Blaze Usergroup Meetings 2026 - #31 by jkuester Testing it turned up two real bugs, both fixed in this beta, and both worth knowing about because they failed silently.

Delegated events dropped in native mode (Native (no-jQuery) event backend silently drops delegated events: scope heuristic compares view.name strings, not view identity · Issue #512 · meteor/blaze · GitHub, fixed by fix(blaze): match delegated event selectors within delegation root by dupontbertrand · Pull Request #513 · meteor/blaze · GitHub). When the delegation root carried the same view name as an ancestor, in practice an inclusion inside {{#if}}, a very common admin-screen shape, the event never reached its handler. The button looked fine and did nothing. The alpha’s 414 green tests never touched that path: adding the two regression tests to the alpha without the fix turns the suite red, which is how the fix was verified.

Stale {{#each}} renders (#each re-rendered with stale data context · Issue #468 · meteor/blaze · GitHub, fixed by fix: prevent stale re-renders of #each items during sequence update (#468) by dupontbertrand · Pull Request #501 · meteor/blaze · GitHub). While a sequence was being updated, item views could re-render with their old data before being removed or updated. Not a 3.1 regression, the bug is older, but it is fixed here.

Blaze is now tested inside Meteor core

This is the part that changes the most for the long run. Until now, Blaze was tested only by its own Tinytest suite, in a test app pinned to Meteor 3.4, and nothing exercised the native DOM backend through Meteor’s build pipeline, which is exactly why the delegated-event bug above shipped in the alpha unnoticed.

Evaluate Blaze 3.1.0 with Meteor 3.6 by nachocodoner · Pull Request #14754 · meteor/meteor · GitHub changes that. The Blaze submodule in meteor/meteor now points at this beta, and any change to that submodule triggers the E2E workflow. The three Blaze E2E apps (blaze, blaze-router, full-blaze) run against the pinned Blaze commit, in the native and the jQuery backend, in development and in production builds, and each run verifies which commit, which version and which backend it actually loaded. The two delegated-event cases (Native (no-jQuery) event backend silently drops delegated events: scope heuristic compares view.name strings, not view identity · Issue #512 · meteor/blaze · GitHub) are regression tests there as well, so the case that broke real screens is now guarded by Meteor’s own CI, in a real app, through the real bundler.

Blaze 3.1 is not bundled into a Meteor release yet: the beta is published on its own, and the core PR is the evaluation step before that happens.

Trying it

Two lines in .meteor/packages:

blaze@3.1.0-beta.0
blaze-html-templates@3.1.0-beta.0

Then meteor remove jquery if you want the native backend.

One trap for package authors: since Meteor 3.5, test-in-console pulls jQuery in, so a test run you believe is native may be a second jQuery run. Check Blaze._DOMBackend._hasJQuery in the page before trusting it.

Where it stands

The full Tinytest suite, run from the published beta commit on Meteor 3.5.2 rather than the 3.4 the Blaze CI pins:

Configuration Passed Failed
Native DOM backend (no jQuery) 420 0
jQuery 3.7.1 backend 424 0

On the app side, eleven projects of mine run the 3.1 prerelease on Meteor 3.5 to 3.5.2, including a compliance SaaS still in development with no jquery package at all, whose 1941-test suite scores exactly what it scored on Blaze 3.0.x.

What would help now

Install the beta and post what happens, good or bad: your Meteor version, whether you kept jQuery, and what broke. Three things are worth checking specifically:

  1. Controls that stop responding. That is the shape of Native (no-jQuery) event backend silently drops delegated events: scope heuristic compares view.name strings, not view identity · Issue #512 · meteor/blaze · GitHub . It should be gone; if you still see it, say so here.
  2. TemplateInstance.$. Without jQuery it returns an array, so .data(), .addClass() and friends are not available on the result.
  3. Packages that assume jQuery, including anything reading Blaze._DOMBackend directly.

Reports from apps other than the ones already tested are what moves this to a stable 3.1.0. The documentation is being updated in parallel: a few pages still describe the 3.0 behaviour, starting with the migration guide, which says jQuery was removed rather than made optional.

After 3.1

Once this beta has had a round of real feedback, it would be worth reopening Blaze Next - Vision, Ideas, Tasks etc and deciding together what a 3.2 should contain. Nothing of that is settled with the core team yet, and Blaze is not on the Meteor roadmap, so what lands next is what the community argues for and then builds.

There is more on the table than bug fixes.

Performance is one of those priorities, and there are numbers behind it. Blaze’s runtime is small and fast on ordinary screens; where it hurts today is large lists, because observe-sequence locates positions by scanning. On a 5000-row table, reversing the order takes 11.4 s. Replacing those scans with a Fenwick tree brings the same reversal to 3.35 s, removing half the rows from 5.0 s to 1.92 s, and clearing the table from 7.7 s to 3.40 s. All of it measured on Meteor 3.5.2 in production mode with the native backend, alternating the two builds pair by pair. That work is written and verified but not submitted yet, and the next bottleneck after it sits in Blaze.Each itself. It is exactly the kind of thing a 3.2 could carry :chart_increasing:

The direction I find most promising: Blaze is a small, simple templating language, which makes it an unusually good fit for the coding agents that are going to write a large share of our code. Shaping a templating language that agents read and write well is a real opportunity, and it needs opinions from people who ship Blaze apps every day :folded_hands:

Thanks to @jkuester, who reviewed and merged the fixes, and to @nachocodoner, who published the beta and built the E2E coverage in core :flexed_biceps:

6 Likes