---
title: Blazor WASM Bootstrap
---
The feature-rich Blazor WASM Bootstrap template is ideal for teams with strong C# skills building Line Of Business (LOB) applications.
Utilizing Blazor WebAssembly (WASM) with a ServiceStack backend yields an optimal frictionless [API First development model](/api-first-development) where UIs can bind directly to Typed DTOs whilst benefiting from ServiceStack's [structured error handling](/validation) & rich contextual form validation binding.
## Getting Started
Customize and Download a new Blazor WASM Bootstrap project with your preferred project name:
Download new C# Blazor WASM Project
Alternatively you can create & download a new Blazor Project with the [x dotnet tool](/dotnet-new):
:::sh
npx create-net LegacyTemplates/blazor-wasm ProjectName
:::
## Optimal Development Workflow
By utilizing ServiceStack's [decoupled project structure](/physical-project-structure), combined with Blazor enabling C# on the client, we're able to get 100% reuse of your APIs shared DTOs as-is to enable an end-to-end Typed API automatically free from any additional tooling or code-gen complexity.
## Api and ApiAsync methods
.NET was originally conceived to use Exceptions for error control flow however there's been a tendency in modern languages & libraries to shun Exceptions and return errors as normal values, an approach we believe is a more flexible & ergonomic way to handle API responses.
### The ApiResult way
The `Api(Request)` and `ApiAsync(Request)` APIs returns a typed `ApiResult` Value Result encapsulating either a Typed Response or a structured API Error populated in `ResponseStatus` allowing you to handle API responses programmatically without `try/catch` handling:
The below example code to create a new Booking:
```csharp
CreateBooking request = new();
ApiResult api = new();
async Task OnSubmit()
{
api = await Client.ApiAsync(request);
if (api.Succeeded)
{
await done.InvokeAsync(api.Response!);
request = new();
}
}
```
Which despite its terseness handles both **success** and **error** API responses, **if successful** it invokes the `done()` callback notifying its parent of the new Booking API Response before resetting the Form's data model with a new Request DTO.
Upon **failure** the error response is populated in `api.Error` which binds to the UI via Blazor's `` to propagate it to all its child components in order to show contextual validation errors next to their respective Input controls.
## JSON API Client
The recommended way for configuring a Service Client to use in your Blazor WASM Apps is to use `AddBlazorApiClient()`, e.g:
```csharp
builder.Services.AddBlazorApiClient(builder.Configuration["ApiBaseUrl"] ?? builder.HostEnvironment.BaseAddress);
```
Which registers a typed Http Client factory returning a recommended pre-configured `JsonApiClient` to communicate with your back-end ServiceStack APIs including support for CORS, required when hosting the decoupled UI on a different server (e.g. CDN) to your server.
If you're deploying your Blazor WASM UI to a CDN you'll need to specify the URL for the server, otherwise if it's deployed together with your Server App you can use the Host's Base Address.
### Public Pages & Components
To reduce boiler plate, your Blazor Pages & components can inherit the templates local [AppComponentBase.cs](https://github.com/LegacyTemplates/blazor-wasm/blob/main/MyApp.Client/AppComponentBase.cs) which inherits `BlazorComponentBase` which gets injected with the `JsonApiClient` and provides short-hand access to its most common APIs:
```csharp
public class BlazorComponentBase : ComponentBase, IHasJsonApiClient
{
[Inject]
public JsonApiClient? Client { get; set; }
public virtual Task> ApiAsync(IReturn request) => Client!.ApiAsync(this, request);
public virtual Task> ApiAsync(IReturnVoid request) => Client!.ApiAsync(this, request);
public virtual Task SendAsync(IReturn request) => Client!.SendAsync(this, request);
}
```
### Protected Pages & Components
Pages and Components requiring Authentication should inherit from [AppAuthComponentBase](https://github.com/LegacyTemplates/blazor-wasm/blob/main/MyApp.Client/AppComponentBase.cs) instead which integrates with Blazor's Authentication Model to provide access to the currently authenticated user:
```csharp
public abstract class AppAuthComponentBase : AppComponentBase
{
[CascadingParameter]
protected Task? AuthenticationStateTask { get; set; }
protected bool HasInit { get; set; }
protected bool IsAuthenticated => User?.Identity?.IsAuthenticated ?? false;
protected ClaimsPrincipal? User { get; set; }
protected override async Task OnParametersSetAsync()
{
var state = await AuthenticationStateTask!;
User = state.User;
HasInit = true;
}
}
```
## Benefits of Shared DTOs
Typically with Web Apps, our client is using a different language to C#, so an equivalent request DTOs need to be generated for the client.
### TypeScript Example
For example, TypeScript generated DTOs still give us typed end-to-end services with the help of tooling like [Add ServiceStack Reference](/add-servicestack-reference)
```csharp
[Route("/hello/{Name}")]
public class Hello : IReturn
{
public string Name { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
```
Turns into:
```typescript
// @Route("/hello/{Name}")
export class Hello implements IReturn
{
public name: string;
public constructor(init?: Partial) { (Object as any).assign(this, init); }
public getTypeName() { return 'Hello'; }
public getMethod() { return 'POST'; }
public createResponse() { return new HelloResponse(); }
}
export class HelloResponse
{
public result: string;
public responseStatus: ResponseStatus;
public constructor(init?: Partial) { (Object as any).assign(this, init); }
}
```
When Request or Response DTOs changes during development, the client DTOs need to be regenerated using a command like [`x csharp`](./add-servicestack-reference.md#simple-command-line-utilities).
### Blazor WASM Example
Developing your Blazor WASM UI however, you just change your shared request/response DTO in the shared `ServiceModel` project, and both your client and server compile against the same request/response DTO classes.
This eliminates the need for any additional step.
In the `ServiceModel` project, we still have:
```csharp
[Route("/hello/{Name}")]
public class Hello : IReturn
{
public string Name { get; set; }
}
public class HelloResponse
{
public string Result { get; set; }
}
```
Which the Blazor C# App can use directly in its **.razor** pages:
```csharp
@code {
Hello request = new()
{
Name = "Blazor WASM"
};
ApiResult api = new();
protected override async Task OnInitializedAsync() => await submit();
async Task submit() => api = await ApiAsync(request);
}
```
## ServiceStack.Blazor
The **ServiceStack.Blazor** library contains integrated functionality for Blazor including an optimal JSON API HttpClient Factory, API-enabled base components, HTML Utils and Bootstrap & Tailwind UI Input components heavily utilized throughout the template.
### Built-in Blazor and Tailwind UI Components
The Built-in UI Components enable a clean & productive dev model and share the same base classes making them functionally equivalent and can be swapped when switching CSS frameworks by updating its namespace in your projects `_Imports.razor`
The Blazor Components in **ServiceStack.Blazor** include:
| Component | Description |
|-------------------|-----------------------------------------------------------------------------------|
| `` | Text Input control for string properties |
| `` | Date Input control for Date properties |
| `` | Checkbox Input control for Boolean properties |
| `` | Select Dropdown for properties with finite list of values like Enums |
| `` | Text Input control for large strings |
| `` | Dynamic component utilizing the appropriate above Input controls in Auto Forms |
| `` | Displaying successful notification feedback |
| `` | Displaying error summary message when no contextual field validation is available |
| `` | Used with `FilesUploadFeature` and `UploadTo` attribute to upload files |
::: info
All Input controls support contextual validation of ServiceStack's existing [structured Error responses](/error-handling)
:::
### Themable
Should it be needed, all components are themable by running the included [README.ss](https://github.com/LegacyTemplates/blazor-wasm/blob/main/MyApp.Client/Shared/Components/README.ss) executable documentation that copies its **Razor** UI markup locally into your project enabling customization of UI including controls.
### Bookings CRUD
The C# Service Client `Api*` methods make calling remote ServiceStack APIs similar to calling a C# method as its returned `ApiResult` encapsulates both a typed **Error** & API **Response** as an alternate way to handle errors as all components can bind directly to its `api.Error`.
The reusability extends to your APIs typed Request DTOs which components can directly 2-way data bind to.
Below is an example of a CRUD Booking form [BookingsCrud/Create.razor](https://github.com/ServiceStack/ServiceStack/blob/master/tests/ServiceStack.Blazor.Tests/Client/Pages/BookingsCrud/Create.razor) used to Create Bookings:
```html
@attribute [Authorize(Roles="Employee")]
@inherits AppAuthComponentBase
@code {
[Parameter] public EventCallback done { get; set; }
[Parameter] public string? @class { get; set; }
CreateBooking request = new()
{
BookingStartDate = DateTime.UtcNow,
};
// Hide Error Summary Messages for Visible Fields which displays contextual validation errors
string[] VisibleFields => new[] {
nameof(request.Name),
nameof(request.RoomType),
nameof(request.RoomNumber),
nameof(request.BookingStartDate),
nameof(request.BookingEndDate),
nameof(request.Cost),
nameof(request.Notes),
};
ApiResult api = new();
async Task OnSubmit()
{
api = await ApiAsync(request);
if (api.Succeeded)
{
await done.InvokeAsync(api.Response!);
request = new();
}
}
async Task close() => await done.InvokeAsync(null);
}
```
Which binds directly to the [CreateBooking](https://github.com/NetCoreTemplates/blazor-vue/blob/main/MyApp.ServiceModel/Bookings.cs) Request DTO:
```csharp
[Tag("bookings"), Description("Create a new Booking")]
[Route("/bookings", "POST")]
[ValidateHasRole("Employee")]
[AutoApply(Behavior.AuditCreate)]
public class CreateBooking : ICreateDb, IReturn
{
[Description("Name this Booking is for"), ValidateNotEmpty]
public string Name { get; set; }
public RoomType RoomType { get; set; }
[ValidateGreaterThan(0)]
public int RoomNumber { get; set; }
[ValidateGreaterThan(0)]
public decimal Cost { get; set; }
public DateTime BookingStartDate { get; set; }
public DateTime? BookingEndDate { get; set; }
[Input(Type = "textarea")]
public string? Notes { get; set; }
}
```
To initially render this form:
Whose `[ValidateNotEmpty]` [declarative validator](/declarative-validation) filters down to the **client Input** UI to prevent unnecessary invalid API requests:
Validation of server error responses looks like:
#### Use of `form`
Validation errors use the standard `