ember-components

star 1

Write modern Ember UI components using Polaris template tags (.gjs/.gts), Glimmer components, and @tracked state. Avoid legacy Ember components.

Skill-js By Skill-js schedule Updated 6/2/2026

name: ember-components description: Write modern Ember UI components using Polaris template tags (.gjs/.gts), Glimmer components, and @tracked state. Avoid legacy Ember components.

Modern Ember Components

When writing UI components for modern Ember.js (Octane/Polaris), always use Glimmer Components (@glimmer/component) and native ES classes.

1. File Format: Template Tags (.gjs / .gts)

If the project supports Polaris paradigms (Ember 4.x/5.x+ with Glint/Embroider), author components using a single file with <template> tags instead of separate .js and .hbs files.

// app/components/hello-world.gjs
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { on } from '@ember/modifier';

export default class HelloWorld extends Component {
  @tracked count = 0;

  increment = () => {
    this.count++;
  }

  <template>
    <div class="hello-world">
      <h2>Hello {{@name}}!</h2>
      <p>Count: {{this.count}}</p>
      <button type="button" {{on "click" this.increment}}>+1</button>
    </div>
  </template>
}

2. State & Reactivity

  • Use @tracked for any reactive state.
  • Use native JS getters (get propertyName() {}) for derived state.
  • Never use @computed or .get() / .set(). Mutate state directly (e.g., this.count++).
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

export default class UserProfile extends Component {
  @tracked firstName = 'John';
  @tracked lastName = 'Doe';

  // Derived state auto-tracks the @tracked properties it accesses
  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

3. Component Arguments (Args)

Arguments passed to a component are accessible via this.args in JS, and @ in the template.

  • Args are read-only. Never attempt to mutate this.args directly.
  • Instead, invoke actions passed in via args.
// Caller: <MyForm @initialValue="Hello" @onSubmit={{this.save}} />

export default class MyForm extends Component {
  @tracked value = this.args.initialValue;

  submit = () => {
    this.args.onSubmit(this.value);
  }
}

4. DOM Manipulation (Modifiers)

Never use classic lifecycle hooks like didInsertElement for DOM interactions. Instead, use Element Modifiers.

import Component from '@glimmer/component';
import { modifier } from 'ember-modifier';

const autofocus = modifier((element) => {
  element.focus();
});

export default class MyInput extends Component {
  <template>
    <input type="text" {{autofocus}} />
  </template>
}

Gotchas & Guardrails

  • Strict Mode in .gjs/.gts: You must explicitly import components, helpers, and modifiers to use them in a <template>. They are not magically resolved from the global namespace.
  • Ember.Component: Never import Component from '@ember/component'. Always use @glimmer/component.
  • this context: In templates, backing class properties require this. (e.g., {{this.count}}), while arguments use @ (e.g., {{@title}}).
Install via CLI
npx skills add https://github.com/Skill-js/ember-skills --skill ember-components
Repository Details
star Stars 1
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator