CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-glimmer--component

Glimmer component library providing the modern component base class for Ember.js applications

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

index.mddocs/

@glimmer/component

@glimmer/component is the modern component library for Ember.js applications, providing a TypeScript-first reactive component architecture based on tracked properties and autotracking. It serves as the lightweight base class that replaced classic Ember components in Ember Octane (3.15+) and newer editions.

Package Information

  • Package Name: @glimmer/component
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install @glimmer/component
  • Repository: https://github.com/emberjs/ember.js
  • License: MIT

Core Imports

import Component from "@glimmer/component";

For named imports:

import Component, { type Args, type EmptyObject, type ExpandSignature } from "@glimmer/component";

Basic Usage

import Component from "@glimmer/component";

// Template-only component (no class needed)
// Create app/templates/components/person-profile.hbs:
// <h1>{{@person.name}}</h1>
// <img src={{@person.avatar}}>

// Class-based component
export default class PersonProfileComponent extends Component {
  get displayName() {
    let { title, firstName, lastName } = this.args.person;
    
    if (title) {
      return `${title} ${lastName}`;
    } else {
      return `${firstName} ${lastName}`;
    }
  }
}

Template usage:

<PersonProfile @person={{this.currentUser}} />

Architecture

@glimmer/component is built around several key concepts:

  • Reactive Component Model: Components automatically re-render when tracked properties change
  • Argument-based API: Data flows down through @args from parent components
  • Lifecycle Hooks: Simple lifecycle with constructor and willDestroy hooks
  • Template Integration: Works seamlessly with Handlebars templates and Ember's rendering engine
  • Type Safety: Full TypeScript support with generic signature types for components
  • Ember Integration: Deep integration with Ember's dependency injection, runloop, and destruction systems

Capabilities

Component Base Class

The main GlimmerComponent class providing the foundation for all Glimmer components.

/**
 * A component is a reusable UI element that consists of a `.hbs` template and an
 * optional JavaScript class that defines its behavior.
 */
export default class GlimmerComponent<S = unknown> {
  /**
   * Constructs a new component and assigns itself the passed properties.
   * @param owner - Ember owner/container instance
   * @param args - Named arguments object passed from parent component
   */
  constructor(owner: Owner, args: Args<S>);

  /**
   * Named arguments passed to the component from its parent component.
   * They can be accessed in JavaScript via `this.args.argumentName` and in the template via `@argumentName`.
   */
  readonly args: Readonly<Args<S>>;

  /**
   * A boolean flag to tell if the component is in the process of destroying.
   * This is set to true before `willDestroy` is called.
   */
  get isDestroying(): boolean;

  /**
   * A boolean to tell if the component has been fully destroyed.
   * This is set to true after `willDestroy` is called.
   */
  get isDestroyed(): boolean;

  /**
   * Called before the component has been removed from the DOM.
   * This lifecycle hook can be used to cleanup the component and any related state.
   */
  willDestroy(): void;
}

Usage Examples:

import Component from "@glimmer/component";
import { service } from "@ember/service";

// Basic component with computed property
export default class UserCard extends Component {
  get fullName() {
    return `${this.args.firstName} ${this.args.lastName}`;
  }
}

// Component with service injection and lifecycle
export default class AnimatedComponent extends Component {
  @service myAnimations;

  constructor(owner, args) {
    super(owner, args);
    
    if (this.args.fadeIn === true) {
      this.myAnimations.register(this, 'fade-in');
    }
  }

  willDestroy() {
    super.willDestroy(...arguments);
    this.myAnimations.unregister(this);
  }
}

Type Signature Support

Type utilities for creating type-safe component signatures with proper argument typing.

/**
 * Type utility for extracting named arguments from component signature
 */
type Args<S> = ExpandSignature<S>['Args']['Named'];

/**
 * Empty object type with excess property checking
 * @internal - Exported for declaration emit only
 */
type EmptyObject = { [Empty]?: true };

/**
 * Utility type for expanding component signature shorthand forms
 * @internal - Exported for tooling compatibility only
 */
type ExpandSignature<T> = T extends any ? _ExpandSignature<T> : never;

Usage Examples:

// Define component signature
interface Signature {
  Args: {
    firstName: string;
    lastName: string;
    age?: number;
  };
}

export default class UserComponent extends Component<Signature> {
  // this.args is now type-safe with firstName, lastName, age properties
  get canVote() {
    return (this.args.age ?? 0) >= 18;
  }
}

// Using the component with type checking
// <UserComponent @firstName="John" @lastName="Doe" @age={{25}} />

Component Arguments

The args property provides access to all named arguments passed from parent components.

/**
 * Named arguments passed to the component from its parent component.
 * Automatically tracked for reactivity.
 */
readonly args: Readonly<Args<S>>;

Arguments are:

  • Readonly: Cannot be mutated directly
  • Tracked: Automatically trigger re-renders when changed by parent
  • Type-safe: When using signature types, provide full TypeScript checking

Usage Examples:

// In component class
export default class WelcomeMessage extends Component {
  get greeting() {
    const timeOfDay = this.args.time === 'morning' ? 'Good morning' : 'Hello';
    return `${timeOfDay}, ${this.args.name}!`;
  }
}

// In template
// {{this.greeting}}

// Component usage
// <WelcomeMessage @name="Alice" @time="morning" />

Lifecycle Hooks

Component lifecycle management with constructor and destruction hooks.

/**
 * Component constructor called when instance is created
 * @param owner - Ember owner/container instance  
 * @param args - Named arguments object
 */
constructor(owner: Owner, args: Args<S>);

/**
 * Called before the component has been removed from the DOM.
 * Use for cleanup tasks like unregistering event listeners or canceling timers.
 */
willDestroy(): void;

Usage Examples:

import Component from "@glimmer/component";
import { service } from "@ember/service";

export default class TimerComponent extends Component {
  @service notifications;
  
  timer = null;

  constructor(owner, args) {
    super(owner, args);
    
    // Initialize component state
    if (this.args.autoStart) {
      this.startTimer();
    }
  }

  startTimer() {
    this.timer = setInterval(() => {
      this.notifications.show('Timer tick');
    }, 1000);
  }

  willDestroy() {
    super.willDestroy(...arguments);
    
    // Cleanup
    if (this.timer) {
      clearInterval(this.timer);
    }
  }
}

Component State Tracking

Properties for tracking component destruction state.

/**
 * Boolean flag indicating if component is in the process of destroying.
 * Set to true before willDestroy is called.
 */
get isDestroying(): boolean;

/**
 * Boolean flag indicating if component has been fully destroyed.
 * Set to true after willDestroy is called.
 */
get isDestroyed(): boolean;

Usage Examples:

export default class DataComponent extends Component {
  async loadData() {
    // Avoid async operations if component is being destroyed
    if (this.isDestroying) {
      return;
    }
    
    const data = await fetch('/api/data');
    
    // Check again after async operation
    if (!this.isDestroyed) {
      this.processData(data);
    }
  }
}

Template Integration

Components work seamlessly with Handlebars templates:

Angle Bracket Syntax (Recommended)

<PersonProfile @person={{this.currentUser}} @mode="detailed" />

Curly Brace Syntax (Legacy)

{{person-profile person=this.currentUser mode="detailed"}}

Block Form with Yield

<PersonProfile @person={{this.currentUser}} as |profileData|>
  <p>Additional content: {{profileData.summary}}</p>
</PersonProfile>

Named Blocks

<PersonProfile @person={{this.currentUser}}>
  <:title as |name|>{{name}} - Custom Title</:title>
  <:default as |signature|>{{signature}}</:default>
</PersonProfile>

Error Handling

Components should handle errors gracefully:

  • Constructor errors: Validation errors for required arguments
  • Lifecycle errors: Errors during willDestroy cleanup
  • Argument type errors: TypeScript compilation errors for invalid argument types

Component System Integration

@glimmer/component integrates deeply with Ember's component system through a custom component manager. The component manager handles:

  • Component Instantiation: Creates component instances with proper owner and args
  • Lifecycle Management: Schedules destruction using Ember's runloop
  • State Tracking: Manages isDestroying and isDestroyed flags
  • Ember Integration: Ensures proper integration with Ember's rendering engine

Components are automatically registered with the component manager when the class is imported, enabling seamless integration with Ember templates.

Dependencies

@glimmer/component relies on these Ember.js ecosystem packages:

  • @glimmer/env: Environment detection utilities
  • @ember/component: Component system integration
  • @ember/owner: Dependency injection system
  • @ember/runloop: Runloop scheduling for destruction
  • @ember/destroyable: Destruction utilities
  • @embroider/addon-shim: V2 addon compatibility

Types

/**
 * Utility type for extracting named arguments from component signature
 */
type Args<S> = ExpandSignature<S>['Args']['Named'];

/**
 * Empty object type with excess property checking
 * @internal - Exported for declaration emit only
 */
type EmptyObject = { [Empty]?: true };

/**
 * Utility type for expanding component signature shorthand forms
 * @internal - Exported for tooling compatibility only
 */
type ExpandSignature<T> = T extends any ? _ExpandSignature<T> : never;

/**
 * Constructor interface for component classes
 */
interface Constructor<T> {
  new (owner: unknown, args: Record<string, unknown>): T;
}

/**
 * Ember owner/container interface for dependency injection
 */
interface Owner {
  // Ember owner methods for service injection and resolution
}

docs

index.md

tile.json