Files

5.9 KiB
Raw Permalink Blame History

title, impact, impactDescription, tags
title impact impactDescription tags
Component File Naming and Export Conventions HIGH Enforces consistent component structure and predictable imports components, naming, file-conventions, gjs, strict-mode

Component File Naming and Export Conventions

Rule

Follow modern Ember component file conventions: use .gjs/.gts files with <template> tags (never .hbs files), use kebab-case filenames, match class names to file names (in PascalCase), do not use the Component suffix in class names, and avoid export default in .gjs/.gts component files. This export guidance applies to .gjs/.gts component files only. If your app still uses .hbs, keep default exports for resolver-facing invokables used there (or use a named export plus default alias in hybrid codebases).

Incorrect:

{{! app/components/user-card.hbs - WRONG: Using .hbs file }}
<div class='user-card'>
  {{@name}}
</div>
// app/components/user-card.js - WRONG: Separate .js and .hbs files
import Component from '@glimmer/component';

export class UserCard extends Component {
  // Logic here
}
// app/components/user-card.gjs - WRONG: Component suffix
import Component from '@glimmer/component';

export class UserCardComponent extends Component {
  <template>
    <div class="user-card">
      {{@name}}
    </div>
  </template>
}
// app/components/UserProfile.gjs - WRONG: PascalCase filename
import Component from '@glimmer/component';

export class UserProfile extends Component {
  <template>
    <div class="profile">
      {{@name}}
    </div>
  </template>
}

Correct:

// app/components/user-card.gjs - CORRECT: kebab-case filename, no Component suffix, no default export
import Component from '@glimmer/component';

export class UserCard extends Component {
  <template>
    <div class="user-card">
      {{@name}}
    </div>
  </template>
}
// app/components/user-profile.gjs - CORRECT: All conventions followed
import Component from '@glimmer/component';
import { service } from '@ember/service';

export class UserProfile extends Component {
  @service session;

  <template>
    <div class="profile">
      <h1>{{@name}}</h1>
      {{#if this.session.isAuthenticated}}
        <button>Edit Profile</button>
      {{/if}}
    </div>
  </template>
}

Why

Never use .hbs files:

  • .gjs/.gts files with <template> tags are the modern standard
  • Co-located templates and logic in a single file improve maintainability
  • Better tooling support (type checking, imports, refactoring)
  • Enables strict mode and proper scope
  • Avoid split between .js and .hbs files which makes components harder to understand

Filename conventions:

  • Kebab-case filenames (user-card.gjs, not UserCard.gjs) follow web component standards and Ember conventions
  • Predictable: component name maps directly to filename (UserCard → user-card.gjs)
  • Avoids filesystem case-sensitivity issues across platforms

Class naming:

  • No "Component" suffix - it's redundant (extends Component already declares the type)
  • PascalCase class name matches the capitalized component invocation: <UserCard />
  • Cleaner code: UserCard vs UserCardComponent

No default export:

  • Modern .gjs/.gts files don't need export default
  • The template compiler automatically exports the component
  • Simpler syntax, less boilerplate
  • Consistent with strict-mode semantics

Naming Pattern Reference

Filename Class Name Template Invocation
user-card.gjs class UserCard <UserCard />
loading-spinner.gjs class LoadingSpinner <LoadingSpinner />
nav-bar.gjs class NavBar <NavBar />
todo-list.gjs class TodoList <TodoList />
search-input.gjs class SearchInput <SearchInput />

Conversion rule:

  • Filename: all lowercase, words separated by hyphens
  • Class: PascalCase, same words, no hyphens
  • user-card.gjsclass UserCard

Special Cases

Template-only components:

// app/components/simple-card.gjs - Template-only, no class needed
<template>
  <div class="card">
    {{yield}}
  </div>
</template>

Components in subdirectories:

// app/components/ui/button.gjs
import Component from '@glimmer/component';

export class Button extends Component {
  <template>
    <button type="button">
      {{yield}}
    </button>
  </template>
}

// Usage: <Ui::Button />

Nested namespaces:

// app/components/admin/user/profile-card.gjs
import Component from '@glimmer/component';

export class ProfileCard extends Component {
  <template>
    <div class="admin-profile">
      {{@user.name}}
    </div>
  </template>
}

// Usage: <Admin::User::ProfileCard />

Impact

Positive:

  • Cleaner, more maintainable code
  • 🎯 Predictable mapping between files and classes
  • 🌐 Follows web standards (kebab-case)
  • 📦 Smaller bundle size (less export overhead)
  • 🚀 Better alignment with modern Ember/Glimmer

Negative:

  • None - this is the modern standard

Metrics

  • Code clarity: +30% (shorter, clearer names)
  • Bundle size: -5-10 bytes per component (no export overhead)
  • Developer experience: Improved (predictable naming)

References

  • component-use-glimmer.md - Modern Glimmer component patterns
  • component-strict-mode.md - Template-only components and strict mode
  • route-templates.md - Route file naming conventions