Help us improve
Share bugs, ideas, or general feedback.
From ng
Generate Angular artifacts (components, services, pipes, directives, guards, interceptors, routes, stores). Use automatically when the user asks to create any Angular code, scaffold a feature, or add a new file to an Angular project. Mirrors Angular CLI conventions.
npx claudepluginhub mayeedwin/angular-plugin --plugin ngHow this skill is triggered — by the user, by Claude, or both
Slash command
/ng:angular-generateThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Primary reference**: https://angular.dev/llms.txt — always consult Angular documentation for current API.
Provides expert Angular/TypeScript patterns for standalone components, signals, RxJS, NgRx state management, smart/dumb components, and performance.
Generates Angular code and provides architectural guidance on signals, forms, DI, routing, SSR, accessibility, animations, styling, testing, and CLI tooling.
Generates Angular code and architecture guidance for projects, components, services, signals, forms, DI, routing, SSR, accessibility, animations, styling, testing, and CLI tools.
Share bugs, ideas, or general feedback.
Primary reference: https://angular.dev/llms.txt — always consult Angular documentation for current API.
app.config.ts (standalone app) vs app.module.ts (module-based app)tsconfig.json to find path aliases (@shared/*, @core/*, etc.)package.json for Angular version and installed packages (NgRx, signals API availability)jest.config.js) or Karma (karma.conf.js) is the test runnerng generate)Angular 20+: Component files use
{name}.ts(no.component.suffix) with matching{name}.html,{name}.scss,{name}.spec.ts. All other artifacts retain their type suffix (.service.ts,.pipe.ts, etc.).
| Angular CLI | Plugin equivalent | Output |
|---|---|---|
ng g c {name} | /ng:generate component {name} | {name}.ts + {name}.html + {name}.scss + {name}.spec.ts |
ng g s {name} | /ng:generate service {name} | {name}.service.ts + {name}.service.spec.ts |
ng g p {name} | /ng:generate pipe {name} | {name}.pipe.ts + {name}.pipe.spec.ts |
ng g d {name} | /ng:generate directive {name} | {name}.directive.ts + {name}.directive.spec.ts |
ng g g {name} | /ng:generate guard {name} | {name}.guard.ts + {name}.guard.spec.ts |
ng g interceptor {name} | /ng:generate interceptor {name} | {name}.interceptor.ts + {name}.interceptor.spec.ts |
ng g r {name} | /ng:generate routes {name} | {name}.routes.ts |
| — | /ng:generate store {name} | NgRx feature store (actions/reducer/selectors/effects/index) |
src/app/shared/{type}s/{artifact-name}/src/app/pages/{feature}/{type}s/{artifact-name}/src/app/core/services/src/app/core/guards/ or src/app/core/interceptors/src/app/pages/{feature}/Always create an index.ts barrel in the containing directory if one does not already exist.
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
@Component({
selector: 'app-{name}',
standalone: true,
imports: [],
templateUrl: './{name}.component.html',
styleUrl: './{name}.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class {Name}Component {
// Use inject() — never constructor injection
// private readonly service = inject({Name}Service);
}
HTML template — use new Angular control flow:
<!-- @if / @for / @switch — never *ngIf / *ngFor -->
providedIn: 'root' default)import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({ providedIn: 'root' })
export class {Name}Service {
private readonly http = inject(HttpClient);
}
Use feature-level providers array in routes config when service must be scoped to a lazy route.
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({ name: '{name}', standalone: true, pure: true })
export class {Name}Pipe implements PipeTransform {
transform(value: unknown, ...args: unknown[]): unknown {
return value;
}
}
import { Directive, inject } from '@angular/core';
@Directive({ selector: '[app{Name}]', standalone: true })
export class {Name}Directive {
// private readonly el = inject(ElementRef);
}
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
export const {name}Guard: CanActivateFn = (route, state) => {
const router = inject(Router);
// return true | false | UrlTree
return true;
};
import { inject } from '@angular/core';
import { HttpInterceptorFn } from '@angular/common/http';
export const {name}Interceptor: HttpInterceptorFn = (req, next) => {
return next(req);
};
import { Routes } from '@angular/router';
import { {Name}Component } from './{name}.component';
export const {NAME}_ROUTES: Routes = [
{
path: '',
component: {Name}Component,
},
];
actions.ts:
import { createAction, props } from '@ngrx/store';
export const load{Name} = createAction('[{Name}] Load');
export const load{Name}Success = createAction('[{Name}] Load Success', props<{ data: {Name}[] }>());
export const load{Name}Failure = createAction('[{Name}] Load Failure', props<{ error: string }>());
reducer.ts:
import { createReducer, on } from '@ngrx/store';
import * as {Name}Actions from './actions';
export interface {Name}State {
data: {Name}[];
loading: boolean;
error: string | null;
}
const initialState: {Name}State = { data: [], loading: false, error: null };
export const {name}Reducer = createReducer(
initialState,
on({Name}Actions.load{Name}, state => ({ ...state, loading: true, error: null })),
on({Name}Actions.load{Name}Success, (state, { data }) => ({ ...state, loading: false, data })),
on({Name}Actions.load{Name}Failure, (state, { error }) => ({ ...state, loading: false, error })),
);
selectors.ts:
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { {Name}State } from './reducer';
export const select{Name}State = createFeatureSelector<{Name}State>('{name}');
export const select{Name}Data = createSelector(select{Name}State, s => s.data);
export const select{Name}Loading = createSelector(select{Name}State, s => s.loading);
export const select{Name}Error = createSelector(select{Name}State, s => s.error);
effects.ts:
import { inject } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, of, switchMap } from 'rxjs';
import * as {Name}Actions from './actions';
import { {Name}Service } from '../services/{name}.service';
export const load{Name}Effect = createEffect(
(actions$ = inject(Actions), service = inject({Name}Service)) =>
actions$.pipe(
ofType({Name}Actions.load{Name}),
switchMap(() =>
service.getAll().pipe(
map(data => {Name}Actions.load{Name}Success({ data })),
catchError(err => of({Name}Actions.load{Name}Failure({ error: err.message }))),
),
),
),
{ functional: true },
);
Always use takeUntilDestroyed() — never ngOnDestroy + Subject pattern:
private readonly destroyRef = inject(DestroyRef);
ngOnInit() {
this.service.data$
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(data => { /* ... */ });
}
After creating any artifact in a directory, ensure index.ts exports it:
export * from './{name}.component';
export * from './{name}.service';
// etc.
any — use unknown and narrow, or define an interface@Input() and @Output() must be typedreadonly for injected dependencies and signal() values that should not be reassigned