This guide explains how to create components following the Angular 18 Archetype’s structure and best practices.
Component organization
The project organizes components into three main areas:
src/app/core/ - Core infrastructure components (layout, navigation, language switcher)
src/app/shared/ui/ - Reusable UI components used across the application
src/app/features/ - Feature-specific components (create this directory as needed)
All components in this archetype use the standalone API introduced in Angular 15+.
Creating a component with Angular CLI
Choose the appropriate location
Decide where your component belongs:
- Core components: Layout, navigation, app-wide infrastructure
- Shared UI components: Reusable presentational components
- Feature components: Business logic and feature-specific views
Generate the component
Use Angular CLI to generate a standalone component:# Shared UI component
ng generate component shared/ui/button --standalone
# Core component
ng generate component core/header --standalone
# Feature component
ng generate component features/dashboard/user-profile --standalone
The --standalone flag creates a standalone component that doesn’t require a NgModule.
Import and use the component
Import the component where you need it:import { Component } from '@angular/core';
import { ButtonComponent } from '@ui/button.component';
@Component({
selector: 'app-example',
standalone: true,
imports: [ButtonComponent],
template: `<app-button>Click me</app-button>`
})
export class ExampleComponent {}
Component structure example
Here’s a real example from the archetype - the NotificationsComponent at src/app/shared/ui/notifications.component.ts:8:
src/app/shared/ui/notifications.component.ts
import { ChangeDetectionStrategy, Component, InputSignal, OutputEmitterRef, input, output } from '@angular/core';
import { Notification } from '@domain/notification.type';
/**
* Component to show notifications to the user
* @param {Notification[]} notifications The list of notifications to show
* @emits close The event to close the notifications
*/
@Component({
selector: 'lab-notifications',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [],
template: `
<dialog open>
<article>
<header>
<h2>Notifications</h2>
</header>
@for (notification of notifications(); track notification) {
@if (notification.type === 'error') {
<input disabled aria-invalid="true" [value]="notification.message" />
} @else {
<input disabled aria-invalid="false" [value]="notification.message" />
}
}
<footer>
<button (click)="close.emit()">Close</button>
</footer>
</article>
</dialog>
`,
})
export class NotificationsComponent {
// Inputs using signal-based input API
notifications: InputSignal<Notification[]> = input<Notification[]>([]);
// Outputs using new output API
close: OutputEmitterRef<void> = output();
}
Key patterns to follow:
- Standalone component with
standalone: true
- OnPush change detection for better performance
- Signal-based inputs using
input<T>()
- Modern outputs using
output()
- Control flow syntax with
@for and @if
- JSDoc comments for component documentation
Creating a presentational component
Presentational components focus on UI and receive data via inputs:
src/app/shared/ui/card.component.ts
import { Component, InputSignal, input } from '@angular/core';
@Component({
selector: 'app-card',
standalone: true,
template: `
<div class="card">
<h3>{{ title() }}</h3>
<p>{{ description() }}</p>
<ng-content></ng-content>
</div>
`,
styles: [`
.card {
padding: 1rem;
border: 1px solid #ddd;
border-radius: 8px;
}
`]
})
export class CardComponent {
title: InputSignal<string> = input.required<string>();
description: InputSignal<string> = input<string>('');
}
Usage:
@Component({
imports: [CardComponent],
template: `
<app-card title="User Profile" description="Manage your account">
<button>Edit Profile</button>
</app-card>
`
})
Creating a smart component
Smart components handle business logic and state management:
src/app/features/profile/profile.component.ts
import { Component, inject, Signal } from '@angular/core';
import { AuthStore } from '@state/auth.store';
import { User } from '@domain/user.type';
import { CardComponent } from '@ui/card.component';
@Component({
selector: 'app-profile',
standalone: true,
imports: [CardComponent],
template: `
<app-card
[title]="user().name"
[description]="user().email">
<p>User ID: {{ userId() }}</p>
</app-card>
`
})
export class ProfileComponent {
private authStore = inject(AuthStore);
user: Signal<User> = this.authStore.user;
userId: Signal<number> = this.authStore.userId;
}
Using path aliases
The project has configured path aliases in tsconfig.json for cleaner imports:
// Instead of relative imports:
import { NotificationsComponent } from '../../shared/ui/notifications.component';
// Use path aliases:
import { NotificationsComponent } from '@ui/notifications.component';
import { User } from '@domain/user.type';
import { AuthStore } from '@state/auth.store';
import { AuthService } from '@api/auth.service';
Available aliases:
@ui/* → src/app/shared/ui/*
@domain/* → src/app/shared/domain/*
@state/* → src/app/shared/services/state/*
@services/* → src/app/shared/services/*
@api/* → src/app/shared/services/api/*
@env/* → src/environments/*
Inline vs external templates
Inline template (small components)
@Component({
template: `<h1>{{ title }}</h1>`
})
External template (larger components)
@Component({
templateUrl: './profile.component.html',
styleUrl: './profile.component.scss'
})
Use styleUrl (singular) instead of styleUrls in Angular 17+.
Best practices
- Always use standalone components - This is the recommended approach in Angular 15+
- Use OnPush change detection - Improves performance by reducing unnecessary checks
- Prefer signal-based inputs - Use
input<T>() instead of @Input()
- Use the new output API - Use
output() instead of @Output()
- Follow the control flow syntax - Use
@if, @for, @switch instead of *ngIf, *ngFor
- Add JSDoc comments - Document inputs, outputs, and component purpose
- Use path aliases - Keep imports clean and maintainable
- Organize by feature - Group related components, services, and types together