name: angular-frontend description: Guidelines for creating and configuring Angular projects. Use when creating a new Angular project or when asked to configure an existing one. metadata: author: Rainer Stropek
General Documentation
If you are creating a new Angular project, read the official angular-new-app skill first.
If you are looking for general information about Angular, read the official angular-developer skill. If it does not contain what you are looking for, search in https://angular.dev/llms.txt.
Environments
Add environment-related configuration (ng generate environments)
Playwright End-to-End Tests
We do not use Cypress for end-to-end testing. Instead, we use Playwright. Note: The angular-developer skill's e2e-testing reference covers Cypress — ignore it. This project uses Playwright exclusively. See the playwright-cli skill.
- Install Playwright in the Angular app.
- Store Playwright config and tests in the Angular app under the
e2efolder.
Aspire Integration
Add a JS script build/set-env.js that updates the environment.apiBaseUrl environment setting according to the Aspire environment variables (process.env.services__webapi__https__0 || process.env.services__webapi__http__0). The script must write to both src/environments/environment.ts and src/environments/environment.development.ts. Call this JS script in the package.json start script before starting the Angular development server.
The start script must pass the PORT environment variable to the Angular development server (ng serve --port $PORT), so that it works correctly in the Aspire environment.
Use the Aspire.Hosting.NodeJs NuGet package in the Aspire AppHost to host the Angular application. Use AddNpmApp to add the Angular project to the AppHost (with a reference to the web API).
Configure Playwright to run against the local dev server URL provided by Aspire (read the aspire skill to understand how to start the application and discover service URLs).
API Client Generation
When used together with an ASP.NET Core Minimal API web API project, we generate API clients from the Open API specification generated by the web API project. This allows for type-safe API calls in the Angular frontend.
- Add the
ng-openapi-genpackage (dev dependency) - Add a
generate-web-apiscript topackage.json:ng-openapi-gen --input <relative-path-to-web-api>/<web-api-project-name>.json --output src/app/api
Always use the generated API client for API calls, do not call the API directly using HttpClient. This ensures type safety and consistency with the API specification.
Linting
We use angular-eslint for linting. It is added via ng add angular-eslint and configured in eslint.config.js (flat config format).
- Run the linter with
pnpm ng lint(orng lint) in the Frontend folder. - The generated API client folder (
src/app/api/) is excluded from linting via anignoresentry ineslint.config.js. - All code must pass linting with zero warnings and zero errors.
Angular Configuration
In app.config.ts:
- Use zoneless:
provideZonelessChangeDetection() - Use fetch:
provideHttpClient(withFetch()) - Use component input binding:
provideRouter(routes, withComponentInputBinding())
Coding Guidelines
TypeScript Best Practices
- Use strict type checking
- Prefer type inference when the type is obvious
- Avoid the
anytype; useunknownwhen type is uncertain
CSS Best Practices
- Use modern CSS (with nested selectors, CSS variables, etc.)
- Prefer module-scoped styles over global styles. Use global styles only for truly global concerns (e.g. typography, color variables, etc.).
- Avoid CSS preprocessors (SCSS, Less) and utility-first CSS frameworks (Tailwind). Use plain modern CSS.
Angular Best Practices
- Always use standalone components over NgModules
- Must NOT set
standalone: trueinside Angular decorators. It's the default in Angular v20+. - Use signals for state management
- Implement lazy loading for feature routes
- Do NOT use the
@HostBindingand@HostListenerdecorators. Put host bindings inside thehostobject of the@Componentor@Directivedecorator instead - Use
NgOptimizedImagefor all static images.NgOptimizedImagedoes not work for inline base64 images.
Accessibility Requirements
- It MUST pass all AXE checks.
- It MUST follow all WCAG AA minimums, including focus management, color contrast, and ARIA attributes.
Components
- Keep components small and focused on a single responsibility
- Use
input()andoutput()functions instead of decorators - Use
computed()for derived state - Set
changeDetection: ChangeDetectionStrategy.OnPushin@Componentdecorator - Prefer inline templates for small components
- For new forms, prefer Signal Forms (Angular v21+; see the
angular-developerskill). For Angular v20 or existing code, prefer Reactive forms over Template-driven ones. - Do NOT use
ngClass, useclassbindings instead - Do NOT use
ngStyle, usestylebindings instead - When using external templates/styles, use paths relative to the component TS file.
State Management
- Use signals for local component state
- Use
computed()for derived state - Keep state transformations pure and predictable
- Do NOT use
mutateon signals, useupdateorsetinstead
Templates
- Keep templates simple and avoid complex logic
- Use native control flow (
@if,@for,@switch) instead of*ngIf,*ngFor,*ngSwitch - Convert observables to signals using
toSignal()from@angular/core/rxjs-interop. Use the async pipe only when signal conversion is impractical. - Do not assume globals like (
new Date()) are available.
Services
- Design services around a single responsibility
- Use the
providedIn: 'root'option for singleton services - Use the
inject()function instead of constructor injection