A comprehensive demonstration of modern Angular 20+ development patterns, best practices, and architectural decisions. This application showcases the latest Angular features including signals, zoneless change detection, standalone components, and modern development tooling.
FULL GUIDE: Angular 20 Best Practices Guide
- Zoneless Change Detection (promoted to developer preview in v20)
- Incremental Hydration (stable in v20)
- Signal APIs (effect, linkedSignal, toSignal now stable)
- Enhanced Template Expressions (exponential operator
**
,in
operator, template literals) - Host Binding Type Checking (enabled with
typeCheckHostBindings
) - Style Guide Updates (no suffixes by default)
- Experimental Vitest Support (official Angular v20 feature)
- Control Flow Syntax (
@if
,@for
,@switch
,@empty
) - Functional Providers and dependency injection
- Resource API patterns for async operations
- Modern HTTP Client with functional interceptors
- Atomic Design Principles (atoms, molecules, organisms)
- Feature-based Folder Structure with barrel exports
- Signal-first State Management with minimal RxJS
- Container/Presentational Component patterns
- Functional Guards and route protection
- Modern Form Handling with reactive forms and signals
- TypeScript 5.0+ with strict configuration
- ESLint 9+ with Angular-specific rules
- Prettier for consistent code formatting
- Vitest for fast, modern testing
- Path Aliases for clean imports
- Performance Budgets and optimization
- Quick Start
- Project Structure
- Key Components
- Testing Strategy
- Development Guidelines
- Performance Considerations
- Accessibility Features
- Node.js 18.19.0 or higher
- npm 9.0.0 or higher
# Clone the repository
git clone <repository-url>
cd angular-20-best-practices
# Install dependencies
npm install
# Start development server
npm start
npm start # Start development server
npm run build # Build for production
npm test # Run tests with Vitest
npm run test:ui # Run tests with UI
npm run test:coverage # Generate test coverage
npm run lint # Lint the codebase
npm run format # Format code with Prettier
npm run typecheck # Type check without emitting files
src/
โโโ shared/ # Reusable components and services
โ โโโ ui/ # Atomic design components
โ โ โโโ button.ts # Atom: Base button component
โ โ โโโ input.ts # Atom: Input field component
โ โ โโโ field.ts # Molecule: Input + label + validation
โ โ โโโ modal.ts # Molecule: Modal dialog
โ โ โโโ index.ts # Barrel export
โ โโโ data/ # Global services and state
โ โ โโโ auth.ts # Authentication service
โ โ โโโ loading.ts # Loading state management
โ โ โโโ index.ts # Barrel export
โ โโโ utils/ # Utility functions
โโโ features/ # Feature modules
โ โโโ auth/ # Authentication feature
โ โ โโโ login.ts # Login component
โ โโโ dashboard/ # Dashboard feature
โ โ โโโ dashboard.ts # Dashboard component
โ โโโ user-management/ # User CRUD operations
โ โ โโโ user-list.ts # User list component
โ โ โโโ user-form.ts # User creation/editing
โ โ โโโ data/ # User-specific services
โ โโโ profile/ # User profile management
โโโ core/ # Core application modules
โโโ layout/ # Layout components
โ โโโ header.ts # Application header
โ โโโ footer.ts # Application footer
โโโ guards/ # Route guards
โโโ auth-guard.ts # Authentication guard
- Button: Accessible button with variants and signal-based API
- Input: Form input with ControlValueAccessor integration
- Spinner: Loading indicator with size and color options
- Field: Input + label + validation messaging
- Modal: Dialog with keyboard navigation and backdrop
- LoadingIndicator: Global loading state display
- UserList: Data table with filtering and actions
- Dashboard: Statistics and activity overview
- Header: Navigation with authentication state
// Example: Signal-based service
@Injectable({ providedIn: 'root' })
export class UserService {
private users = signal<User[]>([]);
private filters = signal<UserFilters>({ search: '', role: 'all' });
// Computed signal for derived state
filteredUsers = computed(() => {
const users = this.users();
const { search, role } = this.filters();
return users.filter(user => {
const matchesSearch = !search || user.name.includes(search);
const matchesRole = role === 'all' || user.role === role;
return matchesSearch && matchesRole;
});
});
}
// Example: Modern component with signals
@Component({
selector: 'app-example',
template: `
@if (isLoading()) {
<app-spinner />
} @else {
@for (item of items(); track item.id) {
<div>{{ item.name }}</div>
} @empty {
<p>No items found</p>
}
}
`,
host: {
'[class.loading]': 'isLoading()',
'(click)': 'handleClick($event)',
},
})
export class ExampleComponent {
items = input.required<Item[]>();
isLoading = input(false);
itemSelected = output<Item>();
handleClick(event: Event) {
// Handle click
}
}
The application uses Vitest as the primary testing framework, following the official Angular v20 recommendation:
# Install Vitest dependencies (already included)
npm i vitest jsdom --save-dev
# Run tests with Vitest
npm test
# Run tests with UI
npm run test:ui
# Generate coverage
npm run test:coverage
The project uses the new Angular v20 testing setup:
// angular.json
"test": {
"builder": "@angular/build:unit-test",
"options": {
"tsConfig": "tsconfig.spec.json",
"buildTarget": "angular-20-best-practices:build:development",
"runner": "vitest"
}
}
- Unit tests for components, services, and pipes
- Signal-based testing for reactive state
- Accessibility testing with proper ARIA attributes
- Component harnesses for complex UI interactions
- MSW (Mock Service Worker) for API mocking
- Follow Airbnb JavaScript Style Guide adapted for Angular/TypeScript
- Use descriptive and meaningful names for better readability
- Keep components under 300 lines - split if exceeded
- Prefer pure components and pure functions
- Document purpose and usage with JSDoc comments
- Use signal-based inputs/outputs exclusively
- Implement host object for better performance
- Follow atomic design principles for reusability
- Apply accessibility patterns by default
- Use modern template syntax with control flow
- Use signals for state management over RxJS observables
- Apply functional providers instead of NgModules
- Implement proper error handling with user feedback
- Follow single responsibility principle
- Use dependency injection with
inject()
function
- Zoneless change detection enabled by default
- OnPush strategy not needed with zoneless
- Lazy loading with dynamic imports
- Bundle analysis and performance budgets
- Image optimization with NgOptimizedImage
// Example: Optimized component
@Component({
// Zoneless - no need for OnPush
template: `
@defer (when shouldLoad()) {
<heavy-component />
} @placeholder {
<app-spinner />
}
`,
})
export class OptimizedComponent {
shouldLoad = signal(false);
// Computed signals are automatically memoized
expensiveComputation = computed(() => {
return this.data().map(item => complexTransform(item));
});
}
- Semantic HTML structure throughout
- ARIA attributes for screen readers
- Keyboard navigation support
- Focus management in modals and forms
- Color contrast compliance
- Screen reader testing integration
// Example: Accessible component
@Component({
template: `
<button
[attr.aria-label]="ariaLabel()"
[attr.aria-expanded]="isExpanded()"
[attr.aria-controls]="controlsId()"
role="button"
tabindex="0"
>
<ng-content />
</button>
`,
host: {
'(keydown.enter)': 'handleActivation()',
'(keydown.space)': 'handleActivation()',
},
})
export class AccessibleButton {
ariaLabel = input<string>();
isExpanded = input(false);
controlsId = input<string>();
}
- Web Standards API usage where possible
- Progressive enhancement strategies
- Modern JavaScript features (ES2023+)
- Framework interoperability considerations
- Migration-friendly patterns
- Zoneless change detection (production-ready)
- Resource API for data fetching
- Functional component authoring (future)
- Web Components integration
- Angular.dev - Official Angular documentation
- Angular Style Guide - Official style guide
- Angular Signals - Signals documentation
- This application demonstrates all patterns from the Angular 20+ style guide
- Each component includes JSDoc comments explaining the patterns used
- Check individual files for specific implementation details
- Follow the established code style and patterns
- Write tests for new features
- Update documentation as needed
- Run linting and formatting before committing
- Use conventional commit messages
This project is for educational purposes and demonstrates Angular 20+ best practices. Feel free to use these patterns in your own projects.
Built with Angular 20+ ๐