DotNetT2 Light_1200x303

What is Razor? How does it relate to Blazor, and which should you be using? Learn the difference between Blazor and Razor and how to use them in your web applications.

You say Blazor, I say Razor, let's call the whole thing off!

If you've spent any time in the .NET ecosystem, the chances are you've heard people talk of Razor and Blazor, often in the same sentence.

But what is Razor? And is it still relevant now that we have Blazor?

Razor is a Template Markup Syntax

Razor started out life as a template markup syntax, based on C#.

The idea? You could write (mostly) standard HTML markup and throw some C# at it to handle things like showing data and executing view logic.

The common use case for Razor in the last decade or so has been to power ASP.NET MVC Views.

When you write an MVC View (the V part of MVC) you typically use Razor syntax to bind data to your markup.

<h1>
    Hello @Model.FirstName
</h1>

Beyond simple model binding, Razor also enables you use conditional expressions, such as if statements:

@if(Model.IsAdmin){
    <button>Delete Everything!</button>
}

Plus you can do other C# things, such as looping over data using foreach loops.

@foreach(var person in Model.People){
    <h2>@person.Name</h2>
}

At its core, Razor enables you to take standard HTML markup and mix it together with data coming from somewhere else.

Now, it's a good idea at this point to keep in mind the two distinct parts of any UI code that you write.

You have the Markup itself (basically HTML) and Logic — sometimes called UI logic, sometimes business logic.

Either way, this "logic" acts as the glue between your markup (the view) and the rest of your application.

In an MVC app, the UI logic tends to go in the controller where you interact with your application and databases, ultimately retrieving data to send back via a view.

PeopleController.cs

public IActionResult ListPeople(){
    // retrieve some data from your application/database
    var model = PeopleService.ListAll();
    return View(model);
}

Razor Pages — An MVC Alternative

With ASP.NET Core, Microsoft introduced another option.

Razor Pages did away with Views and Controllers, replacing them instead with individual pages.

Crucially, Razor Pages still employ Razor as their template markup syntax (the clue's in the name), meaning you can still write your markup in exactly the same way, using Model Binding, foreach loops and conditional expressions to bring your data to life.

Remember the markup vs. UI logic distinction we made about MVC? It applies here too.

Razor Pages typically consist of a .cshtml page and a cshtml.cs page.

The .cshtml page is where your markup goes, leaving your cshtml.cs page free to take on the UI logic (acting as the bridge between your markup and the rest of your application).

People.cshtml

@page
@model YourApp.Pages.PersonDetails

@foreach (var person in Model.People)
{
    <h2>@person.Name</h2>
}

People.cshtml.cs

public class PersonDetails : PageModel
{
    public List<Person> People { get; set; }

    public void OnGet()
    {
        People = PeopleService.ListAll();
    }
}

In this case, when someone navigates to /People the list of people is retrieved and assigned to People on the Razor Page model.

The Razor syntax in People.cshtml employs a foreach loop to loop over the People list (from the model) and render each person's name.

Razor Components (Hello Blazor)

Finally, we have the new kid in town.

Blazor is an alternative to MVC and Razor Pages but with a twist: It's a single page app framework (SPA) that just happens to use C# instead of JavaScript.

Blazor applications can run on the server, or in the browser thanks to Web Assembly.

But where does Razor fit in to this picture?

It turns out, the components you build for your Blazor applications are officially called Razor Components, and for good reason...

You write them using Razor! Here's an example.

@page "/HelloWorld"

<h1>
    See? It's still Razor!
</h1>

@foreach(var person in People){
    <h2>person.FirstName</h2>
}

This is still the Razor we're used to. You can do everything you could before (use loops, conditionals and so on).

But there is one crucial difference: with Blazor we're dealing in components, not pages.

Components All the Way Down

MVC and Razor Pages have always focused on serving entire pages to the browser.

Browser sends Request /UserList to ASP.NET, which contains Razor Page/Controller View. The Response to Browser is User List Page.

Every time the user clicks a button, or a link, the browser makes a request to the server, which hits the database, gets the .cshtml Razor Views (or Pages), mashes the data and markup together then returns the whole lot back to the browser (which re-renders the entire page).

Now, with Blazor, you are able to build your user interface as a series of components instead.

Browser request / to ASP.NET, which contains Razor Components. The Response is YourApp.dll

This shows how Blazor Web Assembly (Blazor WASM) works.

Much like a single page application built using JavaScript, the first request to your Blazor WASM application returns the entire application, including all of the components you've defined.

These components can represent anything from an entire page to the tiniest of tiny components in your application.

With these components now available to the browser, they can be shown, hidden and updated in response to data and events.

When running in the browser, Blazor applications make network calls to a backend to retrieve and post data; in this sense Blazor apps are more like the applications you'd write using a "modern" JavaScript library/framework such as Vue or Angular.

It's this component model which really sets the Blazor version of Razor apart from both MVC and Razor Pages.

Building Razor Components

To enable this shift to a component-based approach, Blazor brings some new features to the Razor Markup Syntax.

Here are the highlights:

Route to a Component

Sometimes you'll want to render a component when someone navigates to a specific route in your application.

In this mode, your component essentially acts as a page (or view) in your application.

@page "/HelloWorld"

<h1>Hello World</h1>

Render a Component Anywhere You Like

You don't have to route directly to a component, you can also render an instance of a component, inside any other component.

@page "/"

<h1>Welcome to the Progress Telerik Blog</h1>

<PostList />

<ContactUs />

Here we're rendering the PostList and ContactUs components on the page, which a user will get to when they navigate to the root of the application.

Passing Parameters

Rendering components is great, but pretty limited if you always had to show the exact same data every time.

Parameters enable you to pass different data into your components.

@page "/"

<h1>Welcome to the Progress Telerik Blog</h1>

<PostList PostCount="10" ShowSummaries="true"/>

In this case we're passing extra parameters to PostList to control how it behaves.

For this to work, PostList must declare those parameters.

PostList.razor

Showing @PostCount blog posts...

@code {
    [Parameter]
    public int PostCount {get;set;}

    [Parameter]
    public bool ShowSummaries {get;set;}
}

Rendering Child Content

Sometimes when you render a component, you'll also want to define the contents of that component.

This is easiest explained with an example.

Say you're building a dashboard for your application and want to show lots of information as a series of panels. Crucially you want all these panels to share the same look and feel (and perhaps behavior such as the ability to be minimized, expanded to full screen, etc.)

Even though you want each panel to behave and look the same, every time you render one you want to give it different content like so:

<Panel title="Most Active Users">
    <UserList />
</Panel>

<Panel title="Latest News">
    <h2>Highest ever number of downloads recorded in one month!</h2>
    <p>
        This month we recorded our highest ever number of downloads in a single month.
    </p>
</Panel>

Both are instances of a Panel component, but with completely different content.

The question becomes, how do we take this content which we've declared when using the component, and render it?

Razor Components has a trick up its sleeves; any content declared between the tags are made available via a special parameter.

Panel.razor

<div class="panel">
    @ChildContent
</div>

@code {
    [Parameter]
    public RenderFragment ChildContent { get;set; }    
}

Once you have access to the "middle bit" (the content inside the tags) via ChildContent, you can render it inside your component's markup wherever you like.

Helpers for Handling Forms

Forms are simple right?

Well, yes and no. On the surface, forms simply accept some data and post it somewhere else, but in reality, once you throw concerns such as validation into the mix, they tend to consume a disproportionate amount of your precious development time.

Blazor has a few handy helpers for forms, shipping with built-in form components to make the whole "form-building" process easier.

<EditForm Model="@personModel" OnValidSubmit="HandleSubmit">
    <InputText id="firstName" @bind-Value="personModel.FirstName" />
    <button type="submit">Submit</button>
</EditForm>

@code {
    private PersonModel personModel = new PersonModel();

    private void HandleSubmit(){
        // take action here, the form is valid!
    }
}

Here we have an instance of Blazor's EditForm component, which is bound to personModel (an instance of a PersonModel class).

The InputText component is then bound to the FirstName property of that model.

Now, assuming the form is "valid" (more on that in a moment), when a user enters a name and submits the form, the HandleSubmit method will be invoked and we can easily access the posted first name via personModel.FirstName.

But what about validation?

When you define your model, you can use Microsoft's Data Annotations to specify what constitutes valid data for your model.

public class PersonModel {
    [Required]
    public string FirstName { get;set; }
}

We can include an instance of the DataAnnotationsValidator component in our form to indicate we want Blazor to take note of said annotations.

<EditForm Model="@personModel" OnValidSubmit="HandleSubmit">    
    <DataAnnotationsValidator />

    <InputText id="firstName" @bind-Value="personModel.FirstName" />
    <button type="submit">Submit</button>
</EditForm>

Any attempt to submit the form without a FirstName will fail, HandleSubmit will not be invoked and the input(s) which fail validation will get an invalid CSS class.

Trying to submit the form without a first name fails, and the class displays invalid

Event Handlers

Finally, another big benefit of thinking in components instead of entire POSTs to the server is you can easily wire up buttons to do things when clicked!

<button @onclick="ToggleGreeting">
    Toggle Greeting
</button>

@if(showGreeting){
    <h1>Hey!</h1>
}

@code {
    private bool showGreeting { get;set; }

    private void ToggleGreeting(){
        showGreeting = !showGreeting;
    }
}

In MVC or Razor Pages, this would either require stepping outside of Razor to use a little client-side JavaScript, or involve making an entire form post to the server (so it can figure out what to do with your UI).

With Razor Components in Blazor, it's a simple job to provide a method that will be invoked when the button is clicked.

Razor Has Evolved

So where does this leave us?

Where Razor always had its strengths, making it fairly trivial to conditionally show/hide data and bind to your models, it now has new tricks thanks to Blazor.

Blazor brings a modern component-based version of Razor, making it possible to break your features down into tiny components which are small, focused and therefore quicker to build.

Once you have these components you can easily compose them together to make a bigger feature, or an entire application.

The only question left to answer? What will you build...


Jon Hilton
About the Author

Jon Hilton

Jon spends his days building applications using Microsoft technologies (plus, whisper it quietly, a little bit of JavaScript) and his spare time helping developers level up their skills and knowledge via his blog, courses and books. He's especially passionate about enabling developers to build better web applications by mastering the tools available to them. Follow him on Twitter here.

Related Posts

Comments

Comments are disabled in preview mode.