---
title: .NET 10 Angular 21 Tailwind Template
---
We're excited to announce the release of our new **Angular 21 SPA Template** - a modern, full-stack template combining the latest Angular 21 frontend with a powerful .NET 10 backend powered by ServiceStack.
## What's New
### Angular with Modern Features
- **Standalone Components** - No NgModules, cleaner component architecture
- **Signal-based State Management** - Reactive state with Angular's new signals API
- **TypeScript 5.9** - Latest TypeScript features and improved type safety
- **Tailwind CSS 4** - Utility-first styling with dark mode support
### .NET 10 Backend
- **ServiceStack v10** - High-performance .NET APIs with AutoQuery CRUD
- **Entity Framework Core 10** - For ASP.NET Core Identity
- **OrmLite** - Fast, typed POCO ORM for application data
- **SQLite** - Zero-configuration database (easily swap for PostgreSQL, SQL Server, etc.)
### Upgrading to an production RDBMS
To switch from SQLite to PostgreSQL/SQL Server/MySQL:
1. Install preferred RDBMS (`ef-postgres`, `ef-mysql`, `ef-sqlserver`), e.g:
:::sh {.mb-8}
npx add-in ef-postgres
:::
2. Install `db-identity` to also switch to use this RDBMS for [Background Jobs](/rdbms-background-jobs) and [Request Logs Analytics](/admin-ui-rdbms-analytics):
:::sh {.mb-8}
npx add-in db-identity
:::
## Simplified .NET + Angular Development Workflow
- Single endpoint `https://localhost:5001` for both .NET and Angular UI (no dev certs required)
- ASP.NET Core proxies requests to Angular dev server (port 4200)
- Hot Module Replacement (HMR) support for instant UI updates
- WebSocket proxying for Angular HMR functionality

## .NET Angular App with Static Export
**Angular SPA** uses **static export**, where a production build of the Angular App is generated at deployment and published together with the .NET App in its `/wwwroot` folder, utilizing static file serving to render its UI.
This minimal `angular-spa` starting template is perfect for your next AI Assisted project, offering a streamlined foundation for building modern web applications with **Angular 21** and **.NET 10**:

## Key Features
### 🔐 ASP.NET Core Identity Authentication
Full authentication system with beautifully styled Tailwind CSS pages:
- User registration and login
- Email confirmation
- Password reset
- Profile management
- Role-based authorization
### ⚡ Rapid AutoQuery CRUD dev workflow
Quickly generate complete C# [CRUD APIs](/autoquery/crud) and [DB Migrations](/ormlite/db-migrations) from simple [TypeScript data models](https://localhost:5002/autoquery/okai-models):
1. Create a new feature
:::sh
npx okai init MyFeature
:::
2. Define your TypeScript data models in `MyFeature.d.ts`, e.g:
:::sh
code MyApp.ServiceModel/MyFeature.d.ts
:::
3. When ready, generate C# APIs and migrations
:::sh
npx okai MyFeature.d.ts
:::
4. Apply database migrations
:::sh
npm run migrate
:::
### Use AI for quick scaffolding
To help quickly scaffold your data models and features, use ServiceStack's AI assistant. Example of creating AutoQuery CRUD APIs for managing products:
:::sh
npx okai "Manage products price and inventory"
:::
### 📊 Background Jobs
Durable background job processing with:
- Command-based job execution
- Recurring job scheduling
- SMTP email sending via background workers
### 📝 Request Logging
SQLite-backed request logging for:
- API request tracking
- Error monitoring
- Performance analysis
### 🔍 Built-in Admin UIs
- **/ui** - ServiceStack API Explorer
- **/admin-ui** - Database management, user administration
- **/swagger** - OpenAPI documentation (development mode)
## Architecture Highlights
### Hybrid Development Model
During development, `dotnet watch` starts both the .NET backend and Angular dev server with Hot Module Replacement. In production, Angular builds to static files served directly by ASP.NET Core.
### Modular Configuration
Clean separation of concerns with `IHostingStartup` pattern:
- [Configure.AppHost.cs](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp/Configure.AppHost.cs) - Main ServiceStack AppHost registration
- [Configure.Auth.cs](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp/Configure.Auth.cs) - ServiceStack AuthFeature with ASP.NET Core Identity integration
- [Configure.AutoQuery.cs](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp/Configure.AutoQuery.cs) - AutoQuery features and audit events
- [Configure.Db.cs](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp/Configure.Db.cs) - Database setup (OrmLite for app data, EF Core for Identity)
- [Configure.Db.Migrations.cs](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp/Configure.Db.Migrations.cs) - Runs OrmLite and EF DB Migrations and creates initial users
- [Configure.BackgroundJobs.cs](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp/Configure.BackgroundJobs.cs) - Background job processing
- [Configure.HealthChecks.cs](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp/Configure.HealthChecks.cs) - Health monitoring endpoint
This pattern keeps [Program.cs](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp/Program.cs) clean and separates concerns.
### Type-Safe API Client
Auto-generated TypeScript DTOs ensure type safety across the stack:
```typescript
import { QueryBookings } from '@/dtos'
const response = await client.api(new QueryBookings({ minCost: 100 }))
if (response.succeeded) {
console.log(response.response!.results)
}
```
## Deployment Ready
GitHub Actions workflows included for:
- **CI/CD** - Automated build and test
- **Container Builds** - Docker image creation
- **Kamal Deployment** - One-command production deployment with SSL
### Kamal Deployments
All deployments include the GitHub Action workflows to deploy your App to [any Linux Server with Kamal](/kamal-deploy) using Docker, SSH and GitHub Container Registry (ghcr).
Where you can host it on a [Hetzner US Cloud](https://www.hetzner.com/cloud) VM for as low as **$5 per month** or if you have multiple Apps you can delpoy them all to a single VM which we're doing for our .NET Template Live Demos which runs **30 Docker Apps** on a **8GB RAM/80GB SSD** dedicated VM for **$15 /month**.
## AI-Assisted Development with CLAUDE.md
As part of our objectives of improving developer experience and embracing modern AI-assisted development workflows - all new .NET React templates include a comprehensive `AGENTS.md` file designed to optimize AI-assisted development workflows.
### What is CLAUDE.md?
`CLAUDE.md` and [AGENTS.md](https://agents.md) onboards Claude (and other AI assistants) to your codebase by using a structured documentation file that provides it with complete context about your project's architecture, conventions, and technology choices. This enables more accurate code generation, better suggestions, and faster problem-solving.
### What's Included
Each template's `AGENTS.md` contains:
- **Project Architecture Overview** - Technology stack, design patterns, and key architectural decisions
- **Project Structure** - Gives Claude a map of the codebase
- **ServiceStack Conventions** - DTO patterns, Service implementation, AutoQuery, Authentication, and Validation
- **React Integration** - TypeScript DTO generation, API client usage, component patterns, and form handling
- **Database Patterns** - OrmLite setup, migrations, and data access patterns
- **Common Development Tasks** - Step-by-step guides for adding APIs, implementing features, and extending functionality
- **Testing & Deployment** - Test patterns and deployment workflows
### Extending with Project-Specific Details
The existing `CLAUDE.md` serves as a solid foundation, but for best results, you should extend it with project-specific details like the purpose of the project, key parts and features of the project and any unique conventions you've adopted.
### Benefits
- **Faster Onboarding** - New developers (and AI assistants) understand project conventions immediately
- **Consistent Code Generation** - AI tools generate code following your project's patterns
- **Better Context** - AI assistants can reference specific ServiceStack patterns and conventions
- **Reduced Errors** - Clear documentation of framework-specific conventions
- **Living Documentation** - Keep it updated as your project evolves
### How to Use
Claude Code and most AI Assistants already support automatically referencing `CLAUDE.md` and `AGENTS.md` files, for others you can just include it in your prompt context when asking for help, e.g:
> Using my project's AGENTS.md, can you help me add a new AutoQuery API for managing Products?
The AI will understand your App's ServiceStack conventions, React setup, and project structure, providing more accurate and contextual assistance.
### Getting Started
All new [angular-spa.web-templates.io](https://angular-spa.web-templates.io) include [AGENTS.md](https://github.com/NetCoreTemplates/angular-spa/blob/main/AGENTS.md) by default. For existing projects, you can adapt the template to document your App's conventions, patterns and technology choices.
## Feature Tour
Angular's structured approach to modern web development is ideal for large complex Applications that stitches together various technologies, handling authentication, designing responsive UIs, and managing complex state which the new Angular SPA template embraces to provide a productive starting point with a robust foundation packed with essential features right out of the box.
1. **Built-in Identity Authentication:** Secured out-of-the-box, this template integrates seamlessly with ASP.NET Core Identity, providing ready-to-use registration, login, and User Admin management features.
2. **Tailwind v4 CSS:** Rewritten to use Tailwind v4 CSS, allowing you to rapidly build beautiful, responsive designs directly in your markup.
3. **Dark Mode Support:** Cater to user preferences with built-in, easily toggleable dark mode support, styled elegantly with Tailwind.
4. **Customizable DataGrid Component:** Effortlessly display tabular data with the included customizable DataGrid. Easily adapt it for sorting, filtering and displaying your specific data structures.
5. **Reusable Input Components with Validation:** The template includes reusable, pre-styled input components (e.g., text input, selects) with built-in support for validation bound forms and contextual displaying of validation errors.
6. **RxJS & Signals Support:** Modern Angular reactivity: whether you prefer the established power of **RxJS Observables** or the new granular reactivity of **Angular Signals**, our template is structured to support *both* programming models.
We'll take a quick tour to explore the templates features:
### Home Page
The home page sports a responsive Tailwind design where all its components are encapsulated within its
[/app/home](https://github.com/NetCoreTemplates/angular-spa/tree/main/MyApp.Client/src/app/home)
with its logic maintained in `*.ts` files and its presentation UI optionally maintained in a separate `*.html` file.
### Dark Mode
The [dark-mode-toggle.component.ts](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp.Client/src/components/dark-mode-toggle.component.ts)
and [theme.service.ts](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp.Client/src/components/services/theme.service.ts)
handles switching between Light and Dark Mode which is initially populated from the Users OS preference.
### Weather
The Weather page maintained in [/app/weather](https://github.com/NetCoreTemplates/angular-spa/tree/main/MyApp.Client/src/app/weather)
provides a good example of utilizing an RxJS Observable programming model with the
[api-http-client.service.ts](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp.Client/src/components/services/api-http-client.service.ts)
that extends Angular's Observable HttpClient with an additional `api` method that lets you use your Services typed `dtos.ts`
TypeScript DTOs to enable type-safe integration with your back-end services:
```ts
import { Forecast, GetWeatherForecast, ResponseStatus } from 'src/dtos'
import { ApiHttpClient } from 'src/components/services/api-http-client.service'
export class WeatherComponent {
http = inject(ApiHttpClient);
public error: ResponseStatus | null = null;
public forecasts: Forecast[] = [];
getForecasts() {
this.http.api(new GetWeatherForecast({ date:'2025-04-01' })).subscribe({
next:(result) => {
this.error = null;
this.forecasts = result;
},
error:(error) => {
this.error = error;
}
});
}
}
```
Whilst its [weather.component.html](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp.Client/src/app/weather/weather.component.html)
template showcases the new [data-grid.component.ts](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp.Client/src/components/data-grid.component.ts)
to display a beautiful tailwind DataGrid with just:
```html
```
:::{.not-prose .p-4 .mx-auto .max-w-3xl .shadow .rounded-lg}
[](https://angular-spa.web-templates.io/weather)
:::
It's a direct port of our [Vue DataGrid](https://docs.servicestack.net/vue/datagrid) that also supports
the same customizations allowing for custom Headers and Column fields, e.g:
```html
Date
{{ x | date:'MMMM d, yyyy' }}
{{ x }}°
{{ x }}°
{{ x }}
```
Which renders the expected:
:::{.not-prose .p-4 .mx-auto .max-w-3xl .shadow .rounded-lg}
[](https://angular-spa.web-templates.io/weather)
:::
## Todos MVC
The Todos MVC App maintained in [/app/todomvc](https://github.com/NetCoreTemplates/angular-spa/tree/main/MyApp.Client/src/app/todomvc)
demonstrates how to create the popular [todomvc.com](https://todomvc.com) App in Angular 19.
:::{.not-prose .p-4 .mx-auto .max-w-3xl .shadow .rounded-lg}
[](https://angular-spa.web-templates.io/todomvc)
:::
It's another example of building a simple CRUD Application with Angular RxJS Observables and your APIs TypeScript DTOs.
This snippet shows how to query and create Todos with the `ApiHttpClient`:
```ts
import { Todo, QueryTodos, CreateTodo, ResponseStatus } from 'src/dtos'
import { ApiHttpClient } from 'src/components/services/api-http-client.service'
export class TodoMvcComponent implements OnInit {
client = inject(ApiHttpClient);
error: ResponseStatus | null = null;
todos: Todo[] = [];
newTodoText = '';
loadTodos(): void {
this.client.api(new QueryTodos()).subscribe({
next: (todos) => {
this.todos = todos.results;
},
error: (err) => {
this.error = err;
}
});
}
addTodo(): void {
if (!this.newTodoText.trim()) return;
this.client.api(new CreateTodo({
text: this.newTodoText.trim()
})).subscribe({
next: (todo) => {
this.todos.push(todo);
this.newTodoText = '';
},
error: (err) => {
this.error = err;
console.error('Error adding todo:', err);
}
});
}
//...
}
```
## Bookings
All other examples in the template uses Angular's newer Signal for reactivity and the standard ServiceStack `JsonServiceClient`
used in all other TypeScript/JS Apps.
The Bookings Pages are maintained in [/app/bookings](https://github.com/NetCoreTemplates/angular-spa/tree/main/MyApp.Client/src/app/bookings)
and showcases a more complete example of developing a CRUD UI in Angular starting with an example of how to encapsulate
route information for a feature in an isolated [booking.routes.ts](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp.Client/src/app/bookings/booking.routes.ts):
```ts
import { Routes } from '@angular/router';
import { BookingListComponent } from './booking-list.component';
import { BookingCreateComponent } from './booking-create.component';
import { BookingEditComponent } from './booking-edit.component';
import { authGuard } from 'src/guards';
export const BOOKING_ROUTES: Routes = [
{
path: 'bookings',
component: BookingListComponent,
canActivate: [authGuard]
},
{
path: 'bookings/create',
component: BookingCreateComponent,
canActivate: [authGuard]
},
{
path: 'bookings/edit/:id',
component: BookingEditComponent,
canActivate: [authGuard]
}
];
```
The use of the Route `authGuard` ensures only Authenticated Users can access these routes, as well as redirecting
non-authenticated users to the Sign In page.
### Bookings List
:::{.not-prose .p-4 .mx-auto .max-w-3xl .shadow .rounded-lg}
[](https://angular-spa.web-templates.io/bookings)
:::
The bookings list component shows an example of using Angular's Signals with the `JsonServiceClient` together with
an `ApiState` context to enable data bound forms and validation errors:
```ts
@Component({
templateUrl: './booking-list.component.html',
providers: [
...provideApiState()
],
//...
})
export class BookingListComponent implements OnInit {
private router = inject(Router);
private client = inject(JsonServiceClient);
api = inject(ApiState);
// Signals for state
allBookings = signal([]);
ngOnInit(): void {
this.loadBookings();
}
async loadBookings(): Promise {
this.api.begin();
const api = await this.client.api(new QueryBookings({
orderByDesc: 'BookingStartDate',
}));
if (api.succeeded) {
this.allBookings.set(api.response!.results);
}
this.api.complete(api.error);
}
}
```
Using `provideApiState()` implicitly injects the populated API context containing both the APIs Loading and Error state into child components saving you from having to explicitly inject it into each component.
E.g. the `` component will display when API Requests are in-flight whilst API Error Responses are displayed
after receiving failed API Responses:
```html
@if (allBookings().length > 0) {
...
}
@else {
No bookings found
}
```
### Create Booking
The [booking-create.component.ts](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp.Client/src/app/bookings/booking-create.component.ts) shows the standard pattern of calling ServiceStack Typed APIs to
save forms whilst saving any validation errors to the `ApiState` context:
```ts
async save(): Promise {
this.api.begin();
const request = new CreateBooking(this.booking());
const api = await this.client.api(request);
if (api.succeeded) {
// Navigate back to bookings list after successful save
this.router.navigate(['/bookings']);
}
this.api.complete(api.error);
}
```
Where any contextual validation will be displayed next to the input field:
:::{.not-prose .p-4 .mx-auto .max-w-3xl .shadow .rounded-lg}
[](https://angular-spa.web-templates.io/bookings/create)
:::
### Edit Booking
The [booking-edit.component.ts](https://github.com/NetCoreTemplates/angular-spa/blob/main/MyApp.Client/src/app/bookings/booking-edit.component.ts)
shows an example of using the `JsonServiceClient` with Signals to get and modify bookings:
```ts
export class BookingEditComponent implements OnInit {
private route = inject(ActivatedRoute);
private router = inject(Router);
private client = inject(JsonServiceClient);
meta = inject(MetadataService);
api = inject(ApiState);
// Signals
booking = signal(new Booking());
ngOnInit(): void {
// Get booking ID from route params
const id = this.route.snapshot.paramMap.get('id');
if (id) {
this.fetchBooking(parseInt(id, 10));
} else {
this.api.setErrorMessage('Booking ID is required');
}
}
async fetchBooking(id: number): Promise {
this.api.begin();
const api = await this.client.api(new QueryBookings({id}));
if (api.succeeded) {
this.booking.set(api.response!.results[0]);
}
this.api.complete(api.error);
}
async save(): Promise {
this.api.begin();
const api = await this.client.api(new UpdateBooking(this.booking()));
if (api.succeeded) {
this.router.navigate(['/bookings']);
}
this.api.complete(api.error);
}
}
```
:::{.not-prose .p-4 .mx-auto .max-w-3xl .shadow .rounded-lg}
[](https://angular-spa.web-templates.io/bookings/edit/1)
:::
It shows an example of a validation bound form bounded to a signal instance of a `Booking` DTO with summary and
contextual validation and utilization of your API's metadata with `meta.enumOptions('RoomType')` which populates
the `