
Frontend Angular
- 1 installs
- 3 repo stars
- Updated July 8, 2026
- ai-enhanced-engineer/aiee-team
frontend-angular is a Claude Code skill covering modern Angular 21+ patterns including signals, standalone components, zoneless change detection, and the new control-flow syntax.
About
frontend-angular is a Claude Code skill covering modern Angular 21+ production patterns. It documents signal-based reactivity, standalone components, zoneless change detection, the new control-flow syntax, and the input/output/model signal APIs. A developer uses it for Angular architecture decisions or when implementing components with the latest Angular APIs.
- Angular 21+ patterns: signals, computed, effects, and standalone components
- Covers zoneless change detection and new control flow (@if, @for, @switch)
- Signal-based service pattern and Core Web Vitals performance targets
Frontend Angular by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,912 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Jul 9, 2026 (Skillselion catalog sync)
frontend-angular capabilities & compatibility
- Capabilities
- angular components · signal state · component architecture
- Use cases
- frontend
What frontend-angular says it does
Modern Angular 21+ patterns including signals, standalone components, zoneless change detection, and new control flow syntax.
Production-ready patterns for Angular 21+ applications with zoneless change detection.
npx skills add https://github.com/ai-enhanced-engineer/aiee-team --skill frontend-angularAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 3 |
| Last updated | July 8, 2026 |
| Repository | ai-enhanced-engineer/aiee-team ↗ |
What it does
Build Angular 21+ components using signals, standalone components, zoneless change detection, and the new control-flow syntax.
Who is it for?
Angular architecture decisions and implementing components with the latest Angular 21+ APIs.
Skip if: React or other frameworks; the skill lists cases where Angular is not the right choice.
When should I use this skill?
For Angular architecture decisions or implementing components with latest Angular APIs.
What you get
Angular 21+ components using signals, standalone architecture, and the new control flow.
By the numbers
- Core Web Vitals targets: LCP < 2.5s, INP < 200ms, CLS < 0.1, initial bundle < 250KB
Files
Angular Production Patterns
Production-ready patterns for Angular 21+ applications with zoneless change detection.
Core Concepts
Signal-Based Reactivity
import { Component, signal, computed, effect } from '@angular/core';
@Component({
selector: 'app-counter',
standalone: true,
template: `
<button (click)="increment()">
{{ count() }} × 2 = {{ doubled() }}
</button>
`
})
export class CounterComponent {
count = signal(0);
doubled = computed(() => this.count() * 2);
constructor() {
effect(() => console.log('Count changed:', this.count()));
}
increment() {
this.count.update(n => n + 1);
}
}Key APIs
| API | Purpose |
|---|---|
signal() | Reactive state declaration |
computed() | Derived values (auto-tracked) |
effect() | Side effects on signal changes |
input() | Component inputs (replaces @Input) |
output() | Component outputs (replaces @Output) |
model() | Two-way bindable signals |
Modern Control Flow
<!-- Conditionals -->
@if (isLoading()) {
<app-spinner />
} @else if (hasError()) {
<app-error [message]="error()" />
} @else {
<app-content [data]="data()" />
}
<!-- Loops with tracking -->
@for (item of items(); track item.id) {
<app-item [data]="item" />
} @empty {
<p>No items found</p>
}
<!-- Switch -->
@switch (status()) {
@case ('pending') { <app-pending /> }
@case ('active') { <app-active /> }
@default { <app-unknown /> }
}Standalone Architecture
// No NgModules - components declare their dependencies
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [CommonModule, RouterModule, MetricsComponent],
template: `...`
})
export class DashboardComponent {}
// Bootstrapping
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes),
provideHttpClient(withInterceptors([authInterceptor]))
]
});Component I/O (Angular 21+)
@Component({...})
export class UserCardComponent {
// Input signal (required)
user = input.required<User>();
// Input with default
showAvatar = input(true);
// Output
selected = output<User>();
// Two-way binding
isExpanded = model(false);
onSelect() {
this.selected.emit(this.user());
}
}Performance Targets
| Metric | Target |
|---|---|
| LCP | < 2.5s |
| INP | < 200ms |
| CLS | < 0.1 |
| Initial Bundle | < 250KB |
When NOT to Use Angular
- Simple interactivity → Vanilla JS
- Static marketing site → Astro/11ty
- < 100KB JS budget → Svelte or Web Components
- React ecosystem dependency → React
Signal Testing Patterns
| Pattern | Use Case |
|---|---|
| PLATFORM_ID mocking | Prevent constructor side effects in SSR tests |
WritableSignal with .set() | Control signal state without reassignment |
| NG0100 prevention | Initialize signals before detectChanges() |
Signal-Based Service Pattern
Private writable signal → public readonly → update in tap() → component injects and reads:
@Injectable({ providedIn: 'root' })
export class AnalyticsService {
private _summary = signal<Summary | null>(null);
readonly summary = this._summary.asReadonly();
private _loading = signal(false);
readonly loading = this._loading.asReadonly();
getSummary() {
this._loading.set(true);
return this.http.get<Summary>('/api/analytics/summary').pipe(
tap(data => {
this._summary.set(data);
this._loading.set(false);
}),
catchError(err => {
this._loading.set(false);
throw err;
})
);
}
}Component usage:
@Component({...})
export class DashboardComponent {
private analytics = inject(AnalyticsService);
summary = this.analytics.summary;
loading = this.analytics.loading;
}Pattern: Matches AuthService pattern for consistency across services.
See examples.md for full testing code patterns.
See reference.md for component libraries.
Angular Examples
Production-ready component implementations for Angular 21+.
Dashboard Metrics Component
import { Component, input, computed } from '@angular/core';
import { DecimalPipe, PercentPipe } from '@angular/common';
interface Metric {
label: string;
value: number;
format: 'number' | 'currency' | 'percent';
trend: 'up' | 'down' | 'stable';
}
@Component({
selector: 'app-metrics-widget',
standalone: true,
imports: [DecimalPipe, PercentPipe],
template: `
<div class="metrics-grid" role="region" aria-labelledby="metrics-heading">
<h2 id="metrics-heading" class="sr-only">Key Metrics</h2>
@for (metric of metrics(); track metric.label) {
<article
class="metric-card"
[attr.aria-label]="metric.label + ': ' + formatValue(metric)">
<span class="label">{{ metric.label }}</span>
<span class="value">{{ formatValue(metric) }}</span>
<span class="trend" [class]="metric.trend">
@switch (metric.trend) {
@case ('up') { ↑ }
@case ('down') { ↓ }
@default { → }
}
</span>
</article>
}
</div>
`,
styles: [`
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
}
.metric-card {
padding: 1.5rem;
border-radius: 8px;
background: var(--surface-card);
box-shadow: var(--shadow-sm);
}
.trend.up { color: var(--green-500); }
.trend.down { color: var(--red-500); }
.sr-only {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
}
`]
})
export class MetricsWidgetComponent {
metrics = input.required<Metric[]>();
formatValue(metric: Metric): string {
switch (metric.format) {
case 'currency':
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(metric.value);
case 'percent':
return `${(metric.value * 100).toFixed(1)}%`;
default:
return metric.value.toLocaleString();
}
}
}Angular Reference
Detailed patterns and component library guidance for Angular 21+.
Zoneless Change Detection (Default in v21)
// No Zone.js needed - signals trigger updates automatically
bootstrapApplication(AppComponent, {
providers: [
// Zoneless is default in Angular 21+
// Only add this if you need Zone.js for legacy code:
// provideZoneChangeDetection()
]
});Benefits:
- Smaller bundle (no Zone.js ~100KB)
- Predictable change detection
- Better debugging (no Zone.js stack traces)
- Signals automatically schedule updates
Dependency Injection
// Modern inject() function (preferred)
@Component({...})
export class UserService {
private http = inject(HttpClient);
private router = inject(Router);
}
// Constructor injection (still valid)
constructor(private http: HttpClient) {}HTTP Client Patterns
// Functional interceptors (Angular 21+)
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const token = inject(AuthService).getToken();
if (token) {
req = req.clone({
setHeaders: { Authorization: `Bearer ${token}` }
});
}
return next(req);
};
// Registration
provideHttpClient(
withInterceptors([authInterceptor, loggingInterceptor])
)Resource API (Async Data)
// Signal-based async data loading
@Component({...})
export class UsersComponent {
private usersService = inject(UsersService);
searchQuery = signal('');
usersResource = resource({
request: () => ({ query: this.searchQuery() }),
loader: async ({ request }) => {
return this.usersService.search(request.query);
}
});
// In template:
// @if (usersResource.isLoading()) { ... }
// @if (usersResource.hasValue()) { ... }
// @if (usersResource.error()) { ... }
}Component Libraries Comparison
| Library | Cost | Components | Best For |
|---|---|---|---|
| PrimeNG | Free (MIT) | 80+ | B2B dashboards, forms |
| Kendo UI | $1,028+/dev/year | 110+ | Enterprise, data grids |
| Angular Material | Free | ~40 | Material Design apps |
Reactive Forms with Signals
import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
@Component({
standalone: true,
imports: [ReactiveFormsModule],
template: `
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<input formControlName="email" />
@if (form.controls.email.errors?.['required']) {
<span class="error">Email is required</span>
}
<button type="submit" [disabled]="form.invalid">Submit</button>
</form>
`
})
export class LoginComponent {
private fb = inject(FormBuilder);
form = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]]
});
onSubmit() {
if (this.form.valid) {
console.log(this.form.value);
}
}
}Router Patterns
// Route configuration
export const routes: Routes = [
{ path: '', component: HomeComponent },
{
path: 'dashboard',
loadComponent: () => import('./dashboard/dashboard.component')
.then(m => m.DashboardComponent),
canActivate: [authGuard]
},
{ path: '**', component: NotFoundComponent }
];
// Functional guard
export const authGuard: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);
if (auth.isAuthenticated()) {
return true;
}
return router.createUrlTree(['/login'], {
queryParams: { returnUrl: state.url }
});
};Error Handling
// Global error handler
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
private snackBar = inject(MatSnackBar);
handleError(error: Error) {
console.error('Unhandled error:', error);
this.snackBar.open('An error occurred', 'Dismiss', {
duration: 5000
});
}
}
// Provider
{ provide: ErrorHandler, useClass: GlobalErrorHandler }Related skills
FAQ
What Angular version does this skill target?
Angular 21+, with zoneless change detection as the default and the new @if/@for/@switch control flow.
When does it say not to use Angular?
For simple interactivity (vanilla JS), static marketing sites (Astro/11ty), a <100KB JS budget (Svelte or Web Components), or React-ecosystem dependencies.