Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/meteor/meteor/llms.txt

Use this file to discover all available pages before exploring further.

Blaze is Meteor’s built-in reactive templating engine. It provides a simple, intuitive way to create dynamic user interfaces with automatic reactivity and live updates.
Comprehensive Blaze documentation is available at the Blaze Community Site.

Installation

Blaze is included by default when you create a new Meteor app. To add it to an existing project:
meteor add blaze-html-templates
For reactive variables:
meteor add reactive-var

Quick Start

Create a Blaze app:
meteor create my-app --blaze

Basic Structure

Blaze uses HTML templates with special syntax for dynamic content:
<head>
  <title>My Meteor App</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>

<body>
  <h1>Welcome to Meteor!</h1>
  {{> hello}}
  {{> info}}
</body>

<template name="hello">
  <button>Click Me</button>
  <p>You've pressed the button {{counter}} times.</p>
</template>

<template name="info">
  <h2>Learn Meteor!</h2>
  <ul>
    <li><a href="https://www.meteor.com/try" target="_blank">Do the Tutorial</a></li>
    <li><a href="http://guide.meteor.com" target="_blank">Follow the Guide</a></li>
  </ul>
</template>

Template Helpers and Events

Add behavior to templates with JavaScript:
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import './main.html';

Template.hello.onCreated(function helloOnCreated() {
  // counter starts at 0
  this.counter = new ReactiveVar(0);
});

Template.hello.helpers({
  counter() {
    return Template.instance().counter.get();
  },
});

Template.hello.events({
  'click button'(event, instance) {
    // increment the counter when button is clicked
    instance.counter.set(instance.counter.get() + 1);
  },
});

Component-Based Structure

Organize Blaze templates as reusable components:
/imports
  /ui
    /components
      /hello
        hello.html
        hello.js
      /info
        info.html
        info.js
    /layouts
      /body
        body.html
        body.js

Component Example

<template name="hello">
  <h1>Welcome to Meteor!</h1>
  <button>Click Me</button>
  <p>You've pressed the button {{counter}} times.</p>
</template>

Working with Collections

Blaze automatically reacts to data changes:
<template name="info">
  <h2>Learn Meteor!</h2>
  <ul>
    <li>
      <form class="info-link-add">
        <input type="text" name="title" placeholder="Title" required>
        <input type="url" name="url" placeholder="Url" required>
        <input type="submit" value="Add new link">
      </form>
    </li>
    {{#each links}}
      <li><a href="{{url}}" target="_blank">{{title}}</a></li>
    {{/each}}
  </ul>
</template>

Template Lifecycle

Blaze provides lifecycle callbacks for template instances:
Template.myTemplate.onCreated(function () {
  // Called when template instance is created
  this.counter = new ReactiveVar(0);
  this.autorun(() => {
    // Reactive computation
  });
});

Template.myTemplate.onRendered(function () {
  // Called when template is rendered to DOM
  this.$('input').focus();
});

Template.myTemplate.onDestroyed(function () {
  // Called when template is removed
  // Cleanup happens automatically for subscriptions and autorun
});

Template Syntax

Data Context

<!-- Current data context -->
<p>{{title}}</p>

<!-- Nested property -->
<p>{{user.name}}</p>

<!-- Helper function -->
<p>{{formatDate createdAt}}</p>

Control Flow

<!-- Conditional rendering -->
{{#if isLoggedIn}}
  <p>Welcome, {{username}}!</p>
{{else}}
  <p>Please log in.</p>
{{/if}}

<!-- Iteration -->
{{#each tasks}}
  <li>{{title}}</li>
{{else}}
  <li>No tasks found.</li>
{{/each}}

<!-- With block -->
{{#with currentUser}}
  <p>{{username}}</p>
  <p>{{email}}</p>
{{/with}}

Template Inclusion

<!-- Include template -->
{{> taskItem}}

<!-- Pass data context -->
{{> taskItem task=currentTask}}

<!-- Pass multiple arguments -->
{{> userProfile user=currentUser editable=true}}

Reactivity

Reactive Variables

import { ReactiveVar } from 'meteor/reactive-var';

Template.example.onCreated(function () {
  this.searchQuery = new ReactiveVar('');
});

Template.example.helpers({
  results() {
    const query = Template.instance().searchQuery.get();
    return Items.find({ name: new RegExp(query, 'i') });
  },
});

Template.example.events({
  'input .search'(event, instance) {
    instance.searchQuery.set(event.target.value);
  },
});

Reactive Dictionaries

import { ReactiveDict } from 'meteor/reactive-dict';

Template.example.onCreated(function () {
  this.state = new ReactiveDict();
  this.state.set('loading', false);
  this.state.set('filter', 'all');
});

Template.example.helpers({
  isLoading() {
    return Template.instance().state.get('loading');
  },
  currentFilter() {
    return Template.instance().state.get('filter');
  },
});

Subscriptions

Template.taskList.onCreated(function () {
  // Subscribe to data
  this.subscribe('tasks');
  
  // Subscribe with parameters
  this.autorun(() => {
    const listId = FlowRouter.getParam('listId');
    this.subscribe('tasks', listId);
  });
});

Template.taskList.helpers({
  tasks() {
    return Tasks.find({});
  },
  isLoading() {
    return !Template.instance().subscriptionsReady();
  },
});

Integration with React

Use React components inside Blaze templates:
meteor add react-template-helper
<template name="userDisplay">
  <div>Hello, {{username}}</div>
  <div>{{> React component=UserAvatar userId=_id}}</div>
</template>
import UserAvatar from './UserAvatar.jsx';

Template.userDisplay.helpers({
  UserAvatar() {
    return UserAvatar;
  }
});

Best Practices

Store state on template instances, not global variables:
Template.example.onCreated(function () {
  this.counter = new ReactiveVar(0); // Good
});

// Not: let counter = 0; (Bad - shared across instances)
Use this.subscribe() in onCreated for automatic cleanup:
Template.example.onCreated(function () {
  this.subscribe('data'); // Automatically cleaned up
});
Wrap reactive computations in autorun:
Template.example.onCreated(function () {
  this.autorun(() => {
    const id = Session.get('currentId');
    this.subscribe('item', id);
  });
});
Use .limit() and .fields() to reduce data transfer:
Template.example.helpers({
  recentTasks() {
    return Tasks.find({}, { 
      limit: 10,
      fields: { title: 1, createdAt: 1 },
      sort: { createdAt: -1 }
    });
  },
});

Common Patterns

Loading States

Template.example.onCreated(function () {
  this.subscribe('data');
});

Template.example.helpers({
  isLoading() {
    return !Template.instance().subscriptionsReady();
  },
});
{{#if isLoading}}
  <div class="loading">Loading...</div>
{{else}}
  {{#each items}}
    <div>{{name}}</div>
  {{/each}}
{{/if}}

Forms

<template name="taskForm">
  <form class="new-task">
    <input type="text" name="title" placeholder="New task" required>
    <button type="submit">Add Task</button>
  </form>
</template>
Template.taskForm.events({
  'submit .new-task'(event) {
    event.preventDefault();
    
    const title = event.target.title.value;
    
    Meteor.callAsync('tasks.insert', title)
      .then(() => {
        event.target.title.value = '';
      })
      .catch((error) => {
        alert(error.message);
      });
  },
});

Resources

Blaze Guide

Comprehensive guide to Blaze templating

Blaze API

Complete API reference for Blaze

Blaze Tutorial

Step-by-step tutorial for building Blaze apps

Reactive Var

Documentation for ReactiveVar