What is Glimmer.js?
Glimmer is the rendering engine that powers Ember.js templates since Ember 2.10. It compiles Handlebars-flavoured templates to a binary "wire format" and a stream of opcodes that run on a virtual machine — the result is fast, fine-grained DOM updates with autotracking. Glimmer templates are .hbs files that look like HTML extended with {{...}} expressions and {{#block}}...{{/block}} helpers; Octane introduced Angle Bracket Invocation (<MyComponent @arg={{value}} />) as the modern way to call components.
Is Ember still relevant?
Yes — Ember 5 ships in 2023, the project has a defined six-week release cycle, and Ember 6 is in development. The framework powers production applications at LinkedIn, Square, Apple Music, Tilde (Skylight), Intercom, and many others. The community is smaller than React or Vue but the framework is mature and the conventions are stable. If you maintain an Ember app you need a Glimmer template formatter; if you are picking a new framework, Ember remains a strong choice for opinionated full-stack apps.
What is the difference between Handlebars and Glimmer templates?
Handlebars is the original templating language (Mustache-derived). Glimmer is a superset — it parses the same Handlebars syntax ({{value}}, {{#each}}...{{/each}}, {{!-- comment --}}) but adds component-level features: angle-bracket invocation (<MyComp @arg={{x}} />), named blocks (<:default>), modifiers ({{on "click" ...}}), helpers as first-class expressions, and template-only components (.hbs files without a backing class). The formatter handles both because syntactically they are similar at the brace-matching level.
How are {{#if}}...{{else}}...{{/if}} blocks indented?
The opening {{#if condition}} and closing {{/if}} match like a brace pair — content between them indents one level. The {{else}} branch (or {{else if other}}) sits at the same level as the {{#if}} keyword, with its own body indented one level. This matches the Ember community convention used by ember-template-lint and prettier-plugin-ember-template-tag.
Does it handle Octane angle-bracket components?
Yes. <MyComponent @prop={{value}} @click={{this.handle}} /> self-closes and emits inline. <UserCard @user={{this.user}}>...</UserCard> opens a block and indents children one level until </UserCard>. Named blocks (<:title>...</:title>, <:body>...</:body>) inside an angle-bracket component indent under the component, with their own children indented further. Helpers in arguments (@on={{fn this.handle "save"}}) are preserved as-is.
Does it preserve modifiers like {{on "click" this.handler}}?
Yes. Element modifiers attached with {{ }} on an HTML tag — {{on "click" this.handler}}, {{did-insert this.setup}}, {{did-update this.refresh @value}} — are kept in the same line as their host element. The formatter does not split them across lines or modify the helper invocation; it only adjusts indentation around HTML tags and Handlebars blocks.
Will it format my .hbs file or my <template> tag in a .gjs file?
Both work. A standalone .hbs file is plain Glimmer and pastes directly. For a .gjs (or .gts) file using the new <template>...</template> tag syntax, copy the contents between the <template> and </template> tags into the formatter. The surrounding JavaScript imports and class declarations should be formatted with the JavaScript or TypeScript pretty printer instead.
Is the Glimmer template I paste sent to your servers?
No. Formatting runs entirely in your browser using JavaScript. Templates referencing internal route names, action handlers, or proprietary component arguments never leave your device. Open DevTools → Network and click Format to verify no requests are made.