Documentation Menu
Blazor SSR utilizes Razor components to split your markdown into re-usable chunks. These components also have the ability to contain logic specific to themselves and can even have url routes invoke them.
Razor components let you mix in HTML markup with C#, allowing you to quickly create dynamic components to use in your app.
@inject DeveloperService developerService
@code {
private List<Developer> developers { get; set; }
protected override void OnInitialized()
{
developers = await developerService.GetDevelopers();
}
}
<ul>
@foreach (var developer in developers)
{
<li>
@developer.Name
</li>
}
</ul>
Razor components are re-usable elements you can use anywhere in your application.
Your components should be created in the Pages/Components
directory.
To create a new Razor component, simply run the Spark make component command.
spark make component SomeComponent
A new Razor component named SomeComponent.razor
will be created in the Pages/Components
directory.
<p>My Component</p>
@code {
protected override async Task OnInitializedAsync()
{
}
}
To display a component, you may use a Razor component tag within another Razor component or page. Razor component tags are just the name of it’s source file.
<SomeComponent />
You can pass data to a Razor component or page by using Component Parameters.
Component parameters are defined using public C# properties on the component class with the [Parameter]
attribute.
For example, below is a razor component called ChildComponent.razor
. It accepts a parameter of Title
.
<h1>@Title</h1>
@code {
[Parameter]
public string Title { get; set; } = "";
}
Then, in a parent Razor component, you can render the child component and pass the Title
parameter value.
<div>
<SomeComponent Title="This is my title" />
</div>
@code {
}
In this example, the following markup would ultimately be rendered to the browser.
<div>
<h1>This is my title</h1>
</div>
You will often need to pass additional content to your razor components via a RenderFragment
. RenderFragments can be HTML markup or even other Razor Components.
For example, the below ChildComponent.razor
component has a ChildContent
parameter of type RenderFragment
. This represents a segment of UI to render.
<h1>
@ChildContent
</h1>
@code {
[Parameter]
public RenderFragment? ChildContent { get; set; }
}
Then, in a parent Razor component, you can render the child component while passing content between it’s opening and closing tags.
<ChildComponent>
Spark is <span class="text-yellow-600">Awesome!</span>
</ChildComponent>
In this example, the following markup would ultimately be rendered to the browser.
<h1>Spark is <span class="text-yellow-600">Awesome!</span></h1>
You can take advantage of ASP.NET’s dependency injection system by adding the @inject
directive in your Razor components.
.NET will automatically resolve the dependency for you provided it has been registered in the apps service container.
@inject IConfiguration Config
@code {
private string name = '';
protected override void OnInitialized()
{
name = Config.GetValue<string>("Developer:Name");
}
}
Spark exposes the PageState
object to all Razor components as a cascading parameter.
This parameter can be used in any Razor component in a Spark app.
The PageState
object has two properties.
public class PageState
{
public User User { get; set; } = new();
public string AppName { get; set; }
}
These properties are set for you automatically when a route is visited, giving you access to the authenticated users data and the apps name.
For example, you can display the authenticated users name on a page like this:
@code {
[CascadingParameter]
public PageState PageState { get; set; } = default!;
}
<div>@PageState.User.Name</div>
Because Spark use Blazor SSR, you can access the HttpContext
in your Razor components through a cascading parameter.
@code {
[CascadingParameter]
public HttpContext? HttpContext { get; set; }
}
Blazor allows you to assign a route directly to a component in your application. These are called Blazor pages.
To assign a route to a component, use the @page
attribute at the top of your .razor file.
@page "/developers"
<div>
...
</div>
You can create a new Blazor page by hand or by running the following Spark CLI command:
spark make page Developers/Index
Just like a normal component, after running this command Spark will create a new file in your application at Pages/Developers/Index.razor
.
@page "/developers/index"
@code {
protected override void OnInitialized()
{
}
}
<h1>My Page</h1>
It is advised to create your Blazor pages in a subdirectory of the Pages
folder and not the root of the Pages folder.
For example, lets say our app has a list of developers and open source work they’ve done.
We would want the following pages in the Pages/Developers
directory:
Pages/Developers/Index.razor
Show a list of developersPages/Developers/Show.razor
Show 1 developerPages/Developers/Create.razor
Create developerPages/Developers/Edit.razor
Edit developerPages/Developers/Delete.razor
Delete developerBlazer pages and components should always use PascalCase naming.
<MyComponent />
Parameters can be passed into a Blazor page through the url. For example, lets say you have a page to show the details about a developer.
You can setup a route parameter in your URL to pass in the Id. Then setup a public property in your Blazor page with the same name.
Route parameter names are case insensitive.
@page "/developers/{Id:int}"
@if (developer != null) {
<h1>@developer.Name</h1>
}
@code {
[Parameter]
public int Id { get; set; }
private Developer developer { get; set; }
protected override async Task OnInitializedAsync()
{
developer = await _developerService.Get(Id);
}
}
You can set a Blazor pages <title>
element by using the PageTitle
component.
@page "/developers"
<PageTitle>This is the developers page</PageTitle>
To set metadata, like the pages description, use the HeadContent
component.
@page "/developers"
<PageTitle>This is the developers page</PageTitle>
<HeadContent>
<meta name="description" content="This is a page that shows all the developers.">
</HeadContent>
Because Spark uses Server-side Rendered Blazor, all pages are rendered on the server and the HTML is then sent to the clients browser.
This means search engines can easily index your sites content.
Blazor pages use standard html anchor elements to navigate to other Blazor pages.
@page "/developers"
<a href="/">Go to home page</a>
HTMX will intercept the navigation and use an AJAX fetch request. The response will then be evaluated by HTMX and replace the content on the page without having to do a full page reload.
To read about how to authenticate and access User data from your Razor components and pages, check out Spark’s Authentication guide.
Layout files define a top level template for pages in Spark apps.
Spark projects come with a MainLayout.razor file setup for you already in the Pages/Layouts
directory. All pages will use this layout by default.
To create your own layout file, add a new razor component to the Pages/Layouts
directory. For example, Pages/Layouts/NewLayout.razor
.
Layout files need to inherit LayoutComponentBase
and have an @Body
tag to let Blazor know where to render the pages content.
@* Pages/Layouts/NewLayout.razor *@
@inherits LayoutComponentBase
<!DOCTYPE html>
<html lang="en">
<head>
<HeadOutlet />
</head>
<body>
@Body
</body>
</html>
To use the new layout on a page, add the @layout
attribute.
@page "/developers"
@layout NewLayout
<h2>Episodes</h2>
The default layout used by your pages is set in the Pages/PageRoutes.razor
file.
...
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layouts.MainLayout)"/>
...
To change your default layout, simply pass a different layout to the DefaultLayout
parameter.
...
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layouts.NewLayout)"/>
...
Forms are a core component for most web applications. Blazor comes with built-in components to help you easily create dynamic forms.
The easiest way to create a form in your Razor components is to utilize the EditForm
component that comes with Blazor.
@page "/developers/create"
<EditForm Model="@Model" OnSubmit="@Submit" FormName="CreateDeveloper">
<InputText @bind-Value="Model!.Name">
<button type="submit">Save</button>
</EditForm>
@code {
[SupplyParameterFromForm]
public Developer? Model { get; set; }
protected override void OnInitialized() {
Model ??= new();
}
private void Submit()
{
Console.Writeline("New dev named {Model.Name}");
}
}
EditForm
components accept a model parameter. This allows you to bind an object to the edit form, so the data entered in the forms input elements can be bound to your own object on the server when the form is submitted.
The SupplyParameterFromForm
attribute is required for this to work properly.
<EditForm Model="@Model">
...
</EditForm>
@code {
[SupplyParameterFromForm]
public Developer? Model { get; set; }
...
When a form is submitted, the OnInitialized
method will run in your component. This is important to note because if your instantiating your forms model in this method, it will overwrite the bound data when the form is submitted.
To work around this, use a null-coalescing operator to only instantiate a new object if it’s null.
protected override void OnInitialized() {
Model ??= new();
}
EditForm
components can be submitted by assigning the OnValidSubmit
or OnSubmit
event handlers to a method in your component.
When the user clicks the “Submit” button in your edit form, an HTTP request will be made to the server and your method will run.
@page "/developers/create"
<EditForm Model="@Model" OnSubmit="@Submit" FormName="CreateDeveloper">
<InputText @bind-Value="Model!.Name">
<button type="submit">Save</button>
</EditForm>
@code {
...
// The event handler that will be called on form submit
private void Submit()
{
Console.Writeline("New dev named {Model.Name}");
}
}
OnValidSubmit
will run validation on your model before running your event handler method. We will talk more about this below.
Most of the time, you will need to validate your forms data before doing anything with it.
You can accomplish this 1 of 2 ways.
Data Annotations Validator
The data annotations validator is the easiest way to validate form data but can’t cover complex scenarios like fluent validation can.
To use the data annotations, first define your forms model with with Data Annotation attributes on it’s properties.
public class Developer {
[Required]
public string Name { get; set; }
}
Then bind your model to the EditForm
and add the <DataAnnotationsValidator />
component inside of the form.
@page "/developers/create"
<EditForm Model="@Model" OnSubmit="@Submit" FormName="CreateDeveloper">
<DataAnnotationsValidator />
<InputText @bind-Value="Model!.Name">
<button type="submit">Save</button>
</EditForm>
@code {
[SupplyParameterFromForm]
public Developer? Model { get; set; }
...
When the form is submitted, your model will be validated before your OnValidSubmit
method is called. If validations fails, your OnValidSubmit
method will not be called.
To display error messages for failed validation, add the ValidationMessage
component to your forms fields.
...
<InputText @bind-Value="Model!.Name">
<div>
<ValidationMessage For="() => Model!.Name" />
</div>
...
Fluent Validation
Spark comes with the Fluent Validation package installed for you.
Fluent Validation takes more effort to setup than the Data Annotations Validator but is more flexible and covers more advanced validation scenarios.
To use Fluent Validation in your forms, first create a Validator class for you forms model.
public class Developer {
[Required]
public string Name { get; set; }
}
public class DeveloperFormValidator : AbstractValidator<Developer>
{
public DeveloperFormValidator()
{
RuleFor(p => p.Name)
.NotEmpty().WithMessage("Name is required")
}
}
Then use the <FluentValidationValidator />
component inside of your EditForm
.
@page "/developers/create"
<EditForm Model="@Model" OnSubmit="@Submit" FormName="CreateDeveloper">
<FluentValidationValidator />
<InputText @bind-Value="Model!.Name">
<button type="submit">Save</button>
</EditForm>
@code {
[SupplyParameterFromForm]
public Developer? Model { get; set; }
...
To display error messages for failed validation, add the ValidationMessage
component to your forms fields.
...
<InputText @bind-Value="Model!.Name">
<div>
<ValidationMessage For="() => Model!.Name" />
</div>
...
Typically when submitting a form, your goal is to save or update some data in your database from the data submitted in the form.
To do this, simply inject your apps DatabaseContext
into your component and use it to save the forms data.
@page "/developers/create"
@inject DatabaseContext Db
<EditForm Model="@Model" OnSubmit="@SaveDeveloper" FormName="CreateDeveloper">
<InputText @bind-Value="Model!.Name">
<button type="submit">Save</button>
</EditForm>
@code {
[SupplyParameterFromForm]
public Developer? Model { get; set; }
protected override void OnInitialized() {
Model ??= new();
}
private void SaveDeveloper()
{
DB.Developers.Save(Model);
}
}
It’s common to want to redirect to another page in your app after a successful form submission.
You can utilize the NavigationManager
class to do this.
@page "/developers/create"
@inject NavigationManager NavManager
...
@code {
private void SaveDeveloper()
{
DB.Developers.Save(Model);
// Navigate to another page.
NavManager.NavigateTo("dashboard");
}
}