Templates & Components
Build engine templates and Glimmer components using @fleetbase/ember-ui — layout primitives, common widgets, and how to render extension components inside other engines.
Templates & Components
Engine templates use standard Handlebars + Ember. The console ships a large component library — @fleetbase/ember-ui — that gives you matching chrome (layouts, headers, tables, modals, form inputs) without re-implementing styles.
Templates
Engine templates live under addon/templates/, mirroring the route map:
addon/
├── routes.js # this.route('orders', { ... })
├── routes/
│ └── orders.js
├── controllers/
│ └── orders.js
└── templates/
├── application.hbs # outermost layout for the engine
└── orders.hbs # rendered for the orders routeThe engine's application.hbs is your top-level layout — wrap content in the standard <Layout::Section> chrome so it sits correctly inside the console:
{{!-- addon/templates/application.hbs --}}
<Layout::Container>
<Layout::Sidebar />
<Layout::Section>
{{outlet}}
</Layout::Section>
</Layout::Container>Glimmer Components
Components live under addon/components/. Use Octane (@glimmer/component):
// addon/components/order-summary.js
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
export default class OrderSummaryComponent extends Component {
@service fetch;
@service notifications;
@tracked isLoading = false;
@action
async refresh() {
this.isLoading = true;
try {
await this.fetch.get(`my-extension/v1/orders/${this.args.order.id}/refresh`);
this.notifications.success('Refreshed');
} finally {
this.isLoading = false;
}
}
}{{!-- addon/components/order-summary.hbs --}}
<div class="rounded-md border p-4">
<h3 class="font-semibold">{{@order.public_id}}</h3>
<Button @text="Refresh" @icon="rotate" @isLoading={{this.isLoading}} @onClick={{this.refresh}} />
</div>Using @fleetbase/ember-ui
@fleetbase/ember-ui is already on your engine's resolver path — components are available globally without imports. The library covers the platform's design system; reach for it before building chrome from scratch.
Full component reference: Fleetbase UI docs. Each component below links to its dedicated page with props, slots, examples, and edge cases.
Layout primitives
| Component | Purpose | Docs |
|---|---|---|
<Layout::Container> | Outer wrapper with sidebar + section grid | Layout overview |
<Layout::Sidebar> | Collapsible engine sidebar | Layout overview |
<Layout::Section> | Main content column with header/body slots | Layout overview |
<Layout::Section::Header @title="…" /> | Page header bar | Layout overview |
<Layout::Section::Body> | Scrollable body container | Layout overview |
<ContentPanel @title @open> | Collapsible labeled section | Content Panel |
<Drawer> | Slide-out side panel | Drawer |
<Overlay> | Backdrop + portal target | Overlay |
<Spacer @height> | Vertical spacing | Spacer |
Actions
| Component | Purpose | Docs |
|---|---|---|
<Button @text @icon @type @onClick @isLoading /> | Variants: primary, magic, danger, etc. | Button |
<ClickToCopy @value /> | Copy-on-click affordance | Click to Copy |
<ClickToReveal /> | Reveal hidden content (secrets, tokens) | Click to Reveal |
Display
| Component | Purpose | Docs |
|---|---|---|
<Spinner /> | Loading indicator | Spinner |
<Badge @text @type /> | Status badges | Badge |
<Pill @text /> | Compact label chip | Pill |
<Table @data @columns /> | Sortable, paginatable data table | Table |
<Timeline /> | Vertical event timeline | Timeline |
<ActivityLog /> | Activity feed | Activity Log |
<ProgressBar /> | Progress indicator | Progress Bar |
Form Inputs
| Component | Purpose | Docs |
|---|---|---|
<Select @options @onChange /> | Styled select | Select |
<MultiSelect /> | Multiple selection with chips | Multi Select |
<ComboBox /> | Searchable dropdown | Combo Box |
<Checkbox /> / <Toggle /> | Boolean inputs | Checkbox, Toggle |
<DatePicker /> / <DateTimeInput /> | Date / date-time pickers | Date Picker |
<MoneyInput /> | Currency-aware amount input | Money Input |
<UnitInput /> | Number with unit selector | Unit Input |
<PhoneInput /> | International phone input | Phone Input |
<FileUpload /> | Drag-and-drop file upload | File Upload |
<CoordinatesInput /> | Lat/lng + map | Coordinates Input |
<ModelSelect /> | Searchable Ember Data record picker | Model Select |
<InputGroup /> | Label + input + help text wrapper | Input Group |
For complete documentation — including extending components, slot content, and accessibility notes — see the Fleetbase UI documentation.
Cross-engine Components
When another extension wants to render a component from your engine — say, on the Fleet-Ops order details page — the boundary crosses engines. You don't pass component names directly; you pass an ExtensionComponent, which the host resolves lazily.
import { ExtensionComponent } from '@fleetbase/ember-core/contracts';
const ref = new ExtensionComponent(
'@fleetbase/storefront-engine',
'storefront-order-summary' // resolves to addon/components/storefront-order-summary.{js,hbs}
);The receiving registry (e.g. 'fleet-ops:component:order:details') renders ref via <LazyEngineComponent>, which loads the source engine on demand if it isn't already booted.
{{!-- inside the host engine --}}
<LazyEngineComponent
@component={{this.extensionComponent}}
@params={{this.componentArgs}}
/>You don't usually invoke <LazyEngineComponent> yourself — the Registry Service and Virtual Routes wire it for you.
Tailwind & Styling
The console uses Tailwind utility classes and a small set of design tokens. Most ember-ui components accept utility class strings via @class or pass-through HTML attributes (class="…"). For one-off styles, scope them to your engine via component-tree CSS or Tailwind utilities.
If you need engine-scoped global CSS, drop a stylesheet under addon/styles/ — ember-cli-build.js will pick it up.
Services Available in Components
These host services are wired into your engine via @fleetbase/ember-core/exports and can be injected into any component. Detailed reference: Ember Services.
| Service | Use for |
|---|---|
fetch | Authenticated HTTP requests — pass { namespace } to hit your extension's API |
store | Ember Data Store — findRecord, query, etc. |
notifications | Toast notifications (success, error, info, warning) |
currentUser | Logged-in user |
session | Auth session, including session.data.authenticated |
modalsManager | Imperative modal API |
theme | Set body classes, toggle dark mode |
intl | i18n / translations |
abilities | Role-based permission checks (ember-can) |
crud | Generic CRUD action helpers |
events | App-wide event bus |
socket | Real-time pub/sub |
appCache | Browser-storage-backed cache |
universe | The universe service itself + sub-services |
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
export default class extends Component {
@service fetch;
@service notifications;
@service abilities;
}See Also
- Fleetbase UI — full component reference with props, slots, examples
- Ember Services — full reference for host services injectable into components
- Ember Guides → Components — upstream Ember documentation on Glimmer components
- Ember Component API
Source
| File | Description |
|---|---|
packages/ember-ui/addon/components | All shared UI components (source) |
packages/ember-core/addon/exports/services.js | Services available to engines |