Components
Components are the fundamental building blocks of Aurelia applications. A component consists of a view-model (TypeScript class) and an optional view (HTML template) that work together to create reusable UI elements.
Basic Component Structure
Every Aurelia component starts with a simple class:
export class MyComponent {
message = 'Hello from Aurelia!';
}And its corresponding HTML template (no <template> wrapper is needed in Aurelia 2):
<h1>${message}</h1>The ${message} syntax creates a binding between your view-model property and the template, automatically updating the UI when the property changes.
When to Create a Component?
Before creating a component, consider these guidelines:
Create a component when:
✅ You need reusable UI that appears in multiple places
✅ The UI has its own behavior and state
✅ You want to encapsulate complexity (a component should do one thing well)
✅ The UI represents a meaningful concept in your domain (e.g.,
<user-card>,<product-list>)
Use a custom attribute instead when:
✅ You're adding behavior to existing elements without changing structure
✅ You're creating a decorator or modifier (e.g.,
tooltip,draggable)✅ Multiple behaviors can be combined on the same element
✅ Examples:
<button tooltip="Save changes">,<div draggable sortable>
Use a value converter when:
✅ You're just formatting data for display
✅ The transformation is pure (same input → same output)
✅ Examples:
${date | dateFormat},${price | currency}
Custom Elements
To create reusable custom elements, use the @customElement decorator:
Using Components
After creating a component, you need to make it available for use. There are two ways to do this:
Option 1: Import in Templates (Recommended for Most Cases)
Import the component where you need it using the <import> element:
This is the recommended approach because:
Components are only loaded where they're used
Better code organization and maintainability
Clear dependencies in each template
Option 2: Global Registration
Register components globally in your main.ts to use them anywhere without imports:
Now <hello-world></hello-world> works in any template without <import>.
When to use global registration:
Components used on almost every page (headers, footers, layout components)
Shared UI components used throughout the app
Components you want available in all templates by default
When to use local imports:
Feature-specific components
Most custom components
Better tree-shaking and bundle optimization
Bindable Properties
Make component properties configurable from the outside using @bindable:
Use the component by binding values to its properties:
Component Lifecycle
Components have lifecycle hooks for initialization and cleanup:
Common Component Patterns
Pattern: Container/Presenter (Smart/Dumb Components)
Use case: Separate data management from presentation logic.
Container (Smart) Component - Manages data and business logic:
Presenter (Dumb) Component - Pure presentation, no data fetching:
Why this works: Container components handle complexity (data, routing, state), while presenter components are simple, reusable, and easy to test. You can reuse <user-list> anywhere without worrying about data fetching.
Pattern: Composition with Slots
Use case: Create flexible container components that accept custom content.
Usage:
Why this works: Slots make components flexible without needing dozens of bindable properties. The component controls the structure while consumers control the content.
Pattern: Form Components with Two-Way Binding
Use case: Reusable form inputs that work seamlessly with parent form state.
Usage:
Why this works: Two-way binding with BindingMode.twoWay keeps the parent and child in sync automatically. Changes in either place propagate to both.
Pattern: Stateful UI Components
Use case: Components that manage their own internal state.
Why this works: The component owns its UI state (which item is expanded) while accepting data as props. This keeps the parent simple - it just provides data, not UI state.
Pattern: Event-Emitting Components
Use case: Child components that notify parents of user actions.
Usage:
Why this works: Components can communicate via callbacks (tight coupling) or events (loose coupling) depending on your needs. Use callbacks for parent-child communication, events for unrelated components.
Best Practices
Keep Components Focused
✅ Each component should have one clear responsibility
✅ If a component is doing too much, split it into smaller components
❌ Avoid "god components" that handle everything
Favor Composition Over Inheritance
✅ Use slots and component composition
✅ Build complex UIs from simple, reusable pieces
❌ Avoid deep inheritance hierarchies
Make Components Predictable
✅ Use bindable properties for inputs
✅ Use callbacks or events for outputs
✅ Document what bindables are required vs optional
❌ Don't manipulate parent state directly
Test-Friendly Components
✅ Presenter components are easy to test (just props)
✅ Keep business logic in services, not components
✅ Use dependency injection for testability
What's Next
Learn more about component lifecycles
Explore bindable properties in detail
Understand shadow DOM and slots for advanced composition
Last updated
Was this helpful?