English
Français

Blog of Denis VOITURON

for a better .NET world

Blazor Conventions

Objectives

This part of the document is mainly based…

  1. On our experience to write the simplest and best performing code possible.
  2. On how to create and use Razor components in Blazor apps documentation.
  3. On Microsoft Coding Conventions.



Component name

Blazor Components are implemented using a combination of C# and HTML markups. The HTML tags must be in [Component].razor file and C# code must be written in [Component].razor.cs associated file: don’t use @code { } section in razor file. Common Blazor naming conventions include:



Component structure

Organize files and components in a folder structure like this. This makes it easy to find the code related to a page, without having to browse the entire file explorer. Try, as much as possible, to respect the SOLID principles. Mainly by creating autonomous and extensible components: inject the smallest possible service or parameter, manage all the possibilities offered by the component. For example, a data modification page should display the data, check their values and save the data at the end of the process.

For example:

|-- App.razor
|-- Components
|   |-- TimeComponent.razor
|   |-- TimeComponent.razor.cs
|-- Pages
|   |-- Calendars
|   |   |-- CalendarMainPage.razor
|   |   |-- CalendarMainPage.razor.cs
|   |   |-- CalendarMainPage.razor.css
|   |   |-- CalendarMainPage.razor.js
|   |   |-- CalendarEditDialog.razor
|   |   |-- CalendarEditDialog.razor.cs
|   |   |-- CalendarEditDialog.razor.css
|   |   |-- CalendarEditDialog.razor.Parameters.cs

VSCode and Visual Studio have a File nesting feature to regroup visually each item of same page or dialog. In VSCode, set this settings: "explorer.fileNesting.patterns": { "$(capture).razor, $(capture).razor.*".



Methods and properties

The variables and properties must be organized in the following order.

  1. Private variables (using readonly if possible) and constants
  2. The [Inject] properties
  3. [Parameter] properties
  4. Public methods and properties
  5. Internal and Protected methods/properties
  6. Private methods/properties

Place the attributes (Inject, Parameter, …) above the property.

[Inject]
public IJSRuntime JsRuntime { get; set; } = default!;

Since the project has been defined as accepting nullables, it is recommended to define non-null variables and properties with a default! value. To avoid having a warning, at compile time, assign this value by default, only to properties that do not accept null values. For properties that accept null values, don’t forget to define them with the ? (including simple types or objects like string?, int? or object?.

public IConnectionManager Connection { get; set; } = default!;



JavaScript collocated with a component

⚠️ Avoid having too many calls between .NET and JavaScript, because that’s require additional overhead:

  • By default, calls are asynchronous.
  • By default, parameters and return values are JSON-serialized to provide an easy-to-understand conversion mechanism between .NET and JavaScript types.
  • Additionally, on Blazor Server, these calls are passed across the network.

If you need to create JavaScript functions, use the Collocation of JS files.

Collocation of JavaScript files for pages and Razor components is a convenient way to organize scripts in an app: it’s easier to centralize and find the JS code associated to a component. It does not pollute the client with global functions(*). And the component will load this code only when needed.

(*) If a JavaScript function is used by at least 3 components, you can include it in a global file (in wwwroot/js).

Save the JavaScript code in a .js file below the component.

  1. Create a [Component].razor.js file with your JavaScript code.
    export function myModule_myJsMethod() {
       ...
    }
    
  2. Add the following constant and two properties.
    private const string JAVASCRIPT_FILE = "./Pages/[Path]/[Component].razor.js";
       	  
    [Inject]
    private IJSRuntime JsRuntime { get; set; } = default!;
       
    private IJSObjectReference JsModule { get; set; } = default!;
    
  3. Call the JavaScript function, from your C# code, as follows.
    if (JsModule == null)
    {
      JsModule = await JsRuntime.InvokeAsync<IJSObjectReference>("import", JAVASCRIPT_FILE);
    }
    await JsModule.InvokeVoidAsync("myModule_myJsMethod");
    



CSS isolation

If you want to customize the style of your component, it is a good practice to write your styles in a sub-css file: [Component].razor.css. Within the generated bundled file, each component is associated with a scope identifier. For each styled component, an HTML attribute is appended with the format b-[string], where the [string] placeholder is generated by the framework (ex. b-3xxtam6d07).

Find more details in the Microsoft documentation.

To apply changes to a child component, use the ::deep pseudo-element to any descendant elements in the parent component’s .razor.css file. The ::deep pseudo-element selects elements that are descendants of an element’s generated scope identifier.

Example:

::deep h1 { 
    color: red;
}

⚠️ ::deep is only for child components. So, add a global <div> in your component to define the components used as child components.

More details here: https://youtu.be/91zH36Q8Qro

To avoid conflicts with other projects or libraries, add a common prefix to all style names (ex. .studio-nsi-popup).



Performances

More details on https://docs.microsoft.com/en-us/aspnet/core/blazor/performance

Languages

EnglishEnglish
FrenchFrançais

Follow me

Recent posts