`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.
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}} />