Blaze interfaces for Meteor 3
Blaze compiles Spacebars templates into JavaScript and updates small View
regions when their reactive dependencies invalidate. Keep state, subscriptions,
DOM effects, and async work owned by a template instance. Use public
Template, Blaze, Spacebars, and Tracker APIs in application code.
Decision flow
- For a new app, run
meteor create --blaze <name>. Inspect the generated
package.json, .meteor/packages, client entry, and rspack.config.js
before changing packages or build settings.
- Import each template's
.html from the JavaScript module that registers its
helpers, events, and lifecycle hooks. Import that module from the client
entry graph.
- Keep synchronous Minimongo reads in ordinary helpers. If a helper returns a
Promise, render explicit pending, rejected, and resolved states.
- Put
ReactiveVar, ReactiveDict, this.autorun, and this.subscribe on
the template instance. Put DOM initialization in onRendered and undo
external effects in onDestroyed.
- Pass named data and callbacks into reusable child templates. Keep
publication and mutation authority on the server.
- Identify the bundler before diagnosing refresh behavior.
blaze-hot can
replace templates in the Meteor-bundler graph; Rspack currently performs a
full live reload for Blaze. Route general build, migration, database, or
test-runner work to the owning skill.
Current scaffold
meteor create --blaze my-app
cd my-app
meteor
Meteor 3.4+ creates Blaze apps with Rspack by default. Earlier Meteor 3
releases may use the Meteor bundler. The current scaffold declares explicit
client and server meteor.mainModule entries, imports .html from the client
entry, enables the modern build stack, and includes blaze-html-templates,
tracker, reactive-var, hot-module-replacement, blaze-hot, and rspack.
Treat the generated files for the selected Meteor release as the baseline.
Those packages do not enable Blaze HMR in the Rspack graph. Blaze edits there
trigger a fast full page reload and reset page-local state. blaze-hot
replacement behavior applies when the Meteor bundler owns the module.
Do not infer the installed Blaze runtime from the release name alone. Inspect
.meteor/versions, then use the
Blaze history for
feature floors and compatibility changes.
Component scaffold
<template name="taskList">
<label>
<input type="checkbox" class="js-show-done">
Show completed
</label>
{{#if Template.subscriptionsReady}}
{{#each task in tasks}}
{{> taskRow task=task}}
{{else}}
<p>No tasks.</p>
{{/each}}
{{else}}
<p>Loading...</p>
{{/if}}
</template>
import { Template } from "meteor/templating";
import { ReactiveVar } from "meteor/reactive-var";
import { Tasks } from "/imports/api/tasks";
import "./task-list.html";
Template.taskList.onCreated(function () {
this.showDone = new ReactiveVar(false);
this.autorun(() => {
this.subscribe("tasks.list", { showDone: this.showDone.get() });
});
});
Template.taskList.helpers({
tasks() {
const showDone = Template.instance().showDone.get();
return Tasks.find(showDone ? {} : { done: false }, {
sort: { createdAt: -1 },
});
},
});
Template.taskList.events({
"change .js-show-done"(event, instance) {
instance.showDone.set(event.currentTarget.checked);
},
});
this.autorun and this.subscribe stop when the instance is destroyed. A
cursor returned from a synchronous helper remains live through Minimongo.
Authorization and field projection still belong in the publication.
Async helpers
Use #let when loading, rejection, and empty results must be distinct:
{{#let profile=loadProfile}}
{{#if @pending "profile"}}<p>Loading...</p>{{/if}}
{{#if @rejected "profile"}}<p>Could not load profile.</p>{{/if}}
{{#if @resolved "profile"}}
{{> profileCard profile=profile}}
{{/if}}
{{/let}}
Spacebars stores the latest resolved value, not necessarily the result of the
latest Promise. If reactive input can launch overlapping requests, use an
abort signal, generation token, or serialized queue. Do not assume #let
orders results. See references/spacebars-and-async.md.
Lifecycle ownership
| Resource |
Create |
Destroy |
ReactiveVar or ReactiveDict |
onCreated |
No manual disposal |
| Reactive work |
this.autorun |
Automatic with the instance |
| Subscription |
this.subscribe |
Automatic with the instance |
| DOM widget |
onRendered, often after Tracker.afterFlush |
Widget-specific teardown in onDestroyed |
| Window, document, timer, observer |
Lifecycle callback |
Explicit remove, clear, disconnect, or stop in onDestroyed |
| Programmatic Blaze View |
Blaze.render or Blaze.renderWithData |
Blaze.remove(view) |
Read references/lifecycle-and-components.md for data-context rules,
callbacks, dynamic templates, DOM scoping, and cleanup patterns.
Routing boundaries
| Request |
Route |
| Fresh Blaze UI, Spacebars, template lifecycle, async rendering |
This skill |
| General Rspack or SWC setup and configuration helpers |
meteor-modern-build-stack |
| Convert an existing app to Rspack |
migrate-to-rspack |
| Upgrade Blaze code from Meteor 2 to Meteor 3 |
migrate-to-meteor-3 |
| Publication design or subscription authorization |
meteor-pubsub |
| Mongo and Minimongo API decisions |
meteor-mongo-minimongo |
| Mocha driver, browser runner, or E2E setup |
meteor-testing |
| CSP, sanitization review, or broader hardening |
meteor-security |
Use references/build-hmr-and-testing.md only for Blaze-specific entry
imports, HMR ownership, and programmatic template tests.
Anti-patterns
- Return async Minimongo values from every helper. Prefer synchronous client
reads unless the flow is already async or shared with the server.
- Read reactive data only after
await without restoring the captured Tracker
computation.
- Treat
{{#each ...}}{{else}} as a loading indicator. The else branch also
covers rejection and a resolved empty sequence.
- Pass implicit inherited contexts through reusable templates. Pass named data.
- Use global
$() or document.querySelector for component DOM. Scope lookup
to the template instance.
- Insert user-controlled content through triple braces or
Spacebars.SafeString without trusted sanitization.
- Remove DOM nodes created by
Blaze.render without calling Blaze.remove.
- Rely on private or removed UI-era APIs such as
UI.body,
Template.__define__, Template.__body__, Spacebars.TemplateWith, or
Blaze.InOuterTemplateScope.
Authoritative resources
1---2name: meteor-blaze3description: Use when building or debugging Blaze interfaces in Meteor 3: meteor create --blaze, Spacebars templates, Template helpers and events, lifecycle hooks, Tracker, ReactiveVar or ReactiveDict, template subscriptions, Promise helpers, #let async states, Template.dynamic, Blaze.render, and blaze-hot HMR with the Meteor bundler. Triggers on stale async helper results, lost reactivity after await, data-context lookup surprises, duplicate DOM integrations after HMR, Rspack full reloads, or raw HTML in triple braces. Use this skill when the user asks about reusable Blaze components, current Blaze packages, Rspack entry imports, or testing Blaze templates. For Meteor 2 to 3 upgrades, use migrate-to-meteor-3 instead.4license: MIT5---67# Blaze interfaces for Meteor 389Blaze compiles Spacebars templates into JavaScript and updates small View10regions when their reactive dependencies invalidate. Keep state, subscriptions,11DOM effects, and async work owned by a template instance. Use public12`Template`, `Blaze`, `Spacebars`, and Tracker APIs in application code.1314## Decision flow15161. For a new app, run `meteor create --blaze <name>`. Inspect the generated17 `package.json`, `.meteor/packages`, client entry, and `rspack.config.js`18 before changing packages or build settings.192. Import each template's `.html` from the JavaScript module that registers its20 helpers, events, and lifecycle hooks. Import that module from the client21 entry graph.223. Keep synchronous Minimongo reads in ordinary helpers. If a helper returns a23 Promise, render explicit pending, rejected, and resolved states.244. Put `ReactiveVar`, `ReactiveDict`, `this.autorun`, and `this.subscribe` on25 the template instance. Put DOM initialization in `onRendered` and undo26 external effects in `onDestroyed`.275. Pass named data and callbacks into reusable child templates. Keep28 publication and mutation authority on the server.296. Identify the bundler before diagnosing refresh behavior. `blaze-hot` can30 replace templates in the Meteor-bundler graph; Rspack currently performs a31 full live reload for Blaze. Route general build, migration, database, or32 test-runner work to the owning skill.3334## Current scaffold3536```bash37meteor create --blaze my-app38cd my-app39meteor40```4142Meteor 3.4+ creates Blaze apps with Rspack by default. Earlier Meteor 343releases may use the Meteor bundler. The current scaffold declares explicit44client and server `meteor.mainModule` entries, imports `.html` from the client45entry, enables the modern build stack, and includes `blaze-html-templates`,46`tracker`, `reactive-var`, `hot-module-replacement`, `blaze-hot`, and `rspack`.47Treat the generated files for the selected Meteor release as the baseline.48Those packages do not enable Blaze HMR in the Rspack graph. Blaze edits there49trigger a fast full page reload and reset page-local state. `blaze-hot`50replacement behavior applies when the Meteor bundler owns the module.5152Do not infer the installed Blaze runtime from the release name alone. Inspect53`.meteor/versions`, then use the54[Blaze history](https://github.com/meteor/blaze/blob/master/HISTORY.md) for55feature floors and compatibility changes.5657## Component scaffold5859```html60<template name="taskList">61 <label>62 <input type="checkbox" class="js-show-done">63 Show completed64 </label>6566 {{#if Template.subscriptionsReady}}67 {{#each task in tasks}}68 {{> taskRow task=task}}69 {{else}}70 <p>No tasks.</p>71 {{/each}}72 {{else}}73 <p>Loading...</p>74 {{/if}}75</template>76```7778```javascript79import { Template } from "meteor/templating";80import { ReactiveVar } from "meteor/reactive-var";81import { Tasks } from "/imports/api/tasks";82import "./task-list.html";8384Template.taskList.onCreated(function () {85 this.showDone = new ReactiveVar(false);86 this.autorun(() => {87 this.subscribe("tasks.list", { showDone: this.showDone.get() });88 });89});9091Template.taskList.helpers({92 tasks() {93 const showDone = Template.instance().showDone.get();94 return Tasks.find(showDone ? {} : { done: false }, {95 sort: { createdAt: -1 },96 });97 },98});99100Template.taskList.events({101 "change .js-show-done"(event, instance) {102 instance.showDone.set(event.currentTarget.checked);103 },104});105```106107`this.autorun` and `this.subscribe` stop when the instance is destroyed. A108cursor returned from a synchronous helper remains live through Minimongo.109Authorization and field projection still belong in the publication.110111## Async helpers112113Use `#let` when loading, rejection, and empty results must be distinct:114115```html116{{#let profile=loadProfile}}117 {{#if @pending "profile"}}<p>Loading...</p>{{/if}}118 {{#if @rejected "profile"}}<p>Could not load profile.</p>{{/if}}119 {{#if @resolved "profile"}}120 {{> profileCard profile=profile}}121 {{/if}}122{{/let}}123```124125Spacebars stores the latest resolved value, not necessarily the result of the126latest Promise. If reactive input can launch overlapping requests, use an127abort signal, generation token, or serialized queue. Do not assume `#let`128orders results. See `references/spacebars-and-async.md`.129130## Lifecycle ownership131132| Resource | Create | Destroy |133|----------|--------|---------|134| `ReactiveVar` or `ReactiveDict` | `onCreated` | No manual disposal |135| Reactive work | `this.autorun` | Automatic with the instance |136| Subscription | `this.subscribe` | Automatic with the instance |137| DOM widget | `onRendered`, often after `Tracker.afterFlush` | Widget-specific teardown in `onDestroyed` |138| Window, document, timer, observer | Lifecycle callback | Explicit remove, clear, disconnect, or stop in `onDestroyed` |139| Programmatic Blaze View | `Blaze.render` or `Blaze.renderWithData` | `Blaze.remove(view)` |140141Read `references/lifecycle-and-components.md` for data-context rules,142callbacks, dynamic templates, DOM scoping, and cleanup patterns.143144## Routing boundaries145146| Request | Route |147|---------|-------|148| Fresh Blaze UI, Spacebars, template lifecycle, async rendering | This skill |149| General Rspack or SWC setup and configuration helpers | `meteor-modern-build-stack` |150| Convert an existing app to Rspack | `migrate-to-rspack` |151| Upgrade Blaze code from Meteor 2 to Meteor 3 | `migrate-to-meteor-3` |152| Publication design or subscription authorization | `meteor-pubsub` |153| Mongo and Minimongo API decisions | `meteor-mongo-minimongo` |154| Mocha driver, browser runner, or E2E setup | `meteor-testing` |155| CSP, sanitization review, or broader hardening | `meteor-security` |156157Use `references/build-hmr-and-testing.md` only for Blaze-specific entry158imports, HMR ownership, and programmatic template tests.159160## Anti-patterns161162- Return async Minimongo values from every helper. Prefer synchronous client163 reads unless the flow is already async or shared with the server.164- Read reactive data only after `await` without restoring the captured Tracker165 computation.166- Treat `{{#each ...}}{{else}}` as a loading indicator. The `else` branch also167 covers rejection and a resolved empty sequence.168- Pass implicit inherited contexts through reusable templates. Pass named data.169- Use global `$()` or `document.querySelector` for component DOM. Scope lookup170 to the template instance.171- Insert user-controlled content through triple braces or172 `Spacebars.SafeString` without trusted sanitization.173- Remove DOM nodes created by `Blaze.render` without calling `Blaze.remove`.174- Rely on private or removed UI-era APIs such as `UI.body`,175 `Template.__define__`, `Template.__body__`, `Spacebars.TemplateWith`, or176 `Blaze.InOuterTemplateScope`.177178## Authoritative resources179180- [Meteor Blaze tutorial](https://docs.meteor.com/tutorials/blaze/)181- [Blaze guide](https://www.blazejs.org/guide/introduction.html)182- [Spacebars API](https://www.blazejs.org/api/spacebars)183- [Templates API](https://www.blazejs.org/api/templates.html)184- [Blaze programmatic API](https://www.blazejs.org/api/blaze.html)185- [`meteor/blaze` source and tests](https://github.com/meteor/blaze)186- `references/spacebars-and-async.md`187- `references/lifecycle-and-components.md`188- `references/build-hmr-and-testing.md`189- `references/eval-cases.md`