Components — Glimmer Class + HBS Template

`app/components/my-button.{js,hbs}`

Components — Glimmer Class + HBS Template

An Ember component is a Glimmer class plus a colocated Handlebars template, invoked from other templates with angle-bracket syntax.

4 min read Level 2/5 #ember#components#glimmer
What you'll learn
  • Create a Glimmer component with `@glimmer/component`
  • Define its template in a sibling .hbs file
  • Invoke it with angle-bracket syntax and pass `@args`

A Glimmer component is the modern Octane component: a native class plus an HBS template living side by side in app/components/.

Define the Class

// app/components/my-button.js
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';

export default class MyButton extends Component {
  @tracked pressed = false;

  @action
  handleClick(event) {
    this.pressed = true;
    this.args.onClick?.(event);
  }
}

Class fields hold state; @tracked makes them reactive. Arguments passed by the caller arrive on this.args.

Define the Template

{{! app/components/my-button.hbs }}
<button
  type="button"
  class="btn {{if this.pressed 'is-pressed'}}"
  {{on "click" this.handleClick}}
>
  {{yield}}
</button>

{{yield}} renders whatever children the caller passes between the tags.

Use the Component

<MyButton @onClick={{this.save}}>
  Save changes
</MyButton>

Arguments use the @ prefix. They are immutable from the component’s perspective — to pass changes back up, call a passed-in callback like @onClick.

Template-Only Components

If a component has no state or logic, skip the .js file. A lone .hbs is a valid component:

{{! app/components/avatar.hbs }}
<img class="avatar" src={{@src}} alt={{@alt}} />
Services — Long-Lived Singletons →