<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~files/atom-premium.xsl"?>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedpress="https://feed.press/xmlns" xmlns:media="http://search.yahoo.com/mrss/" xmlns:podcast="https://podcastindex.org/namespace/1.0">
  <feedpress:locale>en</feedpress:locale>
  <link rel="hub" href="https://feedpress.superfeedr.com/"/>
  <logo>https://static.feedpress.com/logo/telerik-blogs-web-blazor-61850150254c2.jpg</logo>
  <title type="text">Telerik Blogs | Web | Blazor</title>
  <subtitle type="text">The official blog of Progress Telerik - expert articles and tutorials for developers.</subtitle>
  <id>uuid:c4f0568c-1d21-4183-b64e-94826c6a4dbf;id=4529</id>
  <updated>2026-07-07T01:11:20Z</updated>
  <link rel="alternate" href="https://www.telerik.com/"/>
  <link rel="self" type="application/atom+xml" href="https://feeds.telerik.com/blogs/web-blazor"/>
  <entry>
    <id>urn:uuid:25764796-2937-4908-aea5-7d0a72c05de6</id>
    <title type="text">Choosing the Right Telerik UI for Blazor Chart</title>
    <summary type="text">Which type of chart is best for your data set? Explore the different types of Blazor charts and get a guide of which to use in certain use cases.</summary>
    <published>2026-06-30T16:57:43Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Héctor Pérez </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17370447/choosing-right-telerik-ui-blazor-chart"/>
    <content type="text"><![CDATA[<p><span class="featured">Which type of chart is best for your data set? Explore the different types of Blazor charts and get a guide of which to use in certain use cases.</span></p><p>In data-driven applications, it is common to want to display information dynamically and visually. However, there are times when we have doubts about which type of chart to use in our applications.</p><p>Choosing the wrong type of chart can cause users to become confused or even distort the information by displaying it incompletely or with a layout that doesn&rsquo;t make much sense.</p><p>That is why this article is a roundup of the types of charts available in the Progress Telerik <a target="_blank" href="https://www.telerik.com/blazor-ui">UI suite for Blazor</a> and when to use each. Let&rsquo;s get to it!</p><h2 id="chart-types-in-telerik-ui-for-blazor">Chart Types in Telerik UI for Blazor</h2><p>Let&rsquo;s analyze the different types of <a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/components/chart/overview">charts available in Telerik UI for Blazor</a>, as well as when to use them.</p><h3 id="column-chart">Column Chart</h3><p>The first type of chart, column, is perhaps the simplest. With this chart we can display vertical bars in which the height represents the value of each category. It&rsquo;s a quite versatile chart type, which is why it&rsquo;s very common to see it in applications and elsewhere. The chart looks like the following:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/simple-column-chart.png?sfvrsn=2ad90540_2" alt="Simple column chart" /></p><p>This type of chart is useful when we want to compare values across categories, show rankings or classifications, and visualize data where order does matter.</p><h3 id="bar-chart">Bar Chart</h3><p>The bar chart is the equivalent of the column chart but in a horizontal format. In the Telerik component <code>ChartSeriesType.Bar</code> is used and the category axis is on the vertical axis instead of horizontal:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/simple-bar-chart.png?sfvrsn=2e966c33_2" alt="Simple bar chart showing categories and values." /></p><p>This chart can be used when category names are long and you need more space to display them, when you want to show a horizontal comparison between values, and when you want to emphasize the difference in values between categories.</p><h3 id="line-chart">Line Chart</h3><p>The line chart displays a graph with connected points, which makes this chart ideal for showing trends over time. In the Telerik component it supports normal, step and smooth lines:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/simple-line-chart.png?sfvrsn=4c604b1_2" alt="Simple line chart showing an upward trend" /></p><p>This chart is suitable when we want to visualize temporal trends, compare different evolutions within the same period and when continuity between data points is relevant.</p><h3 id="area-chart">Area Chart</h3><p>This type of chart is similar to the line chart, with the difference that the space beneath the lines is filled with a semi-transparent color. This gives greater emphasis to the volume or magnitude of the data. In the Telerik component we can control the opacity using the <code>Opacity</code> property.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/area-chart-simple.png?sfvrsn=5e3dca17_2" alt="Simple area chart showing rising values over time" /></p><p>You can use this chart when you want to emphasize the volume of the data, their trend or when you want to show how different series overlap.</p><h3 id="pie-chart">Pie Chart</h3><p>The pie chart displays a circle divided into proportional slices for each value specified in the category. It only supports a single series, which makes it an ideal chart for showing the composition of a whole:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/simple-pie-chart.png?sfvrsn=56a76ea0_2" alt="Simple pie chart showing proportional data distribution" /></p><p>Some examples of when to use this type of chart are when you want to show a percentage distribution of data, while being mindful of the maximum number of slices so as not to overcrowd the chart. Likewise, when you want users to quickly understand portions of a whole and when you require the values to add up to 100%.</p><h3 id="donut-chart">Donut Chart</h3><p>This type of chart is similar to a pie chart but with a hole in the center. This hole can be used to display a highlighted data point or, as the Progress Telerik team has done, allow multiple series to be shown:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/donut-chart-simple.png?sfvrsn=89334033_2" alt="Simple donut chart with four colored segments" /></p><p>This chart is recommended for showing, in addition to the pie-chart scenarios, hierarchical relationships between data sets.</p><h3 id="scatter-chart">Scatter Chart</h3><p>The scatter chart displays a series of points defined by the <strong>x</strong> and <strong>y</strong> axes, with these being numeric data:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/scatter-plot-basic.png?sfvrsn=f64a9932_2" alt="Scatter plot showing correlated data points" /></p><p>This type of chart is ideal when you want to show a correlation between two numeric variables, such as the relationship between users connected in an application and a website&rsquo;s load time. It is also useful for identifying patterns, clusters, scientific data, among others.</p><h3 id="bubble-chart">Bubble Chart</h3><p>This chart is similar to the scatter chart in how it works, but adds a third dimension, which is the size of the bubble:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/simple-bubble-chart.png?sfvrsn=34686cbe_2" alt="Simple bubble chart showing values by size" /></p><p>The third dimension is useful for comparing entities with multiple metrics simultaneously.</p><h3 id="scatter-line-chart">Scatter Line Chart</h3><p>The scatter line chart is similar to the scatter chart, except that in this case the points are connected by lines:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/scatter-line-plot.png?sfvrsn=86d3bc9b_2" alt="Scatter plot with connecting line showing data relationship" /></p><p>This chart is convenient to use when you want to show the relationship between two variables while maintaining continuity in the data or trend.</p><h3 id="candlestick-chart">Candlestick Chart</h3><p>This candlestick-style chart is widely used in the financial sector, as it allows showing four values for a period: open, close, high and low. If we are talking about stocks in the securities market, the body of the candle represents the price from open to close, while the wicks, which are the thin lines above and below, show the period&rsquo;s high and low:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/candlestick-chart.png?sfvrsn=a9d98305_2" alt="Candlestick chart showing market price movements" /></p><p>This chart is suitable for showing financial movements or when you want to identify trading patterns.</p><h3 id="ohlc-chart-open-high-low-close">OHLC Chart (Open-High-Low-Close)</h3><p>This chart shows the same four values as the candlestick chart, but instead of using candle bodies, it uses horizontal marks. In this chart the horizontal values represent the open and close, while the lower and upper extremes show the maximum and minimum:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/ohlc-candlestick-chart.png?sfvrsn=3fafa6ef_2" alt="Candlestick OHLC chart" /></p><p>The use cases for this chart are the same as for the candlestick chart, but when you want a cleaner visualization. It is also ideal when you want to show many time periods, in which a candlestick chart would overlap the values.</p><h3 id="heatmap-chart">Heatmap Chart</h3><p>The heatmap displays a grid where color intensity represents the magnitude of a value across two dimensions:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/heatmap-chart-color-grid.png?sfvrsn=2f853608_2" alt="Heatmap showing color-coded intensity across a grid" /></p><p>If you are a software developer, you will surely recognize the image above, since GitHub shows a similar chart to display the commits you have made on an account. This demonstrates that the chart is ideal for showing an activity density in two dimensions and when you want to show behavior patterns across intersecting categories.</p><h3 id="radar-line-chart">Radar Line Chart</h3><p>This chart starts from a common center from which several axes radiate. Each axis represents a specific value, which are then connected to form a closed shape:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/radar-line-chart.png?sfvrsn=2ef60c49_2" alt="Radar line chart comparing multiple variables" /></p><p>It is very useful when you want to make profile comparisons, such as variables between LLM models or when you want to make before/after comparisons.</p><h3 id="radar-area-chart">Radar Area Chart</h3><p>This radar area chart is similar to the radar line chart, with the difference that the space of the formed shape is filled with a color:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/radar-area-chart-filled.png?sfvrsn=364cf4ed_2" alt="Filled radar area chart showing multivariate data" /></p><p>This chart works well to understand how different profiles overlap on the chart quickly.</p><h3 id="radar-column-chart">Radar Column Chart</h3><p>The radar column chart follows the same principle as the radar line and radar area charts, with the difference that it shows diagonal bars extending from the center:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/radar-column-chart.png?sfvrsn=5607c18f_2" alt="Radar column chart for comparison" /></p><p>By showing bars instead of connecting points, this chart is excellent for comparing individual values across categories and when an overlap in the radar area could make reading values difficult.</p><h3 id="range-area-chart">Range Area Chart</h3><p>The range area chart shows an area defined between two continuous lines defining the range between a minimum and a maximum value, similar to the area chart but not starting from zero:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/range-area-chart.png?sfvrsn=1a0018c5_2" alt="Range area chart showing high and low values." /></p><p>By displaying ranges, it is ideal in scenarios such as temperature ranges, error margins and when you need to emphasize a difference between continuous sequences of values.</p><h3 id="range-bar-chart">Range Bar Chart</h3><p>This chart lets you display ranges of values over a period of time using horizontal bars, whose position and length vary according to a from value and a to value:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/range-bar-chart.png?sfvrsn=d0c7eb72_2" alt="Horizontal range bar chart showing start and end values" /></p><p>This chart can help you show time ranges or durations, or compare ranges between categories or groups.</p><h3 id="range-column-chart">Range Column Chart</h3><p>It is the vertical version of the range bar chart. In this chart vertical bars are displayed, which vary according to the from and to values:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/range-column-chart.png?sfvrsn=13016741_2" alt="Range column chart showing min and max values" /></p><p>This chart could be useful to show ranges of scores between periods or when you need to emphasize both the minimum and the maximum of each category.</p><h3 id="waterfall-chart">Waterfall Chart</h3><p>This chart is based on a series of bars that show a cumulative value of sequential positive and negative values. Each bar starts where the previous one ended, which allows you to see the value&rsquo;s behavior after a period. In the Telerik component it is also possible to display columns with total or partial values:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/waterfall-chart-simple.png?sfvrsn=5b498a9d_2" alt="Waterfall chart showing sequential positive and negative values" /></p><p>Use cases for this chart include when you need to analyze cash flow over months, for budget and profitability analysis, among others.</p><h3 id="sankey-chart">Sankey Chart</h3><p>This Telerik component <code>TelerikSankey</code> displays a set of entities and links to represent the flow between them. In addition, the thickness of each link shows the value, allowing users to easily see the entity-to-value flow:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/sankey-diagram-flows.png?sfvrsn=be7afe80_2" alt="Sankey diagram showing flows between categories" /></p><p>Because of its flexibility, the Sankey chart is ideal for analyzing traffic distributions, breaking down budgets or redistributing resources.</p><h3 id="stock-chart">Stock Chart</h3><p>To use a stock chart, you should use the <code>TelerikStockChart</code> component, which is part of the Telerik UI for Blazor suite. This component is specifically designed for financial time series data. It supports types we&rsquo;ve already covered such as candlestick and OHLC, with the difference that it includes a navigator to select a specific time period:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/stock-market-line-chart.gif?sfvrsn=33fc642e_2" alt="Stock chart showing stock market price movements" /></p><p>This component is useful when you need to navigate time periods in financial data or to create trading dashboards or investment analysis.</p><h3 id="trendline-chart">Trendline Chart</h3><p>The last chart type we&rsquo;ll analyze is the trendline type. This chart isn&rsquo;t really a chart type where you define its own data; rather, it takes the data from an existing chart to reveal the trend of the data. The component supports several trend types: linear, average, exponential, logarithmic, among others:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/trendline-chart-simple.png?sfvrsn=68161b2c_2" alt="Simple trendline chart showing upward data trend" /></p><p>Some use cases for this chart type include revealing the overall direction of the data, smoothing fluctuations, performing forecasts, as well as conducting statistical analyses and validating patterns over time.</p><h3 id="decision-tree-for-selecting-the-best-chart">Decision Tree for Selecting the Best Chart</h3><p>After having analyzed the different types of charts that we can find in the Telerik UI for Blazor suite, I want to leave you a decision list that can help you select the best chart for your use case:</p><p><strong>Comparison &amp; Trends</strong><br />- If there is time or continuity -&gt; Use a line, area or scatter line chart<br />- If there are simple categories -&gt; Use a column or bar chart<br />- If there are long category names -&gt; Use a bar chart<br />- If you want to show underlying trends -&gt; Add a trendline</p><p><strong>Composition (Parts of a Whole)</strong><br />- For a single series -&gt; Use a pie or donut chart<br />- For multiple series -&gt; Use a donut chart<br />- For flows between domains -&gt; Use a Sankey diagram</p><p><strong>Relationships &amp; Distribution</strong><br />- With 2 variables -&gt; Use a scatter plot<br />- With 3 variables -&gt; Use a bubble chart<br />- For 2D density -&gt; Use a heatmap<br />- For multidimensional profiles -&gt; Use a radar chart (area, column or line)</p><p><strong>Ranges &amp; Cumulative Changes</strong><br />- To show the effect of changes -&gt; Use a waterfall chart<br />- For continuous ranges -&gt; Use a range area chart<br />- For time durations -&gt; Use a range bar chart<br />- For vertical ranges -&gt; Use a range column chart</p><p><strong>Specialized Data</strong><br />- For financial/price movement -&gt; Use a candlestick or stock chart</p><p>This list should help you make a better decision about when to use each type of chart.</p><h2 id="conclusion">Conclusion</h2><p>Throughout this article, we have taken a tour of the different types of charts that we can find in the Telerik suite for Blazor. This was aimed at understanding the structure of each one, how it looks and which are the best scenarios for their use. After this information, it&rsquo;s your turn to present your users&rsquo; data in different ways, always keeping in mind that their interpretation should be as simple as possible.</p><p>Ready to experiment with all these chart types? Telerik UI for Blazor comes with a free 30-day trial:</p><p><a href="https://www.telerik.com/try/ui-for-blazor" target="_blank" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/23050/17370447.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:f453a1a3-be23-4d75-bf04-ef6ba83b2753</id>
    <title type="text">Blazor Basics: Implementing a Loading Indicator for Asynchronous Components</title>
    <summary type="text">Learn how to properly implement a custom loading indicator and why professional UI libraries can provide the best user experience.</summary>
    <published>2026-06-24T19:22:32Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Claudio Bernasconi </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17366682/blazor-basics-implementing-loading-indicator-asynchronous-components"/>
    <content type="text"><![CDATA[<p><span class="featured">Learn how to properly implement a custom loading indicator and why professional UI libraries can provide the best user experience.</span></p><p>In modern web development, we often asynchronously load data from the server. Blazor is no different. To let the user know that data is being loaded and the app is working, we should show a busy or loading indicator during such operations.</p><p>In this article, we will implement a basic loading indicator in Blazor using CSS and also explore more advanced solutions.</p><p>You can <a target="_blank" href="https://github.com/claudiobernasconi/BlazorLoadingIndicator">access the code used in this example on GitHub</a>.</p><h2 id="the-blazor-project-template-approach">The Blazor Project Template Approach</h2><p>Let&rsquo;s start simple by looking at the loading indicator used in the default Blazor project template.</p><pre class=" language-html"><code class="prism  language-html">@if (forecasts == null)
{
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>em</span><span class="token punctuation">&gt;</span></span>Loading...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>em</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
}
else
{
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>table</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>table<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token comment">&lt;!-- code omitted --&gt;</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>table</span><span class="token punctuation">&gt;</span></span>
}
</code></pre><p>This very basic approach uses an <code>if-else</code> statement to determine the component state and a simple paragraph HTML element to render a <em>&ldquo;Loading&hellip;&rdquo;</em> text.</p><p>There are a few flaws with this overly simplistic solution:</p><ol><li><p>Static text does not provide a modern user experience. In a world where attention spans are short and user experience is important, we should use a <strong>spinner animation</strong> or another element that conveys the <strong>feeling of progress</strong>.</p></li><li><p>Static text does not act as a <strong>placeholder</strong> with the <strong>correct size</strong>. In the example above from the Weather page of the default Blazor project template, the <em>Loading</em> text has a much lower height compared to the fully rendered table containing the weather information.</p></li><li><p>A code snippet like the <em>Loading</em> text above is not reusable. It doesn&rsquo;t include a CSS class to make it look consistent across pages, nor is it reusable. Copying the same snippet across multiple pages will yield different user experiences if one of the developers decides to alter its design in the future.</p></li></ol><h2 id="a-basic-reusable-loading-indicator-component">A Basic Reusable Loading Indicator Component</h2><p>Let&rsquo;s build a basic <strong>reusable</strong> <code>LoadingIndicator</code> component. First of all, we want to internalize the loading state.</p><p>This helps us get rid of the <code>if-else</code> statement in each page component. We want the code in the page component to look like this:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>LoadingIndicator</span> <span class="token attr-name">IsLoading</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(forecasts == null)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>table</span><span class="token punctuation">&gt;</span></span>
    <span class="token comment">&lt;!-- code omitted --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>LoadingIndicator</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>Let&rsquo;s start with the component template:</p><pre class=" language-csharp"><code class="prism  language-csharp">@<span class="token keyword">if</span> <span class="token punctuation">(</span>IsLoading<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"loading-indicator"</span> Style<span class="token operator">=</span><span class="token string">"@($"</span>height<span class="token punctuation">:</span> <span class="token punctuation">{</span>Height<span class="token punctuation">}</span>px<span class="token string">")"</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"circular-progress"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"progress-text"</span><span class="token operator">&gt;</span>Loading<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
<span class="token punctuation">}</span>
<span class="token keyword">else</span>
<span class="token punctuation">{</span>
    @ChildContent
<span class="token punctuation">}</span>
</code></pre><p>We internalized the <code>if-else</code> statement handling the loading state. During loading, we display a loading indicator, which consists of three <code>div</code>s.</p><p>We show the child content (in the example above, an HTML table) when the loading state is <code>false</code>.</p><p>Now let&rsquo;s look at the code section:</p><pre class=" language-csharp"><code class="prism  language-csharp">@code <span class="token punctuation">{</span>
    <span class="token punctuation">[</span>Parameter<span class="token punctuation">]</span>
    <span class="token keyword">public</span> required <span class="token keyword">int</span> Height <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token number">96</span><span class="token punctuation">;</span>

    <span class="token punctuation">[</span>Parameter<span class="token punctuation">,</span> EditorRequired<span class="token punctuation">]</span>
    <span class="token keyword">public</span> required <span class="token keyword">bool</span> IsLoading <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span>Parameter<span class="token punctuation">,</span> EditorRequired<span class="token punctuation">]</span>
    <span class="token keyword">public</span> RenderFragment<span class="token operator">?</span> ChildContent <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Here, we have three component parameters. Let&rsquo;s go through them one by one.</p><p>The <code>Height</code> parameter is optional (notice the absence of the <code>EditorRequired</code> attribute). By default, the height of our loading indicator is <strong>96 pixels</strong>. However, we can provide a custom value to this property for each instance on a page component.</p><p>The <code>IsLoading</code> parameter decides whether the loading indicator or its children are rendered.</p><p>The <code>ChildContent</code> parameter of type <code>RenderFragement</code> allows us to wrap the content of the page into the <code>LoadingIndicator</code> for a good developer experience.</p><p>Now that we have the component template and its behavior, we need the <strong>CSS styling</strong>:</p><pre class=" language-css"><code class="prism  language-css"><span class="token selector"><span class="token class">.loading-indicator</span> </span><span class="token punctuation">{</span>
    <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
    <span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span>
    <span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
    <span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
    <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">100%</span><span class="token punctuation">;</span>
    <span class="token property">gap</span><span class="token punctuation">:</span> <span class="token number">8</span>px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.circular-progress</span> </span><span class="token punctuation">{</span>
    <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">32</span>px<span class="token punctuation">;</span>
    <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">32</span>px<span class="token punctuation">;</span>
    <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">3</span>px solid <span class="token function">rgba</span><span class="token punctuation">(</span><span class="token number">0</span>, <span class="token number">0</span>, <span class="token number">0</span>, <span class="token number">0.1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token property">border-top</span><span class="token punctuation">:</span> <span class="token number">3</span>px solid <span class="token hexcode">#3b82f6</span><span class="token punctuation">;</span>
    <span class="token property">border-radius</span><span class="token punctuation">:</span> <span class="token number">50%</span><span class="token punctuation">;</span>
    <span class="token property">animation</span><span class="token punctuation">:</span> spin <span class="token number">0.8</span>s linear infinite<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.progress-text</span> </span><span class="token punctuation">{</span>
    <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token number">1</span>rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">/* Spinner animation */</span>
<span class="token atrule"><span class="token rule">@keyframes</span> spin</span> <span class="token punctuation">{</span>
    <span class="token selector">0% </span><span class="token punctuation">{</span>
        <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span><span class="token number">0</span>deg<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token selector">100% </span><span class="token punctuation">{</span>
        <span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span><span class="token number">360</span>deg<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>CSS, unfortunately, most of the time requires <strong>a lot of code</strong> to implement rather simple things.</p><p>For example, here, we have the <code>loading-indicator</code> class, which contains a <code>flex</code> layout and its layout properties. It basically takes up the entire available width and places its children one below the other.</p><p>Next, we have the <code>circular-progress</code> class, which defines the circle&rsquo;s layout. It&rsquo;s the base of our animation.</p><p>The <code>progress-text</code> class is simple for now and only sets the <code>font-size</code>.</p><p>Last but not least, we have an <strong>animation</strong> definition using <code>keyframes</code>. We use the named animation in the <code>circular-progress</code> class&rsquo;s animation property.</p><p>Let&rsquo;s see how it works in action.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/loading-indicator.png?sfvrsn=f76d683b_2" title="Loading Indicator Component in a Blazor App" alt="The default Blazor web app project template with a custom loading indicator on the Weather page." /></p><p>I set the delay in the <code>Weather</code> page component to <strong>2 seconds</strong> so that I can see the full loading animation for testing.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token comment">// Simulate asynchronous loading to demonstrate a loading indicator</span>
<span class="token keyword">await</span> Task<span class="token punctuation">.</span><span class="token function">Delay</span><span class="token punctuation">(</span><span class="token number">2000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>As soon as the data is loaded from the screen, the <code>IsLoading</code> property of the <code>LoadingIndicator</code> component will return <code>false</code>. Therefore, instead of the circular progress animation, we will see the pages&rsquo; content.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/rendered-page-content.png?sfvrsn=b7f42a10_2" title="Weather page with weather information" alt="The default Blazor web app project template with the Weather page and rendered weather information." /></p><p><strong>Hint:</strong> One of the advantages of component-oriented web frameworks such as Blazor is that we can reuse this <code>LoadingIndicator</code> component and therefore hide the large CSS definition and the component behavior behind this <code>LoadingIndicator</code> component.</p><h2 id="professional-integrated-solutions">Professional Integrated Solutions</h2><p>While we definitely provided a more useful component and a better user experience with our reusable <code>LoadingIndicator</code> component, there is still room for improvement.</p><p>As application developers, we want to <strong>provide the best solutions for business problems</strong> while providing the <strong>best possible user experience</strong>.</p><p>We should focus on <strong>delivering value</strong> to our business rather than wasting time implementing generic components. Standing on the shoulders of giants is the right call.</p><p>There are professional user interface libraries, such as <a href="https://www.telerik.com/blazor-ui" target="_blank">Progress Telerik UI for Blazor</a>. Developers of those libraries invest much more time into building those reusable components than we are able to spend in our projects.</p><p>Component libraries like Telerik UI for Blazor integrate sophisticated <a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/common-features/loading-sign">loading indicators and loading state handling</a> into higher-level components, such as AutoComplete fields, Grid layouts and ListViews, and more advanced components like TreeList and TreeView.</p><h2 id="conclusion">Conclusion</h2><p>Handling the <strong>loading state</strong> and providing a <strong>good user experience</strong> during <strong>asynchronous data fetching operations</strong> is crucial in modern web development.</p><p>The default Blazor web application project template provides a simple solution that lacks in multiple regards.</p><p>We implement a custom <code>LoadingIndicator</code> component, which improves the solution from the default project template by extracting logic and behavior into a <strong>reusable component</strong>. Using its <code>Height</code> property, we can make the loading indicator take the same space as the rendered elements, thus preventing layout shifts. It&rsquo;s a step in the right direction.</p><p>However, if possible, I usually prefer using one of the brilliant, existing solutions over my handmade approach. For example, the <a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/common-features/loading-sign">Loader and LoaderContainer components</a> provide a great developer and user experience for building modern Blazor web applications.</p><hr /><blockquote><p>Telerik UI for Blazor comes with a free 30-day trial, so you can explore these plus 120 other components for yourself.</p><br /><p><a target="_blank" href="https://www.telerik.com/try/ui-for-blazor" class="Btn">Try Now</a></p></blockquote><img src="https://feeds.telerik.com/link/23050/17366682.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:49c497b3-3ecf-4392-b05b-8f6a9712b329</id>
    <title type="text">Telerik UI for Blazor Meets A2UI: The Next Step Toward Dynamic UI Generation</title>
    <summary type="text">Learn how A2UI enables AI systems to generate interactive, dynamically generated Blazor interfaces and how Progress Telerik UI for Blazor will help.</summary>
    <published>2026-06-15T13:30:53Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Maya Mateva </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17361459/telerik-ui-blazor-meets-a2ui-next-step-toward-dynamic-ui-generation"/>
    <content type="text"><![CDATA[<p><span class="featured">Learn how A2UI enables AI systems to generate interactive, dynamically generated Blazor interfaces and how Progress Telerik UI for Blazor will help.</span></p><p>Imagine this: instead of clicking through a travel app&mdash;picking dates, applying filters, scrolling results, opening tabs to compare&mdash;you type one sentence:</p><p><em>&ldquo;I want to get a flight from London to New York. Give me a plan as a dashboard with important data about the flight.&rdquo;</em></p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/blazor-a2ui-flight-dashboard.gif?sfvrsn=9f6fca6_2" alt="Telerik UI for Blazor A2UI produced a flight assistant dashboard that asks origin, destination, departure and return dates, then shows flight overviews, airport information, travel tips, flight prices in a bar chart, flight price data in table by month" /></p><p>What comes back is more than a paragraph explaining the basics. It&rsquo;s a whole interface with a structured results grid showing airline, price, duration and departure time. It&rsquo;s a comparison view so you can weigh your options, all assembled on the spot. No predefined screen needed. Just intent and a UI that responds to it.</p><p>That is the experience shift A2UI creates. And it is coming to <a href="https://www.telerik.com/blazor-ui">Progress Telerik UI for Blazor</a>.</p><p>Don&rsquo;t miss the live demo! <a href="https://www.telerik.com/campaigns/telerik-and-kendo-ui-2026-q2-release-webinar">Register for the Telerik and Kendo UI 2026 Q2 release webinar on June 16</a> to see A2UI transforming intent into interactive Blazor UI in real time.</p><h2 id="what-is-a2ui">What Is A2UI?</h2><p><a href="https://a2ui.org/specification/v0.8-a2ui/">A2UI (Agent-to-User Interface)</a> is an open protocol introduced by Google that defines a standard way for AI agents to communicate their intent to generate a user interface. Think of it as a universal UI language that agents can speak to any client application.</p><p>Rather than describing insights, AI describes interfaces. The application then renders those interfaces using its own UI framework, styling and interaction model.</p><p>In this model:</p><ul><li><strong>AI facilitates the UI composition layer.</strong> It reasons over context and emits a structured definition&mdash;a declarative JSON format describing which components to render and what data to populate them with.</li><li><strong>The application remains the source of truth for rendering and behavior.</strong> The client maintains a catalog of pre-approved components.</li><li><strong>The user experiences fully interactive interfaces generated from intent.</strong> Not static mockups. Not screenshots. Real components with real data.</li></ul><p>A2UI effectively shifts AI from being a narrator to being a <strong>composer of user experiences</strong>.</p><h2 id="a2ui-in-telerik-ui-for-blazor">A2UI in Telerik UI for Blazor</h2><p>Within Telerik UI for <a href="https://demos.telerik.com/blazor-ui/a2ui/ai-a2ui" target="_blank">Blazor, A2UI</a> introduces an additional layer on top of the existing component ecosystem. Components are no longer only developer-defined. They become composable primitives available for AI-driven interface construction.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/a2ui-progression.png?sfvrsn=986c7d88_2" alt="Backend, transport like AGUI, A2UI instead of text-only response – dynamically generated UI, Progress components, application" /></p><p>In effect, the library becomes a <strong>structured palette for dynamic interface generation</strong>. <strong>Your design system stays intact.</strong> <strong>Your business logic does not move.</strong></p><h2 id="why-a2ui-makes-sense-agents-think-in-text-users-work-in-interfaces">Why A2UI Makes Sense: Agents Think in Text; Users Work in Interfaces</h2><p>AI is rapidly reshaping how software is built and consumed. Over the past year, we have seen an acceleration of copilots, agents and large language models capable of reasoning over data, executing workflows and assisting users in ways that were previously out of reach.</p><p>Yet, one fundamental limitation remains.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/agents-text-users-interfaces.png?sfvrsn=ba8582d0_2" alt="Agents think in text. Users work in interfaces" /></p><p>AI is exceptionally good at producing text. But applications are not built around text. They are built around interfaces.</p><p>People perceive and interact more easily through visuals&mdash;dashboards, forms, grids, charts, filters, and workflows they can interact with directly. They want to see, explore and act. This is why A2UI has so much potential.</p><p>Modern enterprise applications have long assumed that UI is static. Developers define screens. Users adapt to them. AI, when introduced, typically sits alongside the application as a conversational layer rather than becoming part of the interface itself.</p><p>A2UI introduces the possibility to change that. When AI systems generate structured UI definitions that adapt to context and data, rather than just a block of text, we aren&rsquo;t just enabling what the <em>system says</em>, but what the <em>user sees</em>.</p><p>A single request like &ldquo;analyze my sales performance this quarter&rdquo; can translate into multiple valid experiences depending on who is asking and why&mdash;an executive KPI dashboard, a detailed filterable data grid, a chart-centric exploratory view or a hybrid analytical workspace. The underlying data is the same, but the intent is different. And AI is uniquely positioned to interpret it.</p><h2 id="a2ui-in-practice">A2UI in Practice</h2><p>Use cases for A2UI implementation are as myriad as you can imagine.</p><p>Let&rsquo;s go back to the example of booking a flight. Instead of tedious stepping through pages, one by one, filling and selecting values, you can type: <em>&ldquo;I want to fill a form for booking a flight from London to New York.&rdquo;</em></p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/a2ui-flight-form.png?sfvrsn=6dcce9c5_2" alt="In prompt box, I want to fill a form for booking a flight from London to New York. Then fields for origin, destination, departure date and time, passenger name" /></p><p>The AI interprets that intent and <strong>emits</strong> a UI definition&mdash;a JSON description of a data form (pre-filled with data where possible and using the right components), a results grid (columns: airline, price, departure, duration, stops) and a comparison chart with the top options.</p><p>The Blazor renderer receives that definition and displays the interface using real Telerik UI for Blazor components. The Grid, the Form, the Charts&mdash;all themed and all enabling you to interpret and visualize data as naturally as possible.</p><p>Different users expressing the same intent might receive different representations depending on context, preferences or available data. Maybe one person gets a chart-first view and another gets a table. That is dynamic UI generation in practice.</p><h3 id="a-step-further-building-on-an-emerging-ai-ecosystem">A Step Further: Building on an Emerging AI Ecosystem</h3><p>The flight-booking scenario reflects a much broader shift happening across the AI ecosystem. Applications are moving from static interactions toward dynamic, personalized, agent-driven experiences where intent, reasoning and UI composition are tightly connected.</p><p>This is where the A2UI approach becomes part of a larger architecture rather than a standalone concept.</p><p>In the current implementation, the demo builds on complementary layers that are increasingly relevant in modern AI systems:</p><ul><li><strong>AI interprets user intent</strong>, translating natural language into structured goals and constraints.</li><li>The <a href="https://learn.microsoft.com/en-us/agent-framework/overview/"><strong>Microsoft Agent Framework</strong></a> <strong>orchestrates reasoning and execution</strong>, coordinating tools, workflows and multi-step decision-making across systems.</li><li><a href="https://docs.ag-ui.com/introduction"><strong>AG-UI</strong></a> <strong>provides the interaction contract for agent-driven interfaces</strong>, so UI intent and structure are expressed in a consistent, agent- and renderer-agnostic way.</li><li><strong>The A2UI layer for Telerik components for Blazor defines the structure of the user experience</strong>, describing how the outcome of that reasoning should be expressed as a dynamic, interactive UI rather than plain text.</li><li><strong>Telerik UI for Blazor renders the final experience</strong>, materializing that structure using enterprise-grade components such as grids, forms and charts with full theming, interactivity and performance.</li></ul><p>Together, these components enable AI systems to move beyond conversational output and participate directly in application workflows. Instead of producing text responses, agents can reason over data and shape the user experience itself.</p><h2 id="telerik-a2ui-renderer-for-blazor—availability-preview">Telerik A2UI Renderer for Blazor&mdash;Availability: Preview</h2><p>A2UI is still an emerging concept and ecosystem. Standards, protocols, component contracts and implementation patterns are actively evolving as the industry explores what dynamic UI generation should look like in production applications.</p><p>We are approaching A2UI with the same mindset. The feature is still experimental. As the implementation is evolving, feedback is actively shaping its direction.</p><p>During this phase, we are actively observing real-world usage patterns, gathering feedback from early adopters and refining how intent translates into UI structure. Specifically, we are exploring where AI-generated UI delivers real value and how much control users need to retain over generated interfaces.</p><p>The <a href="https://a2ui.org/specification/v0.8-a2ui/">A2UI specification</a> itself is at v0.8 stable, and the ecosystem around it&mdash;transport layers like AG-UI, Agent Frameworks from Microsoft (hint: we are exploring those too) and others and renderers for different frameworks&mdash;is all moving quickly. This is the right moment to explore the pattern, give us feedback and understand how the renderer model fits your architecture. And get to the next level of creating great UI and UX experience.</p><p>The current A2UI Preview for Telerik UI for Blazor focuses on a constrained but high-impact set of building blocks:</p><ul><li><strong>Input components</strong> &ndash; text fields, date pickers, numeric inputs, dropdowns</li><li><strong>Data Grid</strong> &ndash; data exploration, visualization and comparison</li><li><strong>Charts</strong> &ndash; visual representations that adapt to what the data warrants</li></ul><p>These are intentionally chosen because they represent the backbone of most business applications. This Preview is <strong>opinionated by design</strong>. Rather than attempting to support every UI pattern, it focuses on exploring how intent can reliably map to structured, usable interfaces. <a href="https://demos.telerik.com/blazor-ui/a2ui/ai-a2ui" target="_blank">Go straight to the demo</a>, click and prompt around on an easy-to-follow use case and get in touch with feedback.</p><p>If your organization is exploring AI-powered applications in 2026, this is the ideal time to start a pilot. Use our <a href="https://www.telerik.com/account/support-center/contact-us">Support system</a> to tell us more about your use case or drop us a line in our <a href="https://feedback.telerik.com/blazor">Blazor Feedback Portal</a> or in the <a href="https://www.telerik.com/forums/blazor">Telerik Forums</a>.</p><p>The goal isn&rsquo;t to replace your existing application. The goal is to explore how AI can augment it through interfaces that adapt to the user, the context and the task at hand.</p><h2 id="webmcp-vs.-a2ui-what’s-the-difference">WebMCP vs. A2UI: What&rsquo;s the Difference?</h2><p>If you&rsquo;ve been following our recent AI initiatives, you may have also seen our <a href="https://www.telerik.com/blazor-ui/documentation/ai/web-mcp/overview">Preview support for WebMCP</a>, which enables AI agents to interact with applications through standardized tools and capabilities.</p><p>While both WebMCP and A2UI are part of the broader movement toward AI-native applications, they address different parts of the big picture.</p><p>You can think of them as complementary layers: WebMCP focuses on how <strong>AI agents interact with applications</strong>. A2UI focuses on how <strong>applications interact with users</strong> through AI-generated experiences.</p><p>Together, they create a powerful foundation for AI-native applications and are an extension of our commitment to great UI and UX.</p><p>With WebMCP, an AI agent can discover available actions, execute workflows, retrieve data and interact with business functionality exposed by an application.</p><p>With A2UI, that same agent can dynamically generate forms, grids, charts, dashboards and other user experiences that help users understand, validate and act on the results.</p><p>An AI agent can use WebMCP to retrieve information, execute actions and understand business capabilities, while A2UI can transform those results into dynamic user experiences built with Telerik UI for Blazor components.</p><p>To learn more about our WebMCP Preview support, check out our blog post: <a href="https://www.telerik.com/blogs/telerik-and-kendo-meet-webmcp">Telerik and Kendo UI Meet WebMCP</a>.</p><p>The current capabilities represent only the beginning. The beauty of dynamic UI generation is that the number of possible use cases is virtually unlimited. AI maps context and intent, while Telerik UI for Blazor provides the components that bring those experiences to life.</p><hr /><p>Want to shape how A2UI support for Telerik UI for Blazor develops? Share your thoughts on the <a href="https://feedback.telerik.com/blazor">Blazor Feedback Portal</a> or in the <a href="https://www.telerik.com/forums/blazor">Telerik forums</a>. We are reading everything.</p><p>And don&rsquo;t forget to <a href="https://www.telerik.com/campaigns/telerik-and-kendo-ui-2026-q2-release-webinar">join the webinar</a> for a live demo of A2UI.</p><img src="https://feeds.telerik.com/link/23050/17361459.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:fc4f18d8-9f5a-4741-bfdd-7844b4bdd7e0</id>
    <title type="text">Exploring the SLNX Solution File Format</title>
    <summary type="text">SLNX files are here! In this post, we'll talk about what this format is, why it exists, what it gets right and where you might get tripped up.</summary>
    <published>2026-06-11T19:09:21Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Dave Brock </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17359642/exploring-slnx-solution-file-format"/>
    <content type="text"><![CDATA[<p><span class="featured">SLNX files are here! In this post, we'll talk about what this format is, why it exists, what it gets right and where you might get tripped up.</span></p><p>If you&rsquo;ve worked with .NET for any length of time, you&rsquo;ve made peace with the <code>.sln</code> file. Not because it&rsquo;s good (it isn&rsquo;t) but because it&rsquo;s the format we have. It&rsquo;s verbose, GUID-laden and a reliable source of merge conflicts on Friday afternoons. It&rsquo;s the format we tolerate, not the one we&rsquo;d choose.</p><p>The good news is there&rsquo;s a new format in town. The <code>.slnx</code> format is Microsoft&rsquo;s XML-based replacement for the venerable <code>.sln</code> file, and it has been steadily gaining first-class support across Visual Studio, the .NET CLI, MSBuild and Rider over the last year. As of <a target="_blank" href="https://devblogs.microsoft.com/dotnet/introducing-slnx-support-dotnet-cli/">.NET 9.0.200</a> and <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/new-simpler-solution-file-format/">Visual Studio 17.13+</a>, you can use it for real projects without crossing your fingers. And <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/dotnet-new-sln-slnx-default">as of .NET 10</a>, <code>dotnet new sln</code> defaults to <code>.slnx</code>.</p><p>In this post, let&rsquo;s talk about what <code>.slnx</code> is, why it exists, what it gets right and where you might get tripped up.</p><h2 id="a-quick-sln-retrospective">A Quick SLN Retrospective</h2><p>To appreciate why we&rsquo;re getting a new format, let&rsquo;s remember what we&rsquo;ve been working with. Here&rsquo;s a typical fragment of an <code>.sln</code> file.</p><pre><code>Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.34804.81
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApi", "MyApi\MyApi.csproj", "{F95781B3-A973-4D19-9585-974DA143E6A1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8FC526EA-218B-4615-8410-4E1850611F38}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F95781B3-A973-4D19-9585-974DA143E6A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F95781B3-A973-4D19-9585-974DA143E6A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F95781B3-A973-4D19-9585-974DA143E6A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F95781B3-A973-4D19-9585-974DA143E6A1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
</code></pre><p>Oof. The SLN format is a custom Microsoft format that has some weird quirks. Every project gets a GUID. Solution folders are themselves projects with their own GUIDs. Configuration combinations even get cross-multiplied so a four-project solution with two configurations and two platforms produces <em>16 lines</em> of nearly identical setup.</p><p>The pain points are well known to us all. If even two developers add a project at the same time, both edit <code>GlobalSection(ProjectConfigurationPlatforms)</code>, and Git throws its hands up.</p><p>GUIDs, bless their hearts, are not for human eyes. It&rsquo;s a nightmare trying to figure out which project owns which configuration block. And outside of Visual Studio, generating or modifying <code>.sln</code> files reliably often requires <a target="_blank" href="https://microsoft.github.io/slngen/">a tool like <code>slngen</code></a> because no one wants to write the parser themselves.</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">Visual Studio 2026, 6 Months Later</h4></div><div class="col-8"><p class="u-fs16 u-mb0">See what&rsquo;s really behind the <a target="_blank" href="https://www.telerik.com/blogs/visual-studio-2026-6-months-later">Visual Studio 2026 updates</a> and improvements, such as performance, GitHub Copilot, Hot Reload and more.
.</p></div></div><hr class="u-mb3" /></aside><p>This is what the <code>.slnx</code> tries to fix.</p><h2 id="looking-inside-the-.slnx">Looking Inside the .slnx</h2><p>Here&rsquo;s the same project, but expressed as <code>.slnx</code>.</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Solution</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/Solution Items/<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Directory.Build.props<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Folder</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>MyApi/MyApi.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Solution</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>Notice how we have no GUIDs, no cross-multiplied configuration table and no <code>EndGlobalSection</code> markers. Even a developer who is foreign to the <code>.slnx</code> format can read it and immediately understand it.</p><p>Expanding a little, here&rsquo;s an example from a layered solution.</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Solution</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/Solution Items/<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>.editorconfig<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Directory.Build.props<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Directory.Packages.props<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Folder</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/src/<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Application/Application.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Domain/Domain.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Infrastructure/Infrastructure.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Web.Api/Web.Api.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Folder</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/tests/<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tests/UnitTests/UnitTests.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tests/IntegrationTests/IntegrationTests.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Folder</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Solution</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>If you compare this to the equivalent <code>.sln</code>, you&rsquo;re looking at maybe a third of the lines, with none of the GUID overhead. If you need to override configurations for a specific project (like, say your test project shouldn&rsquo;t build in Release mode) you can express that with a <code>Configurations</code> element on the project. In reality, most projects won&rsquo;t need it at all. If you do, you can review examples in <a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence"><code>microsoft/vs-solutionpersistence</code></a> and its <a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence/wiki/Samples">samples wiki</a>.</p><p>If the <code>.slnx</code> looks similar to a <code>.csproj</code>, that&rsquo;s intentional. Microsoft&rsquo;s reasoning is that XML is already the format the rest of MSBuild speaks, the team already had a parser for it, and features like comments and attributes are first-class citizens. A JSON or YAML format might have been trendier, but it would have forced the team to invent semantics for things <code>.csproj</code> already has solved problems for.</p><h2 id="migrating-existing-projects">Migrating Existing Projects</h2><p>Existing projects are a different conversation. Here&rsquo;s what you get by migrating, and how to do it cleanly.</p><ul><li><strong>Merge conflicts are more manageable.</strong> Because there are no GUIDs that change every time you touch a project, the diffs you see in PRs are meaningful. If you add a project, you see one line added. Move a project to a different folder, and you see the path change. Nobody has to play GUID detective.</li><li><strong>You can reasonably edit by hand.</strong> You&rsquo;ll never want to hand-edit a <code>.sln</code> unless it&rsquo;s an emergency. With <code>.slnx</code>, opening it in your editor of choice is perfectly reasonable. I&rsquo;ve fixed broken solution layouts in a text editor faster than Visual Studio would have let me click through the dialogs.</li><li><strong>Tooling has standardized what a solution is.</strong> Microsoft has open-sourced the parser as the <a target="_blank" href="https://www.nuget.org/packages/Microsoft.VisualStudio.SolutionPersistence/">Microsoft.VisualStudio.SolutionPersistence NuGet package</a>. This means MSBuild, the .NET CLI and third-party tools are all reading the same file the same way.</li></ul><p>Based on the official <code>dotnet sln</code> reference, the <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-sln">actual conversion is one command</a>.</p><pre><code>dotnet sln migrate
</code></pre><p>If you run this in a directory with an <code>.sln</code> file, you&rsquo;ll get an <code>.slnx</code> file beside it. From Visual Studio, you can also use <strong>File -&gt; Save Solution As&hellip;</strong> and pick <strong>XML Solution File (.slnx)</strong> from the dropdown. Rider supports a similar flow.</p><p>You don&rsquo;t want to keep both files in the repository. The <code>dotnet sln</code> commands don&rsquo;t reliably pick the right one when you have both, and it&rsquo;s easy for the two to get out of sync as people add or remove projects. Pick when to cut over, delete the old file and move on. While there is <a target="_blank" href="https://github.com/edvilme/dotnet-sln-sync">a community tool</a> called <code>dotnet-sln-sync</code> that helps, that should only be used temporarily and not long term.</p><p>As you convert over, here&rsquo;s a quick checklist to avoid any headaches:</p><ul><li><strong>Verify your <code>global.json</code> allows the right SDK.</strong> You need at least .NET 9.0.200 for full CLI support. If your <code>global.json</code> pins to something older, the migrate command won&rsquo;t work.</li><li><strong>Check your CI build agents.</strong> If you&rsquo;re on self-hosted Azure DevOps agents, for example, confirm they&rsquo;re running an SDK new enough to handle <code>.slnx</code>.</li><li><strong>Audit your pipelines.</strong> Anywhere you have references directly to <code>.sln</code>, update the path. Wildcard references like <code>**/*.sln</code> will miss the new file. If you are using templates where you have a mix of <code>.sln</code> and <code>.slnx</code>, a pattern like <code>**/*.sln*</code> will be useful.</li><li><strong>Look out for dependent tools.</strong> Tools like <code>slngen</code> don&rsquo;t yet support <code>.slnx</code> at the time of this post (you can <a target="_blank" href="https://github.com/microsoft/slngen/issues/643">track the issue here</a>). If you&rsquo;re reliant on a tool outside of the Microsoft umbrella, check before you migrate.</li></ul><h2 id="some-rough-edges">Some Rough Edges</h2><p>The <code>.slnx</code> format is no longer &ldquo;preview&rdquo; in any practical sense, but the ecosystem around it is still catching up.</p><ul><li><strong>Visual Studio file association.</strong> Double-clicking a <code>.slnx</code> file doesn&rsquo;t open Visual Studio by default. You can fix this with a file association or just open it from inside the IDE.</li><li><strong>C# Dev Kit in VS Code.</strong> It works, but you may need to set <a target="_blank" href="https://devblogs.microsoft.com/dotnet/introducing-slnx-support-dotnet-cli/"><code>dotnet.defaultSolution</code></a> to the path of your <code>.slnx</code> if it isn&rsquo;t auto-detected.</li><li><strong>Globbing.</strong> You can&rsquo;t write <code>&lt;Project Path="src/**/*.csproj" /&gt;</code> and have it discover projects automatically. The team&rsquo;s reasoning, captured in the <a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence/issues/61">globbing feature request</a>, is that globbing slows down solution loading on large repos because Visual Studio has to scan the file system before it can render anything. That makes sense, <em>but</em> I think plenty of us would happily trade a slower cold load for never having to add another project entry by hand.</li><li><strong>Third-party tools.</strong> Anything that parses <code>.sln</code> directly needs to update. Hopefully the open-source parser library accelerates that, but you&rsquo;ll find holdouts.</li></ul><p>None of these are dealbreakers for new projects. For older codebases with a lot of tooling baked in, it&rsquo;s worth doing a small pilot before flipping the whole repo.</p><h2 id="wrapping-up">Wrapping Up</h2><p>The <code>.slnx</code> format isn&rsquo;t a revolutionary feature. It doesn&rsquo;t change how you write code, it doesn&rsquo;t add new build capabilities, and it doesn&rsquo;t speed up your application. It is, however, one of those quality-of-life upgrades that quietly results in fewer merge conflicts, less time staring at GUIDs, less friction in your CI pipelines once they&rsquo;re set up.</p><p>If you&rsquo;re starting a new .NET solution, just use <code>.slnx</code> from day one. And with .NET 10, you&rsquo;ll get it without lifting a finger. If you&rsquo;re maintaining an existing <code>.sln</code> file, plan a migration when you have a slow week, audit your tooling and cut over cleanly. The format isn&rsquo;t going anywhere, and getting ahead of the transition is easier than waiting for some downstream tool to force your hand.</p><p>Thanks for reading, and happy coding!</p><h2 id="further-reading">Further Reading</h2><ul><li><a target="_blank" href="https://devblogs.microsoft.com/visualstudio/new-simpler-solution-file-format/">New, simpler solution file format</a></li><li><a target="_blank" href="https://devblogs.microsoft.com/dotnet/introducing-slnx-support-dotnet-cli/">Introducing support for SLNX in the .NET CLI</a></li><li><a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence"><code>microsoft/vs-solutionpersistence</code></a></li><li><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-sln"><code>dotnet sln</code> command reference</a></li><li><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/dotnet-new-sln-slnx-default">.NET 10 breaking change: <code>dotnet new sln</code> defaults to SLNX</a></li></ul><img src="https://feeds.telerik.com/link/23050/17359642.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:f5808969-92f6-4e38-b801-f36a47627abf</id>
    <title type="text">Visual Studio 2026, 6 Months Later</title>
    <summary type="text">Throughout this post, we’ll discuss Visual Studio updates and improvements, such as performance, GitHub Copilot, Hot Reload and more.</summary>
    <published>2026-06-04T21:42:24Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Dave Brock </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17354316/visual-studio-2026-6-months-later"/>
    <content type="text"><![CDATA[<p><span class="featured">Throughout this post, we&rsquo;ll discuss Visual Studio updates and improvements, such as performance, GitHub Copilot, Hot Reload and more.</span></p><p>I remember exactly where I was when <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/visual-studio-2026-is-here-faster-smarter-and-a-hit-with-early-adopters/">Visual Studio 2026 dropped</a> back in November: at my desk, watching the Microsoft launch event with one eye and refactoring an <code>IDataReader</code> implementation with the other.</p><p>The keynote was typical Microsoft. There was a lot of &ldquo;reimagining how developers work.&rdquo; There was, of course, a slide with the word &ldquo;intelligent&rdquo; on it at least eight times. And the word &ldquo;Copilot&rdquo; appeared on screen so many times I started to wonder if Microsoft&rsquo;s marketing team had been replaced by an infinite loop that just prints <code>Copilot</code> forever.</p><p>Then I installed the thing.</p><p>The first week with VS 2026 was the most disoriented I&rsquo;ve felt in a Visual Studio release since <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/a-design-with-all-caps/">the menu bar went all-caps in 2012</a> and I spent a month reading angry forum threads about it.</p><p>In 2026, I was tripped up by the just-slightly different Solution Explorer, the Copilot panel that lives where my Test Explorer used to live, the general Copilot onslaught and the interesting design choices. I felt like everything on my desk had been moved slightly to the left.</p><p>That was temporary. As with most new technology, eventually things felt normal again. I was finding workflows that genuinely beat my old ones and I stopped noticing the cosmetic changes. So here&rsquo;s where I am six months later &hellip; same machine, same kind of solution I work in every day, just a different IDE around it.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/vs2026-overview.png?sfvrsn=4ef376fb_2" alt="" /><br /><span style="font-size:14px;">We meet again, old friend. At least the menu bar isn't in caps.</span></p><p>Six months in, I feel comfortable. Six months is long enough to live with the rough edges, and find what&rsquo;s genuinely useful. So I&rsquo;m here to share them with you.</p><p>As an aside: to keep my sanity intact, I&rsquo;m going to use the word &ldquo;Copilot&rdquo; only when I absolutely have to. Microsoft has decided that every feature in Visual Studio is now Copilot-something &hellip; Copilot Chat, Copilot Edits, Copilot Agent, Copilot Workspaces, GitHub Copilot, Copilot for Azure, Copilot for Pull Requests and so on. So when I say &ldquo;the agent&rdquo; or &ldquo;the AI thing in the sidebar,&rdquo; you&rsquo;ll know what I mean.</p><h2 id="the-performance-story-is-real">The Performance Story Is Real</h2><p>I&rsquo;ll lead with this, mostly because it was the thing I was most skeptical about. Every Visual Studio release for the past decade has touted &ldquo;X% faster solution load!&rdquo; and, to me, every Visual Studio release for the last decade has felt (roughly) the same speed once you test it with your beefy codebase.</p><p>VS 2026 is genuinely faster. Not &ldquo;marketing-faster&rdquo; &hellip; actually faster. The <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/visual-studio-2022/">64-bit-everywhere story that started with VS 2022</a> has matured into something that handles large solutions without the periodic &ldquo;please hold&rdquo; pauses we&rsquo;re all familiar with. In Microsoft&rsquo;s VS2026 launch post, they say they <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/visual-studio-2026-is-here-faster-smarter-and-a-hit-with-early-adopters/">cut hangs by over 50%</a>.</p><p>The IntelliSense responsiveness is the part I notice most. Typing a <code>.</code> after an object now produces suggestions roughly when I expect them to appear. A low bar? Definitely, but previous versions frequently limboed under it.</p><h2 id="agent-mode-is-useful-when-you-pick-the-right-job">Agent Mode Is Useful, When You Pick the Right Job</h2><p>GitHub Copilot&rsquo;s <a target="_blank" href="https://learn.microsoft.com/en-us/visualstudio/ide/copilot-agent-mode?view=visualstudio">agent mode in Visual Studio 2026</a>&mdash;or, as Microsoft prefers, <em>GitHub Copilot Agent Mode powered by Copilot in Visual Studio with Copilot</em>&mdash;has improved tremendously since Visual Studio 2022. Six months in, I think it&rsquo;s genuinely useful, but for a specific set of tasks.</p><p>In my experience, it works well with scoped, well-defined refactors. If I want to ask: <em>Hey Copilot, add a new health check for this Cosmos container and follow the pattern in my existing health checks.</em> (Yes, I sometimes talk to Copilot like a home assistant.) Or: <em>Take my new controller and add the same logging pattern as the others in this project.</em> And, after discovering some methods in a codebase with 11 nested if statements: <em>Convert this <code>for</code> loop to a LINQ expression but keep it readable.</em> It&rsquo;s effective enough that I&rsquo;ve stopped writing this kind of code by hand.</p><p>In my experience, things get harder when the agent needs to understand why something exists. The agent will happily refactor a piece of code that only exists because of, say, a weird third-party integration constraint from 2018, and the resulting code will be cleaner, more maintainable &hellip; and broken in production. Did I actually break something in production? Not this time. But I would have.</p><p>To be fair to the agent, this is exactly where it needs context. It doesn&rsquo;t read minds (although it sure does try). Here&rsquo;s a pattern I&rsquo;ve leaned on.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token comment">// AGENT NOTES:</span>
<span class="token comment">// - This service must remain synchronous; the upstream caller cannot</span>
<span class="token comment">//   be made async without a contract change.</span>
<span class="token comment">// - The string "PRIME" is required by the third-party integration. </span>
<span class="token comment">//   I don't like it but they won't change it. Do not "improve" it </span>
<span class="token comment">//   to PersonType.Prime.ToString().</span>
<span class="token comment">// - Cancellation tokens must be forwarded; do NOT pass `default`.</span>
</code></pre><p>When the agent has clear context like this, it really delivers. Below is the agent producing two new health check files plus a registration update in <code>Program.cs</code>, all from one prompt asking it to mirror an existing pattern. The result is a careful refactoring with the right substitutions swapped in. That&rsquo;s exactly the kind of work I&rsquo;m happy to hand off.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/gh-copilot-agent.png?sfvrsn=a2b5eefe_2" alt="" /><br /><span style="font-size:14px;">The agent panel and one of the two generated files. Three files changed, zero hand-written by me. (My team is thankful for the latter.)</span></p><p>This sounds like babysitting, and it is. Welcome to AI-driven development, my friends. The 10 seconds of setup saves me from a 10-minute cleanup later. When the prep is solid, the result is solid.</p><p>For more information on <a target="_blank" href="https://learn.microsoft.com/en-us/visualstudio/ide/copilot-chat-context?view=visualstudio#use-custom-instructions">custom instructions, check out the Microsoft documentation</a>.</p><h2 id="test-explorer-isnt-frustrating">Test Explorer Isn&rsquo;t Frustrating</h2><p>In my experience, the Test Explorer seems to be much improved in Visual Studio 2026. If I had to sum it up in previous versions, I&rsquo;d say &ldquo;it&rsquo;s fine&rdquo; &hellip; after all, do we ever get jazzed about tests? In previous releases, I kept saying &ldquo;it&rsquo;s fine&rdquo; until I had a few thousand tests, at which point it started getting opinionated about which tests to run.</p><p>The new version is faster, the filtering predictably works, and it doesn&rsquo;t lose track of which tests are running when you switch branches mid-execution. Try it yourself. Run your full test suite and watch the Test Explorer not embarrass itself. It&rsquo;s a small joy.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/test-explorer.png?sfvrsn=222a8ba_2" alt="" /><br /><span style="font-size:14px;">One of those things you don't notice until it stops getting in your way.</span></p><h2 id="better-git-integration">Better Git Integration</h2><p>Visual Studio&rsquo;s Git tooling has historically been one of the IDE&rsquo;s more underwhelming features. Until recently, my workflow was:</p><ol><li>Make code changes in Visual Studio.</li><li>Run Git commands in my terminal (or a third-party tool like GitHub Desktop)</li><li>Ignore the IDE&rsquo;s Git panel entirely.</li></ol><p>With VS 2026, I&rsquo;m finally doing real Git work inside the tool. The new staging interface is better. The conflict resolution UI is also better than it used to be. The &ldquo;please show me an actual diff and not a vague summary&rdquo; feature is good.</p><p>Now, it&rsquo;s no GitKraken and it doesn&rsquo;t try to be. But it&rsquo;s good enough that I no longer switch over to a terminal for routine operations, and that works for me.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/git-conflict.png?sfvrsn=62f8884a_2" alt="" /><br /><span style="font-size:14px;">Three-way merge view, in the IDE I'm already in. Rejoice!</span></p><h2 id="hot-reload-that-reloads-more-often">Hot Reload That Reloads More Often</h2><p>Hot Reload has been one of those features that worked on Friday afternoons if I said &ldquo;please work&rdquo; (especially for ASP.NET Core). Any other time, I&rsquo;d change a method body, save, watch the IDE think very hard about it, and then get: <em>&ldquo;Hot Reload was unable to apply your changes. Restart the application?&rdquo;</em></p><p>The &ldquo;hot&rdquo; in &ldquo;Hot Reload&rdquo; was <a target="_blank" href="https://www.reddit.com/r/dotnet/comments/1my662s/will_microsoft_ever_fix_hot_reload_in_net/">stretching the truth</a> at times (with the understanding that getting it right <a target="_blank" href="https://www.reddit.com/r/dotnet/comments/1my662s/comment/naac88s/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">isn&rsquo;t as simple in .NET</a> as it is for languages like JavaScript).</p><p>With VS 2026, I now reach for it instinctively instead of cynically. Method-body edits in controllers, services and Razor pages just apply. You change a string in a controller, hit Save and see the change. You know, the way it was supposed to work in 2021. The way it kind of worked in 2023. The way it now actually works in 2026.</p><p>When Hot Reload can&rsquo;t apply a change (because the change requires a metadata update or you&rsquo;ve added a field or whatever the actual reason is), the IDE now tells me clearly why instead of vaguely throwing up its hands. That alone goes a long way. I know whether to keep iterating or to just restart the host.</p><h3 id="debugger-improvements-especially-with-async-traces">Debugger Improvements (Especially with Async Traces)</h3><p>Have you ever stared at an async stack trace and felt your soul leave your body? Just me?</p><p>Historically, when an exception bubbled up from somewhere deep in an <code>async</code>/<code>await</code> chain, the stack trace was a wall of <code>MoveNext</code> calls and state machine internals. It was tremendously useful if you wanted to learn about the C# compiler, but not great if you wanted to know which of your methods actually threw.</p><p>Things are looking better in Visual Studio 2026. The debugger now stitches together async call chains into something that reads like the call graph you actually wrote. The <code>MoveNext</code> noise is collapsed by default. The original calling method is shown as the parent frame and not buried six levels down. When an exception is thrown inside a <code>Task.WhenAll</code>, you can see which task threw without reverse-engineering the parallel structure.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/debugger.png?sfvrsn=53980e30_2" alt="" /><br /><span style="font-size:14px;">An async exception, with the actual call chain visible. No <code>MoveNext</code> translation required.</span></p><p>On top of that, conditional breakpoints feel faster to evaluate, and the tracepoints (where you can log a message without breaking) is a one-click affair instead of a buried right-click option.</p><p>Small wins. But debugging is the thing I do every day, and small wins on the thing you do every day add up fast.</p><h2 id="the-verdict">The Verdict</h2><p>Look, the Copilot stuff is genuinely impressive. The agent demos at the keynote were the kind of thing that makes you want to fire up the installer immediately. And after six months, I&rsquo;ll admit that I use it every day.</p><p>But Visual Studio 2026 is a genuine step forward for the boring reasons. The performance is real. The Test Explorer doesn&rsquo;t stress me out. The debugger provides stack traces a human can read. The agent mode handles the kind of work I&rsquo;d previously do by hand. Those are the wins that show up in my daily work, not the ones that show up in keynote demos.</p><p>If you&rsquo;re on Visual Studio 2022 and wondering whether to upgrade: yes. Ignore the marketing, and let the unglamorous improvements do their job. Six months later, you won&rsquo;t be excited about Visual Studio 2026 &hellip; and that&rsquo;s exactly the point. You&rsquo;ll just be working faster.</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">Extension Properties: C# 14&rsquo;s Game-Changing Feature for Cleaner Code</h4></div><div class="col-8"><p class="u-fs16 u-mb0">This post introduces <a target="_blank" href="https://www.telerik.com/blogs/extension-properties-csharp-14-game-changing-feature-cleaner-code">extension properties</a>, a new feature of C# 14 that allows you to use properties in your extension methods.</p></div></div></aside><img src="https://feeds.telerik.com/link/23050/17354316.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:54ada40d-ff98-496d-94ae-1dbc6613c6e0</id>
    <title type="text">Advanced Form Validation in Blazor 10</title>
    <summary type="text">These new validation features available in Blazor as of .NET 10 help manage forms with nested objects and lists, plus perform complex and cross-field validations.</summary>
    <published>2026-06-03T13:34:16Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Héctor Pérez </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17353268/advanced-form-validation-blazor-10"/>
    <content type="text"><![CDATA[<p><span class="featured">These new validation features available in Blazor as of .NET 10 help manage forms with nested objects and lists, plus perform complex and cross-field validations.</span></p><p>Form validation is essential when working with data in Blazor applications. Before .NET 10, if you wanted to perform validation of nested objects, you had to take a series of additional steps that could add complexity to your projects. In this article, we will examine the improvements and new features in .NET 10 to solve this problem through a practical exercise. Let&rsquo;s go!</p><h2 id="understanding-the-problem-of-nested-validations-before-.net-10">Understanding the Problem of Nested Validations Before .NET 10</h2><p>To see the problem when working with nested objects and their validation, let&rsquo;s create a sample Blazor project. For this demonstration, I am working with a project using the <strong>Blazor Web App</strong> template configured with <strong>Interactive Server</strong> rendering <strong>globally</strong>.</p><p>Immediately after creating the project, perform the <a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/getting-started/web-app">installation and configuration steps for the Progress Telerik UI for Blazor controls</a>, as we will use them to quickly build modern interfaces.</p><h3 id="creating-models-in-the-application">Creating Models in the Application</h3><p>Suppose we are building an attendee registration application for a technical event. As a first step, I have defined a class <code>EventRegistration</code> with the following structure:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">EventRegistration</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> Attendee Attendee <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> BillingAddress BillingAddress <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> List<span class="token operator">&lt;</span>SessionSelection<span class="token operator">&gt;</span> Sessions <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token punctuation">[</span><span class="token function">Range</span><span class="token punctuation">(</span><span class="token keyword">typeof</span><span class="token punctuation">(</span><span class="token keyword">bool</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">"true"</span><span class="token punctuation">,</span> <span class="token string">"true"</span><span class="token punctuation">,</span> ErrorMessage <span class="token operator">=</span> <span class="token string">"You must accept the terms and conditions."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">bool</span> AcceptTerms <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The model above has some nested objects, both single objects such as <code>Attendee</code> and a list of type <code>SessionSelection</code>. Likewise, there is a validation on the property <code>AcceptTerms</code> of type <code>Range</code>, which verifies that the value is <code>true</code>.</p><p>The model of a <code>Attendee</code> looks as follows:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Attendee</span>
<span class="token punctuation">{</span>
    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Full name is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token punctuation">[</span><span class="token function">StringLength</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">,</span> ErrorMessage <span class="token operator">=</span> <span class="token string">"Name cannot exceed 100 characters."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> FullName <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Email is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token punctuation">[</span><span class="token function">EmailAddress</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Invalid email format."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Email <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Phone number is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token punctuation">[</span><span class="token function">Phone</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Invalid phone number format."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Phone <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Company name is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Company <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Job title is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> JobTitle <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The <code>Attendee</code> model has more validation annotations per property, which allow validating each attendee&rsquo;s information. The project also includes the <code>BillingAddress</code> model to store address information, also featuring validation attributes:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">BillingAddress</span>
<span class="token punctuation">{</span>
    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Street address is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Street <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"City is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> City <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"State/Province is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> State <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Postal code is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> PostalCode <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Country is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Country <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Finally, the <code>SessionSelection</code> model defines the structure of each session selected by an attendee:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SessionSelection</span>
<span class="token punctuation">{</span>
    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Session name is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> SessionName <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">Required</span><span class="token punctuation">(</span>ErrorMessage <span class="token operator">=</span> <span class="token string">"Preference level is required."</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Preference <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The complexity among the previous models will allow us to test the new validation features included in .NET 10.</p><h3 id="initializing-project-data-using-a-session-catalog-service">Initializing Project Data Using a Session Catalog Service</h3><p>To display mock data in the application, let&rsquo;s create a <code>SessionCatalog</code> service, where we&rsquo;ll define a list of sessions:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SessionCatalog</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> List<span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token operator">&gt;</span> <span class="token function">GetAvailableSessions</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
    <span class="token punctuation">[</span>
        <span class="token string">"Keynote: The Future of .NET"</span><span class="token punctuation">,</span>
        <span class="token string">"Workshop: Blazor Advanced Patterns"</span><span class="token punctuation">,</span>
        <span class="token string">"Talk: SignalR Real-Time Communication"</span><span class="token punctuation">,</span>
        <span class="token string">"Workshop: Minimal APIs Deep Dive"</span><span class="token punctuation">,</span>
        <span class="token string">"Talk: Cloud-Native .NET Applications"</span><span class="token punctuation">,</span>
        <span class="token string">"Workshop: AI Integration with .NET"</span>
    <span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> List<span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token operator">&gt;</span> <span class="token function">GetPreferenceLevels</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
    <span class="token punctuation">[</span>
        <span class="token string">"High"</span><span class="token punctuation">,</span>
        <span class="token string">"Medium"</span><span class="token punctuation">,</span>
        <span class="token string">"Low"</span>
    <span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>We will register this service in <code>Program.cs</code> as follows:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddSingleton<span class="token punctuation">&lt;</span>SessionCatalog<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h3 id="creating-the-graphical-interface">Creating the Graphical Interface</h3><p>To perform various checks, let&rsquo;s create a graphical interface using the <code>TelerikForm</code> component. This will enable generating and customizing model-based forms and with many configuration parameters, making it a powerful and useful component for our example. The resulting code looks like this:</p><pre class=" language-xml"><code class="prism  language-xml">@page "/registration"
@rendermode InteractiveServer
@using FormValidationNET10.Models
@using FormValidationNET10.Services
@inject SessionCatalog SessionCatalog

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PageTitle</span><span class="token punctuation">&gt;</span></span>Event Registration<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>PageTitle</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mb-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Event Registration<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikForm</span> <span class="token attr-name">EditContext</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>editContext<span class="token punctuation">"</span></span>
             <span class="token attr-name">OnValidSubmit</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>HandleValidSubmit<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormValidation</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>DataAnnotationsValidator</span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikValidationSummary</span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>FormValidation</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItems</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItem</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Attendee.FullName<span class="token punctuation">"</span></span> <span class="token attr-name">LabelText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Full Name<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItem</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Attendee.Email<span class="token punctuation">"</span></span> <span class="token attr-name">LabelText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Email<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItem</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Attendee.Phone<span class="token punctuation">"</span></span> <span class="token attr-name">LabelText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Phone<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItem</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Attendee.Company<span class="token punctuation">"</span></span> <span class="token attr-name">LabelText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Company<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItem</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Attendee.JobTitle<span class="token punctuation">"</span></span> <span class="token attr-name">LabelText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Job Title<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItem</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>BillingAddress.Street<span class="token punctuation">"</span></span> <span class="token attr-name">LabelText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Street<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItem</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>BillingAddress.City<span class="token punctuation">"</span></span> <span class="token attr-name">LabelText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>City<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItem</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>BillingAddress.State<span class="token punctuation">"</span></span> <span class="token attr-name">LabelText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>State / Province<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItem</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>BillingAddress.PostalCode<span class="token punctuation">"</span></span> <span class="token attr-name">LabelText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Postal Code<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItem</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>BillingAddress.Country<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Template</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>country-editor<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>k-label k-form-label<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Country<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>k-form-field-wrap<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikDropDownList</span> <span class="token attr-name">@bind-Value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>registration.BillingAddress.Country<span class="token punctuation">"</span></span>
                                         <span class="token attr-name">Data</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@countries<span class="token punctuation">"</span></span>
                                         <span class="token attr-name">DefaultText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Select a country...<span class="token punctuation">"</span></span>
                                         <span class="token attr-name">Id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>country-editor<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikValidationMessage</span> <span class="token attr-name">For</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(() =&gt; registration.BillingAddress.Country)<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Template</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>FormItem</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItem</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>AcceptTerms<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Template</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>k-form-field-wrap<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikCheckBox</span> <span class="token attr-name">@bind-Value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>registration.AcceptTerms<span class="token punctuation">"</span></span> <span class="token attr-name">Id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>acceptTerms<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>acceptTerms<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ms-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>I accept the terms and conditions<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikValidationMessage</span> <span class="token attr-name">For</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(() =&gt; registration.AcceptTerms)<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Template</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>FormItem</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>FormItems</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormItemsTemplate</span> <span class="token attr-name">Context</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>formContext<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        @{
            var items = formContext.Items.OfType<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>IFormItem</span><span class="token punctuation">&gt;</span></span>().ToList();
        }

        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card shadow-sm mb-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-header bg-primary text-white<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h5</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mb-0<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Attendee Information<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h5</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-body<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>row g-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        &lt;TelerikFormItemRenderer Item="@(items.First(x =&gt; x.Field == "Attendee.FullName"))" /&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        &lt;TelerikFormItemRenderer Item="@(items.First(x =&gt; x.Field == "Attendee.Email"))" /&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        &lt;TelerikFormItemRenderer Item="@(items.First(x =&gt; x.Field == "Attendee.Phone"))" /&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        &lt;TelerikFormItemRenderer Item="@(items.First(x =&gt; x.Field == "Attendee.Company"))" /&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        &lt;TelerikFormItemRenderer Item="@(items.First(x =&gt; x.Field == "Attendee.JobTitle"))" /&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card shadow-sm mb-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-header bg-primary text-white<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h5</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mb-0<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Billing Address<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h5</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-body<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>row g-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-12<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        &lt;TelerikFormItemRenderer Item="@(items.First(x =&gt; x.Field == "BillingAddress.Street"))" /&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        &lt;TelerikFormItemRenderer Item="@(items.First(x =&gt; x.Field == "BillingAddress.City"))" /&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        &lt;TelerikFormItemRenderer Item="@(items.First(x =&gt; x.Field == "BillingAddress.State"))" /&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        &lt;TelerikFormItemRenderer Item="@(items.First(x =&gt; x.Field == "BillingAddress.PostalCode"))" /&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-12<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        &lt;TelerikFormItemRenderer Item="@(items.First(x =&gt; x.Field == "BillingAddress.Country"))" /&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card shadow-sm mb-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-header bg-primary text-white d-flex justify-content-between align-items-center<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h5</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mb-0<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Session Selection<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h5</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikButton</span> <span class="token attr-name">OnClick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@AddSession<span class="token punctuation">"</span></span>
                               <span class="token attr-name">ThemeColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ThemeConstants.Button.ThemeColor.Light<span class="token punctuation">"</span></span>
                               <span class="token attr-name">ButtonType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ButtonType.Button<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                    + Add Session
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikButton</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-body<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                @if (registration.Sessions.Count == 0)
                {
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>alert alert-info mb-0<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        No sessions added yet. Click "Add Session" to select the sessions you'd like to attend.
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                }
                else
                {
                    @for (int i = 0; i &lt; registration.Sessions.Count; i++)
                    {
                        var index = i;
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>row g-3 mb-3 align-items-end<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-5<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>k-label k-form-label<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Session<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>
                                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikDropDownList</span> <span class="token attr-name">@bind-Value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>registration.Sessions[index].SessionName<span class="token punctuation">"</span></span>
                                                     <span class="token attr-name">Data</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@availableSessions<span class="token punctuation">"</span></span>
                                                     <span class="token attr-name">DefaultText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Select a session...<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikValidationMessage</span> <span class="token attr-name">For</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(() =&gt; registration.Sessions[index].SessionName)<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>k-label k-form-label<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Preference<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>
                                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikDropDownList</span> <span class="token attr-name">@bind-Value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>registration.Sessions[index].Preference<span class="token punctuation">"</span></span>
                                                     <span class="token attr-name">Data</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@preferenceLevels<span class="token punctuation">"</span></span>
                                                     <span class="token attr-name">DefaultText</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Select preference...<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikValidationMessage</span> <span class="token attr-name">For</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(() =&gt; registration.Sessions[index].Preference)<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikButton</span> <span class="token attr-name">OnClick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(() =&gt; RemoveSession(index))<span class="token punctuation">"</span></span>
                                               <span class="token attr-name">ThemeColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ThemeConstants.Button.ThemeColor.Error<span class="token punctuation">"</span></span>
                                               <span class="token attr-name">ButtonType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ButtonType.Button<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                                    Remove
                                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikButton</span><span class="token punctuation">&gt;</span></span>
                            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                    }
                }
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikValidationMessage</span> <span class="token attr-name">For</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(() =&gt; registration.Sessions)<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card shadow-sm mb-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-body<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                &lt;TelerikFormItemRenderer Item="@(items.First(x =&gt; x.Field == "AcceptTerms"))" /&gt;
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>FormItemsTemplate</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>FormButtons</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikButton</span> <span class="token attr-name">ButtonType</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ButtonType.Submit<span class="token punctuation">"</span></span>
                       <span class="token attr-name">ThemeColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ThemeConstants.Button.ThemeColor.Primary<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            Submit Registration
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikButton</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>FormButtons</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikForm</span><span class="token punctuation">&gt;</span></span>

@if (isSubmitted)
{
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>alert alert-success mt-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h5</span><span class="token punctuation">&gt;</span></span>Registration Successful!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h5</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>Thank you, <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>@registration.Attendee.FullName<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span>! Your registration for @registration.Sessions.Count session(s) has been submitted.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
}

@code {
    private EventRegistration registration = new();
    private EditContext editContext = default!;
    private List<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>string</span><span class="token punctuation">&gt;</span></span> availableSessions = [];
    private List<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>string</span><span class="token punctuation">&gt;</span></span> preferenceLevels = [];
    private List<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>string</span><span class="token punctuation">&gt;</span></span> countries = ["United States", "Canada", "Mexico", "United Kingdom", "Germany", "France", "Spain"];
    private bool isSubmitted;

    protected override void OnInitialized()
    {
        editContext = new EditContext(registration);
        
        availableSessions = SessionCatalog.GetAvailableSessions();
        preferenceLevels = SessionCatalog.GetPreferenceLevels();
    }

    private void AddSession()
    {
        registration.Sessions.Add(new SessionSelection());
    }

    private void RemoveSession(int index)
    {
        registration.Sessions.RemoveAt(index);
    }

    private void HandleValidSubmit(EditContext context)
    {
        isSubmitted = true;
    }
}
</code></pre><h3 id="validating-data-before-.net-10">Validating Data Before .NET 10</h3><p>Once we have all the pieces of the project, let&rsquo;s run it. Let&rsquo;s do a test by trying to submit the empty form:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/form-post-nested-objects-binding-before-net10.gif?sfvrsn=8b4e6fd5_2" alt="Form post showing nested object binding failures before .NET 10" /></p><p>In the previous image, you can see that when submitting the form the only error shown is the one indicating that the terms and conditions have not been accepted. However, although all models implement validations, no others are shown.</p><p>The reason behind this is that <code>DataAnnotationsValidator</code> in Blazor does not perform validation on nested objects or collections. This has been a known limitation in Blazor since its beginnings, which was addressed using custom recursive validators. Let&rsquo;s see how .NET 10 helps us solve this problem.</p><h2 id="validating-data-thanks-to-the-new-features-in-.net-10">Validating Data Thanks to the New Features in .NET 10</h2><p>To perform validation of nested objects and collections, the first step you must take is to add in <code>Program.cs</code> a call to the method <code>AddValidation()</code>:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddValidation</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The method <code>AddValidation()</code> registers the services required for the validation system to work.</p><p>The next step is to open the main model that contains the hierarchy of submodels and add the attribute <code>[ValidatableType]</code> to the class. In our example, the class is <code>EventRegistration</code>, which will look as follows after the modification:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>ValidatableType<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">EventRegistration</span>
<span class="token punctuation">{</span>
   <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token punctuation">}</span>
</code></pre><p>The attribute <code>[ValidatableType]</code> tells the .NET source generator to generate validation logic for the class. Behind the scenes the following steps will be performed:</p><ol><li>All properties of the decorated class are inspected.</li><li>Nested objects are detected and the necessary code to recursively validate their validation annotations is generated.</li><li>Collections are detected and code is generated to iterate through each element, validating their validation annotations.</li></ol><p>After applying the previous changes, you will see that now, when submitting the form with empty fields, all errors are displayed correctly:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/form-validating-nested-objects-net10.gif?sfvrsn=60d57217_2" alt="Web form displaying nested object fields with validation" /></p><p>With these changes, we have correctly enabled full validation of nested objects and collections.</p><h3 id="implementing-cross-field-validation-using-ivalidatableobject">Implementing Cross-field Validation Using IValidatableObject</h3><p>So far we have been able to apply validation rules at the level of individual properties. However, it is common in complex applications to have rules that involve multiple properties or complex business logic. For these cases, .NET provides the interface <code>IValidatableObject</code>, which in version 10 integrates seamlessly with <code>[ValidatableType]</code>.</p><p>Let&rsquo;s perform a check of this type assuming this pair of business rules:</p><ul><li>The user must select at least one session.</li><li>Duplicate sessions are not allowed.</li></ul><p>The rules depend on the state of the collection <code>Sessions</code> as a whole. To make it work correctly, let&rsquo;s modify the class <code>EventRegistration</code> by adding the interface:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>ValidatableType<span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">EventRegistration</span> <span class="token punctuation">:</span> IValidatableObject
<span class="token punctuation">{</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

    <span class="token keyword">public</span> IEnumerable<span class="token operator">&lt;</span>ValidationResult<span class="token operator">&gt;</span> <span class="token function">Validate</span><span class="token punctuation">(</span>ValidationContext validationContext<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
    <span class="token comment">// 1.</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>Sessions<span class="token punctuation">.</span>Count <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">yield</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ValidationResult</span><span class="token punctuation">(</span>
                <span class="token string">"You must select at least one session."</span><span class="token punctuation">,</span>
                <span class="token punctuation">[</span><span class="token function">nameof</span><span class="token punctuation">(</span>Sessions<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        
        <span class="token comment">// 2.</span>
        <span class="token keyword">var</span> duplicates <span class="token operator">=</span> Sessions
            <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>s <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token operator">!</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrEmpty</span><span class="token punctuation">(</span>s<span class="token punctuation">.</span>SessionName<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">GroupBy</span><span class="token punctuation">(</span>s <span class="token operator">=</span><span class="token operator">&gt;</span> s<span class="token punctuation">.</span>SessionName<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>g <span class="token operator">=</span><span class="token operator">&gt;</span> g<span class="token punctuation">.</span><span class="token function">Count</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&gt;</span> <span class="token number">1</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>g <span class="token operator">=</span><span class="token operator">&gt;</span> g<span class="token punctuation">.</span>Key<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>duplicates<span class="token punctuation">.</span>Count <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">yield</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ValidationResult</span><span class="token punctuation">(</span>
                $<span class="token string">"Duplicate sessions are not allowed: {string.Join("</span><span class="token punctuation">,</span> <span class="token string">", duplicates)}"</span><span class="token punctuation">,</span>
                <span class="token punctuation">[</span><span class="token function">nameof</span><span class="token punctuation">(</span>Sessions<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code above:</p><ol><li>We validate that at least one session is selected.</li><li>We look for duplicate sessions.</li></ol><p>In case of any error, we return an <code>ValidationResult</code> indicating the error. When running the application, we see the following result if no session is selected:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/validation-no-session-selected.png?sfvrsn=8e250e63_2" alt="Validation error shown for no session selected" /></p><p>Now, when duplicate sessions are selected, the following error is shown:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/validation-warning-duplicate-sessions.png?sfvrsn=38a76043_2" alt="Validation warning indicating duplicate sessions selected" /></p><p>With this, we have created cross-field validation correctly using Blazor.</p><h3 id="improving-validations-using-fieldcssclassprovider">Improving Validations Using FieldCssClassProvider</h3><p>At this point we have a project that correctly validates the form information, thanks to the native capabilities of <code>TelerikForm</code>. We can go one step further and modify the project to add visual indicators at the section level that communicate to the user which sections have errors. We can do this thanks to the method <code>EditContext.IsValid(fieldIdentifier)</code> introduced in .NET 10, which allows directly checking whether a field is valid.</p><p>To use it, the first thing we will do is create a class that inherits from <code>FieldCssClassProvider</code>. For the demonstration I will create a class called <code>RegistrationFieldClassProvider</code> at the root level of the project that looks as follows:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">RegistrationFieldClassProvider</span> <span class="token punctuation">:</span> FieldCssClassProvider
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">override</span> <span class="token keyword">string</span> <span class="token function">GetFieldCssClass</span><span class="token punctuation">(</span>EditContext editContext<span class="token punctuation">,</span> <span class="token keyword">in</span> FieldIdentifier fieldIdentifier<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>editContext<span class="token punctuation">.</span><span class="token function">IsModified</span><span class="token punctuation">(</span>fieldIdentifier<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>

        <span class="token keyword">var</span> isValid <span class="token operator">=</span> editContext<span class="token punctuation">.</span><span class="token function">IsValid</span><span class="token punctuation">(</span>fieldIdentifier<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> isValid <span class="token operator">?</span> <span class="token string">"is-valid"</span> <span class="token punctuation">:</span> <span class="token string">"is-invalid"</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code above, it checks whether the field has been modified. Next <code>IsValid()</code> is used inside the method <code>GetFieldCssClass</code> to check if the field has associated validation messages. Finally, Bootstrap classes are returned that will provide feedback to the user about the validity of the sections.</p><p>To achieve this, we will make the card headers change color according to the state of their fields by modifying <code>Registration.razor</code> as follows:</p><ol><li>Add the event <code>OnValidSubmit</code> to <code>TelerikForm</code>:</li></ol><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikForm</span> <span class="token attr-name">EditContext</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>editContext<span class="token punctuation">"</span></span>
             <span class="token attr-name">OnValidSubmit</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>HandleValidSubmit<span class="token punctuation">"</span></span>
             <span class="token attr-name">OnInvalidSubmit</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>HandleInvalidSubmit<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
</code></pre><ol start="2"><li>Find the <code>h5</code> elements that reference <strong>Attendee Information</strong> and <strong>Billing Address</strong>, and modify their container <code>div</code> as follows:</li></ol><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-header @GetAttendeeHeaderClass()<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h5</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mb-0<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Attendee Information<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h5</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
...
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-header @GetBillingHeaderClass()<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h5</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mb-0<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Billing Address<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h5</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre><ol start="3"><li>Modify the code section by adding the following changes:</li></ol><pre class=" language-csharp"><code class="prism  language-csharp">@code <span class="token punctuation">{</span>
   <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    <span class="token keyword">private</span> <span class="token keyword">bool</span> hasValidated<span class="token punctuation">;</span>

    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">void</span> <span class="token function">OnInitialized</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        editContext <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">EditContext</span><span class="token punctuation">(</span>registration<span class="token punctuation">)</span><span class="token punctuation">;</span>
                
        editContext<span class="token punctuation">.</span><span class="token function">SetFieldCssClassProvider</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RegistrationFieldClassProvider</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        editContext<span class="token punctuation">.</span>OnValidationStateChanged <span class="token operator">+</span><span class="token operator">=</span> <span class="token punctuation">(</span>_<span class="token punctuation">,</span> _<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token function">StateHasChanged</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>        

        availableSessions <span class="token operator">=</span> SessionCatalog<span class="token punctuation">.</span><span class="token function">GetAvailableSessions</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        preferenceLevels <span class="token operator">=</span> SessionCatalog<span class="token punctuation">.</span><span class="token function">GetPreferenceLevels</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
    
    <span class="token keyword">private</span> <span class="token keyword">bool</span> <span class="token generic-method function">IsFieldValid<span class="token punctuation">&lt;</span>T<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>Expression<span class="token operator">&lt;</span>Func<span class="token operator">&lt;</span>T<span class="token operator">&gt;</span><span class="token operator">&gt;</span> accessor<span class="token punctuation">)</span>
       <span class="token operator">=</span><span class="token operator">&gt;</span> editContext<span class="token punctuation">.</span><span class="token function">IsValid</span><span class="token punctuation">(</span>FieldIdentifier<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span>accessor<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">string</span> <span class="token function">GetAttendeeHeaderClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>hasValidated<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token string">"bg-primary text-white"</span><span class="token punctuation">;</span>

        <span class="token keyword">bool</span> allValid <span class="token operator">=</span> <span class="token function">IsFieldValid</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> registration<span class="token punctuation">.</span>Attendee<span class="token punctuation">.</span>FullName<span class="token punctuation">)</span>
            <span class="token operator">&amp;&amp;</span> <span class="token function">IsFieldValid</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> registration<span class="token punctuation">.</span>Attendee<span class="token punctuation">.</span>Email<span class="token punctuation">)</span>
            <span class="token operator">&amp;&amp;</span> <span class="token function">IsFieldValid</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> registration<span class="token punctuation">.</span>Attendee<span class="token punctuation">.</span>Phone<span class="token punctuation">)</span>
            <span class="token operator">&amp;&amp;</span> <span class="token function">IsFieldValid</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> registration<span class="token punctuation">.</span>Attendee<span class="token punctuation">.</span>Company<span class="token punctuation">)</span>
            <span class="token operator">&amp;&amp;</span> <span class="token function">IsFieldValid</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> registration<span class="token punctuation">.</span>Attendee<span class="token punctuation">.</span>JobTitle<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> allValid <span class="token operator">?</span> <span class="token string">"bg-success text-white"</span> <span class="token punctuation">:</span> <span class="token string">"bg-danger text-white"</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">string</span> <span class="token function">GetBillingHeaderClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>hasValidated<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token string">"bg-primary text-white"</span><span class="token punctuation">;</span>

        <span class="token keyword">bool</span> allValid <span class="token operator">=</span> <span class="token function">IsFieldValid</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> registration<span class="token punctuation">.</span>BillingAddress<span class="token punctuation">.</span>Street<span class="token punctuation">)</span>
            <span class="token operator">&amp;&amp;</span> <span class="token function">IsFieldValid</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> registration<span class="token punctuation">.</span>BillingAddress<span class="token punctuation">.</span>City<span class="token punctuation">)</span>
            <span class="token operator">&amp;&amp;</span> <span class="token function">IsFieldValid</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> registration<span class="token punctuation">.</span>BillingAddress<span class="token punctuation">.</span>State<span class="token punctuation">)</span>
            <span class="token operator">&amp;&amp;</span> <span class="token function">IsFieldValid</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> registration<span class="token punctuation">.</span>BillingAddress<span class="token punctuation">.</span>PostalCode<span class="token punctuation">)</span>
            <span class="token operator">&amp;&amp;</span> <span class="token function">IsFieldValid</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> registration<span class="token punctuation">.</span>BillingAddress<span class="token punctuation">.</span>Country<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> allValid <span class="token operator">?</span> <span class="token string">"bg-success text-white"</span> <span class="token punctuation">:</span> <span class="token string">"bg-danger text-white"</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">HandleValidSubmit</span><span class="token punctuation">(</span>EditContext context<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>        
        hasValidated <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>        
        isSubmitted <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
        
    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">HandleInvalidSubmit</span><span class="token punctuation">(</span>EditContext context<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        hasValidated <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code above, I added the field <code>hasValidated</code> to check if the form has been submitted. I also modified <code>OnInitialized</code> by adding the handler for the event <code>OnValidationStateChanged</code>, which allows the UI to update reactively. I also added the helper <code>IsFieldValid&lt;T&gt;</code> and the methods <code>GetAttendeeHeaderClass</code> and <code>GetBillingHeaderClass</code>. This is the result:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/form-section-error-summary.png?sfvrsn=64b57388_2" alt="Form displaying an error summary for entire sections" /></p><p>With this, we have managed to give the user better feedback about the sections they need to complete before proceeding.</p><h2 id="conclusion">Conclusion</h2><p>Throughout this article, we have explored the new features available in .NET 10 that will undoubtedly help you manage forms with nested objects and lists. We&rsquo;ve also looked at how to perform cross-validations and, finally, the process of carrying out more complex validations by checking data in sections.</p><p>Now it&rsquo;s your time to implement these new features in your own projects and provide better feedback to your users.</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">120+ Enterprise-Grade Blazor UI Components</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Accelerate app development with <a target="_blank" href="https://www.telerik.com https://www.telerik.com/blazor-ui">Telerik UI for Blazor</a>&mdash;a complete set of 120+ high-performance, accessible controls, MCP Servers and more. Available with a free 30-day trial.</p></div></div></aside><img src="https://feeds.telerik.com/link/23050/17353268.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:1feea2d1-2fa0-48fa-a5fa-f1d611bdf2d5</id>
    <title type="text">Streaming Server Events with SSE and Blazor</title>
    <summary type="text">Understand the SSE standard, the .NET 10  changes to simplify SSE endpoints and how to add real-time events to your Blazor clients.</summary>
    <published>2026-05-12T13:18:03Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Héctor Pérez </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17339197/streaming-server-events-sse-blazor"/>
    <content type="text"><![CDATA[<p><span class="featured">Understand the SSE standard, the .NET 10 changes to simplify SSE endpoints and how to add real-time events to your Blazor clients.</span></p><p>In the world of web development, there are scenarios where you need to receive real-time information, such as notifications about an event, server metrics or live logs. One possible solution to achieve this is the use of Server-Sent Events (SSE). With the arrival of .NET 10, implementing this type of solution has become much easier, so let&rsquo;s see how to integrate this web standard into your projects.</p><h2 id="what-are-server-sent-events">What Are Server-Sent Events?</h2><p>The <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events">Server-Sent Events (SSE)</a> are not a new technology. Their origins date back to around 2006, and they are a web standard that allows data to be sent to clients continuously using a persistent HTTP connection.</p><p>If you wonder the difference compared to other similar technologies, we can make a comparison:</p><ul><li><p><strong>SSE vs. Polling</strong>: In the case of polling, the direction goes from the client to the server, using the HTTP protocol. We can see it as a client asking the server if there are updates at certain intervals. It involves low implementation complexity.</p></li><li><p><strong>SSE vs. WebSockets</strong>: With WebSockets there is bidirectional communication. It involves high complexity and is ideal for scenarios like video games, chats, etc. It works over the WS protocol.</p></li><li><p><strong>SSE vs. SignalR</strong>: SignalR also establishes bidirectional communication, with medium implementation complexity. It allows the use of binary protocols, which makes it a very good option for scalable enterprise apps. It uses variable protocols.</p></li></ul><p>Analyzing the above, we can conclude that SSE is ideal when the data flow is unidirectional, you need it to work over the HTTP protocol and it must be easy to implement.</p><h2 id="how-does-the-sse-protocol-work">How Does the SSE Protocol Work?</h2><p>The workings behind the scenes of the SSE protocol, in broad terms, are as follows: a server SSE endpoint needs to send an HTTP response of the type <code>Content-Type: text/event-stream</code>. The response looks similar to the following:</p><pre class=" language-json"><code class="prism  language-json">event<span class="token punctuation">:</span> app<span class="token operator">-</span>event
data<span class="token punctuation">:</span> <span class="token punctuation">{</span><span class="token string">"id"</span><span class="token punctuation">:</span><span class="token number">1</span><span class="token punctuation">,</span><span class="token string">"time"</span><span class="token punctuation">:</span><span class="token string">"10:30:45"</span><span class="token punctuation">,</span><span class="token string">"level"</span><span class="token punctuation">:</span><span class="token string">"Info"</span><span class="token punctuation">,</span><span class="token string">"source"</span><span class="token punctuation">:</span><span class="token string">"OrderService"</span><span class="token punctuation">,</span><span class="token string">"message"</span><span class="token punctuation">:</span><span class="token string">"Order #1234 placed successfully"</span><span class="token punctuation">}</span>
</code></pre><p>In the code above, there are some fields we should pay attention to:</p><ul><li><code>event</code>: Specifies the name of the event</li><li><code>data</code>: Is the content of the message</li><li><code>id</code>: Identifier used to perform a reconnection if needed</li></ul><h2 id="whats-new-in-.net-10-for-sse">What&rsquo;s New in .NET 10 for SSE</h2><p>The most important update in .NET 10 for working with SSE is that the ability to return a <code>ServerSentEvents</code> using the API <code>TypedResults.ServerSentEvents</code> has been implemented. This means that the method <code>TypedResults.ServerSentEvents&lt;T&gt;()</code> allows converting any <code>IAsyncEnumerable&lt;T&gt;</code> into a formatted SSE stream without needing to do anything else. Tasks like JSON serialization, HTTP headers, connection closing, etc. are handled automatically.</p><p>To better understand how it works, let&rsquo;s create a project using the SSE standard with ASP.NET 10.</p><h2 id="building-an-sse-project-using-asp.net-10">Building an SSE Project Using ASP.NET 10</h2><p>To practice the theoretical concepts covered so far, we&rsquo;ll create a page that shows a simulation of receiving events from backend services in real time. Using a Progress Telerik UI for <a target="_blank" href="https://www.telerik.com/blazor-ui/grid">Blazor Grid</a>, we&rsquo;ll perform tasks like filtering, grouping and analysis quickly.</p><h3 id="creating-and-configuring-the-project">Creating and Configuring the Project</h3><p>The first thing we&rsquo;ll do is create a project using the <strong>Blazor Web App</strong> template, selecting an <strong>Interactive render mode</strong> of <strong>Server</strong> and <strong>Interactivity location</strong> of <strong>Global</strong>. Next, we&rsquo;ll follow the official installation guide for <a target="_blank" href="https://www.telerik.com/blazor-ui">Telerik UI for Blazor</a> to configure the project and be able to use the Telerik components.</p><h3 id="creating-an-eventbroadcaster">Creating an EventBroadcaster</h3><p>For our project, we&rsquo;ll create an event broadcaster, which in simple terms will be a bus that SSE clients can connect to to consume events, while backend services will use it to publish events. First, we&rsquo;ll create a record that represents a system event:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> SSEDemo<span class="token punctuation">.</span>Services
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> record <span class="token function">AppEvent</span><span class="token punctuation">(</span><span class="token keyword">int</span> Id<span class="token punctuation">,</span> <span class="token keyword">string</span> Time<span class="token punctuation">,</span> <span class="token keyword">string</span> Level<span class="token punctuation">,</span> <span class="token keyword">string</span> Source<span class="token punctuation">,</span> <span class="token keyword">string</span> Message<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Next, we will create the class <code>EventBroadcaster</code> which will have the following definition:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">EventBroadcaster</span>
<span class="token punctuation">{</span>
    <span class="token comment">//1.</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> Channel<span class="token operator">&lt;</span>AppEvent<span class="token operator">&gt;</span> _channel <span class="token operator">=</span> Channel<span class="token punctuation">.</span><span class="token generic-method function">CreateBounded<span class="token punctuation">&lt;</span>AppEvent<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>
    <span class="token keyword">new</span> <span class="token class-name">BoundedChannelOptions</span><span class="token punctuation">(</span><span class="token number">100</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> FullMode <span class="token operator">=</span> BoundedChannelFullMode<span class="token punctuation">.</span>DropOldest <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">int</span> _nextId<span class="token punctuation">;</span>

    <span class="token comment">// 2.</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Publish</span><span class="token punctuation">(</span><span class="token keyword">string</span> level<span class="token punctuation">,</span> <span class="token keyword">string</span> source<span class="token punctuation">,</span> <span class="token keyword">string</span> message<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> id <span class="token operator">=</span> Interlocked<span class="token punctuation">.</span><span class="token function">Increment</span><span class="token punctuation">(</span><span class="token keyword">ref</span> _nextId<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> evt <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AppEvent</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> DateTime<span class="token punctuation">.</span>Now<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"HH:mm:ss"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> level<span class="token punctuation">,</span> source<span class="token punctuation">,</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>
        _channel<span class="token punctuation">.</span>Writer<span class="token punctuation">.</span><span class="token function">TryWrite</span><span class="token punctuation">(</span>evt<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// 3.</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> IAsyncEnumerable<span class="token operator">&lt;</span>AppEvent<span class="token operator">&gt;</span> <span class="token function">Subscribe</span><span class="token punctuation">(</span>
        <span class="token punctuation">[</span>System<span class="token punctuation">.</span>Runtime<span class="token punctuation">.</span>CompilerServices<span class="token punctuation">.</span>EnumeratorCancellation<span class="token punctuation">]</span> CancellationToken ct<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>ct<span class="token punctuation">.</span>IsCancellationRequested<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">bool</span> hasData<span class="token punctuation">;</span>
            <span class="token keyword">try</span>
            <span class="token punctuation">{</span>
                hasData <span class="token operator">=</span> <span class="token keyword">await</span> _channel<span class="token punctuation">.</span>Reader<span class="token punctuation">.</span><span class="token function">WaitToReadAsync</span><span class="token punctuation">(</span>ct<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">OperationCanceledException</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">yield</span> <span class="token keyword">break</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>

            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>hasData<span class="token punctuation">)</span> <span class="token keyword">yield</span> <span class="token keyword">break</span><span class="token punctuation">;</span>

            <span class="token keyword">while</span> <span class="token punctuation">(</span>_channel<span class="token punctuation">.</span>Reader<span class="token punctuation">.</span><span class="token function">TryRead</span><span class="token punctuation">(</span><span class="token keyword">out</span> <span class="token keyword">var</span> evt<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">yield</span> <span class="token keyword">return</span> evt<span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code above, we have the following sections:</p><ol><li><p>Sets up the channel where events will be passed, with a queue limit of 100 messages. <code>FullMode = BoundedChannelFullMode.DropOldest</code> allows that if the queue fills up and a new message arrives, the oldest one is removed to make room for the new message.</p></li><li><p>The method <code>Publish</code> is the one that will be used to emit events. <code>Interlocked.Increment</code> allows generating sequential IDs in a safe manner, while <code>AppEvent</code> packages the received data together with the obtained id. Finally the method <code>TryWrite()</code> attempts to write the event to the channel asynchronously.</p></li><li><p>On the other hand, the method <code>Subscribe</code> returns a continuous stream of asynchronous data through the use of <code>IAsyncEnumerable&lt;AppEvent&gt;</code>. Inside its implementation an infinite loop is created that waits for data to be available in the channel. Once an event enters the channel, it is emitted to the consumer. This will happen until the <code>CancellationToken</code> called <code>ct</code> is canceled.</p></li></ol><h3 id="generating-test-events">Generating Test Events</h3><p>To test the bus defined above, we&rsquo;ll create a service that simulates the activity of multiple services:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">DemoEventGenerator</span><span class="token punctuation">(</span>EventBroadcaster broadcaster<span class="token punctuation">)</span> <span class="token punctuation">:</span> BackgroundService
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">readonly</span> <span class="token keyword">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span> Levels <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">"Info"</span><span class="token punctuation">,</span> <span class="token string">"Warning"</span><span class="token punctuation">,</span> <span class="token string">"Error"</span><span class="token punctuation">,</span> <span class="token string">"Success"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">readonly</span> <span class="token keyword">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span> Sources <span class="token operator">=</span>
        <span class="token punctuation">[</span><span class="token string">"OrderService"</span><span class="token punctuation">,</span> <span class="token string">"PaymentService"</span><span class="token punctuation">,</span> <span class="token string">"InventoryService"</span><span class="token punctuation">,</span> <span class="token string">"AuthService"</span><span class="token punctuation">,</span> <span class="token string">"ShippingService"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">readonly</span> <span class="token keyword">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token punctuation">]</span> Messages <span class="token operator">=</span>
    <span class="token punctuation">[</span>
        <span class="token punctuation">[</span><span class="token string">"Order #{0} placed successfully"</span><span class="token punctuation">,</span> <span class="token string">"New customer registered"</span><span class="token punctuation">,</span> <span class="token string">"Product viewed: SKU-{0}"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">[</span><span class="token string">"High latency detected: {0}ms"</span><span class="token punctuation">,</span> <span class="token string">"Retry attempt #{0}"</span><span class="token punctuation">,</span> <span class="token string">"Queue depth above threshold"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">[</span><span class="token string">"Payment failed for order #{0}"</span><span class="token punctuation">,</span> <span class="token string">"Database timeout after {0}ms"</span><span class="token punctuation">,</span> <span class="token string">"Service unreachable"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
        <span class="token punctuation">[</span><span class="token string">"Deployment completed v2.{0}"</span><span class="token punctuation">,</span> <span class="token string">"Health check passed"</span><span class="token punctuation">,</span> <span class="token string">"Cache refreshed ({0} items)"</span><span class="token punctuation">]</span>
    <span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">async</span> Task <span class="token function">ExecuteAsync</span><span class="token punctuation">(</span>CancellationToken stoppingToken<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> rng <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Random</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>stoppingToken<span class="token punctuation">.</span>IsCancellationRequested<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>            
            <span class="token keyword">await</span> Task<span class="token punctuation">.</span><span class="token function">Delay</span><span class="token punctuation">(</span>rng<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span><span class="token number">1500</span><span class="token punctuation">,</span> <span class="token number">4000</span><span class="token punctuation">)</span><span class="token punctuation">,</span> stoppingToken<span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">var</span> levelIdx <span class="token operator">=</span> rng<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span>Levels<span class="token punctuation">.</span>Length<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">var</span> level <span class="token operator">=</span> Levels<span class="token punctuation">[</span>levelIdx<span class="token punctuation">]</span><span class="token punctuation">;</span>
            <span class="token keyword">var</span> source <span class="token operator">=</span> Sources<span class="token punctuation">[</span>rng<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span>Sources<span class="token punctuation">.</span>Length<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
            <span class="token keyword">var</span> templates <span class="token operator">=</span> Messages<span class="token punctuation">[</span>levelIdx<span class="token punctuation">]</span><span class="token punctuation">;</span>
            <span class="token keyword">var</span> message <span class="token operator">=</span> <span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">Format</span><span class="token punctuation">(</span>templates<span class="token punctuation">[</span>rng<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span>templates<span class="token punctuation">.</span>Length<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span> rng<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">,</span> <span class="token number">9999</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            broadcaster<span class="token punctuation">.</span><span class="token function">Publish</span><span class="token punctuation">(</span>level<span class="token punctuation">,</span> source<span class="token punctuation">,</span> message<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The previous class is a random event generator, taking a random value from the arrays <code>Levels</code>, <code>Sources</code> and <code>Messages</code>. In addition, through the parameter <code>broadcaster</code>, the new event is published to the bus.</p><h3 id="creating-the-sse-endpoint">Creating the SSE Endpoint</h3><p>Now it&rsquo;s time to create the SSE endpoint, which will consume the shared <code>EventBroadcaster</code>, with the purpose of creating the Event Feed so clients can connect to receive events:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">SseEndpoints</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">MapSseEndpoints</span><span class="token punctuation">(</span><span class="token keyword">this</span> WebApplication app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>        
        app<span class="token punctuation">.</span><span class="token function">MapGet</span><span class="token punctuation">(</span><span class="token string">"/sse/events"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>EventBroadcaster broadcaster<span class="token punctuation">,</span> CancellationToken ct<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
            TypedResults<span class="token punctuation">.</span><span class="token function">ServerSentEvents</span><span class="token punctuation">(</span>broadcaster<span class="token punctuation">.</span><span class="token function">Subscribe</span><span class="token punctuation">(</span>ct<span class="token punctuation">)</span><span class="token punctuation">,</span> eventType<span class="token punctuation">:</span> <span class="token string">"app-event"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code above you can notice how <code>EventBroadcaster</code> is injected as a parameter of <code>MapGet</code>. Likewise, <code>TypedResults.ServerSentEvent</code> is executed, which serializes the information and sends the correct SSE format to clients.</p><h3 id="registering-services-in-program.cs">Registering Services in Program.cs</h3><p>For everything to work as expected, we must register in <code>Program.cs</code> the different instances that will interact in the Blazor application. This means creating a singleton instance of <code>EventBroadcaster</code>, so that all systems have access to the same bus. Similarly, we will register <code>DemoEventGenerator</code> as a background service, through the method <code>AddHostedService</code>. This will allow simulated events to be generated in the background continuously:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddSingleton<span class="token punctuation">&lt;</span>EventBroadcaster<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddHostedService<span class="token punctuation">&lt;</span>DemoEventGenerator<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>

app<span class="token punctuation">.</span><span class="token function">MapSseEndpoints</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

app<span class="token punctuation">.</span><span class="token function">Run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>In the previous code, in addition to registering the services, <code>MapSseEndpoints</code> is invoked to enable SSE communication in the project.</p><h3 id="creating-the-blazor-application">Creating the Blazor Application</h3><p>Once we have the application infrastructure ready, it&rsquo;s time to move on to the UI part. At this point, let&rsquo;s start by creating a JavaScript client, because the standard requires using the browser&rsquo;s <code>EventSource</code> API. Since the project is configured as <code>InteractiveServer</code>, we cannot add inline <code>script</code> tags. To work around this, I will add a new file inside the <code>wwwroot/js</code> folder called <code>sse-demos.js</code>:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token comment">//1.</span>
<span class="token keyword">let</span> eventFeedSource <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>

<span class="token number">2</span><span class="token punctuation">.</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">start</span><span class="token punctuation">(</span>dotNetRef<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 3.</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>eventFeedSource<span class="token punctuation">)</span> eventFeedSource<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">//4.</span>
    <span class="token keyword">const</span> src <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">EventSource</span><span class="token punctuation">(</span><span class="token string">'/sse/events'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// 5.</span>
    src<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'app-event'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        dotNetRef<span class="token punctuation">.</span><span class="token function">invokeMethodAsync</span><span class="token punctuation">(</span><span class="token string">'OnEventReceived'</span><span class="token punctuation">,</span> JSON<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// 6.</span>
    src<span class="token punctuation">.</span><span class="token function-variable function">onopen</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> dotNetRef<span class="token punctuation">.</span><span class="token function">invokeMethodAsync</span><span class="token punctuation">(</span><span class="token string">'OnConnectionChanged'</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
    src<span class="token punctuation">.</span><span class="token function-variable function">onerror</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> dotNetRef<span class="token punctuation">.</span><span class="token function">invokeMethodAsync</span><span class="token punctuation">(</span><span class="token string">'OnConnectionChanged'</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>

    eventFeedSource <span class="token operator">=</span> src<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">//7.</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>eventFeedSource<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        eventFeedSource<span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        eventFeedSource <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The previous code does the following:</p><ol><li>A variable <code>eventFeedSource</code> is created to keep information about whether there is an active connection with the server.</li><li>A function named <code>start</code> should be called from C# code when you want to start receiving information&hellip;</li><li>It checks whether there is an open connection, in which case it is closed.</li><li>It tells the browser to connect to <code>/sse/events</code> and to listen for all events that the server sends.</li><li>It filters events named <code>app-event</code>.</li><li>We subscribe to the events <code>onopen</code> and <code>onerror</code>. Each will notify the method <code>OnConnectionChanged</code> about a change in the connection, which will allow showing a different state in the Blazor UI.</li><li>The function <code>stop</code> should be invoked to clean up memory when we want to close the connection.</li></ol><p>With the JS functions ready, the next step is to create the Blazor component. In this new component we will use a <a target="_blank" href="https://www.telerik.com/blazor-ui/grid">Blazor Data Grid</a> type component, because it is a highly configurable component that has built-in options to filter, group, etc., ideal for quickly obtaining information about events in the different systems.</p><p>To do the above, we will create a component called <code>EventFeed.razor</code>, which looks as follows:</p><pre class=" language-xml"><code class="prism  language-xml">@page "/events"
@rendermode InteractiveServer
@inject IJSRuntime JS
@implements IAsyncDisposable

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PageTitle</span><span class="token punctuation">&gt;</span></span>Live Event Feed<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>PageTitle</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mb-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Live Event Feed<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card shadow-sm<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-header d-flex align-items-center justify-content-between py-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>d-flex align-items-center gap-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            @if (isStreaming)
            {
                &lt;span class="badge rounded-pill @(isConnected ? "bg-success" : "bg-secondary") fs-6"&gt;
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>me-1<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>⬤<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>@(isConnected ? "Connected" : "Disconnected")
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
            }
            else
            {
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>badge rounded-pill bg-warning text-dark fs-6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>me-1<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>⏸<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>Paused
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
            }
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-muted<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                Events received: <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-dark<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@totalEventsReceived<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>d-flex gap-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikButton</span> <span class="token attr-name">OnClick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ToggleStreaming<span class="token punctuation">"</span></span>
                           <span class="token attr-name">ThemeColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(isStreaming ? ThemeConstants.Button.ThemeColor.Primary : ThemeConstants.Button.ThemeColor.Success)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                @(isStreaming ? "⏸ Pause" : "▶ Resume")
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikButton</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikButton</span> <span class="token attr-name">OnClick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ClearGrid<span class="token punctuation">"</span></span> <span class="token attr-name">ThemeColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ThemeConstants.Button.ThemeColor.Light<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span> Clear<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikButton</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-body p-0<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikGrid</span> <span class="token attr-name">Data</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@events<span class="token punctuation">"</span></span>
                     <span class="token attr-name">Height</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>560px<span class="token punctuation">"</span></span>
                     <span class="token attr-name">Sortable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>
                     <span class="token attr-name">Resizable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>
                     <span class="token attr-name">Reorderable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>
                     <span class="token attr-name">Groupable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>                     
                     <span class="token attr-name">ShowColumnMenu</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumns</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(AppEvent.Time)<span class="token punctuation">"</span></span> <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Time<span class="token punctuation">"</span></span> <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>110px<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(AppEvent.Level)<span class="token punctuation">"</span></span> <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Level<span class="token punctuation">"</span></span> <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>120px<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Template</span><span class="token punctuation">&gt;</span></span>
                        @{
                            var item = (AppEvent)context;
                        }
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>badge rounded-pill @GetBadgeClass(item.Level) px-3 py-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@item.Level<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Template</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridColumn</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(AppEvent.Source)<span class="token punctuation">"</span></span> <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Source<span class="token punctuation">"</span></span> <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>140px<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(AppEvent.Message)<span class="token punctuation">"</span></span> <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Message<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridColumns</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikGrid</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

@code {
    private List<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>AppEvent</span><span class="token punctuation">&gt;</span></span> events = new();
    private bool isConnected;
    private bool isStreaming = true;
    private int totalEventsReceived;
    private DotNetObjectReference<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>EventFeed</span><span class="token punctuation">&gt;</span></span>? dotNetRef;
    private IJSObjectReference? jsModule;
    private bool _jsInitialized;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            dotNetRef = DotNetObjectReference.Create(this);
                       
            jsModule = await JS.InvokeAsync<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>IJSObjectReference</span><span class="token punctuation">&gt;</span></span>("import", "./js/sse-demos.js");
                        
            await jsModule.InvokeVoidAsync("start", dotNetRef);
            
            _jsInitialized = true;
        }
    }

    [JSInvokable]
    public void OnEventReceived(AppEvent appEvent)
    {
        totalEventsReceived++;
        var updated = new List<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>AppEvent</span><span class="token punctuation">&gt;</span></span>(events);
        updated.Insert(0, appEvent);
        if (updated.Count &gt; 50)
            updated.RemoveRange(50, updated.Count - 50);
        events = updated;
        InvokeAsync(StateHasChanged);
    }

    [JSInvokable]
    public void OnConnectionChanged(bool connected)
    {
        isConnected = connected;
        InvokeAsync(StateHasChanged);
    }

    private async Task ToggleStreaming()
    {
        isStreaming = !isStreaming;
        
        if (jsModule is not null)
        {
            if (isStreaming)
                await jsModule.InvokeVoidAsync("start", dotNetRef);
            else
                await jsModule.InvokeVoidAsync("stop");
        }
    }

    private void ClearGrid()
    {
        events = new List<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>AppEvent</span><span class="token punctuation">&gt;</span></span>();
        StateHasChanged();
    }

    private string GetBadgeClass(string level) =&gt; level switch
    {
        "Info" =&gt; "bg-info text-dark",
        "Warning" =&gt; "bg-warning text-dark",
        "Error" =&gt; "bg-danger",
        "Success" =&gt; "bg-success",
        _ =&gt; "bg-secondary"
    };

    public async ValueTask DisposeAsync()
    {
        if (_jsInitialized &amp;&amp; jsModule is not null)
        {
            try
            {                
                await jsModule.InvokeVoidAsync("stop");
                                
                await jsModule.DisposeAsync();
            }
            catch
            {                
            }
        }
        dotNetRef?.Dispose();
    }

    public class AppEvent
    {
        public int Id { get; set; }
        public string Time { get; set; } = string.Empty;
        public string Level { get; set; } = string.Empty;
        public string Source { get; set; } = string.Empty;
        public string Message { get; set; } = string.Empty;
    }
}
</code></pre><p>In the previous code, there are some points to highlight:</p><ul><li><code>jsModule</code> dynamically loads the JS module.</li><li>The variable <code>dotNetRef</code> wraps the instance of the Blazor component that we will use inside the JS code.</li><li>The method <code>InvokeVoidAsync</code> is used both to start and to stop the event streaming.</li><li>The method <code>OnEventReceived</code> is invoked from the JS code each time an event is received. This allows updating the list <code>events</code> to show the new information in <code>TelerikGrid</code>.</li><li>The <code>OnConnectionChanged</code> method receives a connection status from the JS code to update the UI according to any change in the connection.</li></ul><p>With the new component ready, we can test the application, which looks like the following:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/telerik-datagrid-sse-event-feed.gif?sfvrsn=58cc913a_2" alt="Telerik DataGrid receiving live SSE event feed" /></p><p>With this, we verify that everything works correctly. Also, thanks to the Blazor DataGrid capabilities, we can perform operations such as monitoring only those high-severity events:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/timeline-grouped-events-view.png?sfvrsn=5ae1f10_2" alt="Timeline view of events grouped by day" /></p><p>With this we have a nice event viewer that monitors the status of multiple fictitious systems.</p><h2 id="conclusion">Conclusion</h2><p>Throughout this article you have learned about the SSE standard. You have also seen how changes introduced in .NET 10 help simplify the creation of SSE endpoints, enabling the creation of applications that send real-time events to clients. Now it&rsquo;s your turn to explore when you might use this web standard in your own projects. See you in the next article!</p><hr /><p>Try all this yourself with a free 30-day trial of Telerik UI for Blazor.</p><p><a href="https://www.telerik.com/try/ui-for-blazor" target="_blank" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/23050/17339197.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:2c9543f6-911a-443f-8612-e1d7e336574b</id>
    <title type="text">Routing Management and Creating NotFound Pages in Blazor</title>
    <summary type="text">Route handling got better in .NET 10. Let’s see how to create context for managing information on an error page and show users specific error pages.</summary>
    <published>2026-05-05T15:57:00Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Héctor Pérez </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17334920/routing-management-creating-notfound-pages-blazor"/>
    <content type="text"><![CDATA[<p><span class="featured">Route handling got better in .NET 10. Let&rsquo;s see how to create context for managing information on an error page and show users specific error pages.</span></p><p>If you have worked with Blazor in versions prior to .NET 10, you have certainly encountered some difficulties in implementing not found pages and in handling NavLink to show users the page they are currently on.</p><p>This is why, during the last update to .NET 10, the team behind Blazor has significantly improved the handling of routes and the management of not found pages. In this article, we will analyze the most important aspects. Let&rsquo;s get started!</p><h2 id="understanding-the-handling-of-not-found-routes-in-blazor">Understanding the Handling of Not Found Routes in Blazor</h2><p>When we talk about routing, we refer to the mechanism that allows the user to navigate to a specific component based on the requested URL. However, there may be times when there are non-existent URLs that the user attempts to navigate to, such as when an item has been deleted or when, for some reason, a page has been removed from the site.</p><p>Properly handling these scenarios is essential for a good user experience, good SEO and, in general, having a reliable web application.</p><h2 id="the-problem-with-not-found-pages-before-.net-10">The Problem with Not Found Pages Before .NET 10</h2><p>You should know that before .NET 10, as part of the <code>Router</code> component, there was a render fragment called <code>NotFound</code>, which at first glance could lead us to think that it allowed defining graphical code to show content when a route was not found:</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Router</span> <span class="token attr-name">AppAssembly</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>typeof(Program).Assembly<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Found</span> <span class="token attr-name">Context</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>routeData<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        ...
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Found</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>NotFound</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>LayoutView</span> <span class="token attr-name">Layout</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>typeof(Layout.MainLayout)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span><span class="token punctuation">&gt;</span></span>Page not found<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>Sorry, but there is nothing here!<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>LayoutView</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>NotFound</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Router</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>In practice, something different occurred, as when attempting to navigate to a non-existent page, an error page was displayed instead of the defined graphical code:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/net9-render-fragment-notfound-logic.gif?sfvrsn=d61310e6_2" alt="Execution flow of &#39;NotFound&#39; RenderFragment in .NET 9 routing" /></p><p>Another technique that was used before .NET 10 was to handle the not found page code within the components. For example, there could be an ecommerce-type application with a products page and a product details page. On the product details page, if the user tried to access a non-existent page, it was handled similarly to the following way:</p><pre class=" language-xml"><code class="prism  language-xml">@if (product == null)
{    
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>alert alert-warning text-center<span class="token punctuation">"</span></span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>alert<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h4</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>alert-heading<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Product Not Found<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h4</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>The product with ID <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>@Id<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span> does not exist in our catalog.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>hr</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>btn btn-outline-primary<span class="token punctuation">"</span></span> <span class="token attr-name">@onclick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>GoBack<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token entity" title="←">&amp;larr;</span> Back to Products
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
}
else
{
    ...
}
</code></pre><p>The execution of the previous code resulted in the following appearance:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/handling-notfound-errors-net9.png?sfvrsn=8f3d31f2_2" alt="Handling Not Found errors in .NET 9" /></p><p>The previous code, although it achieved the goal of showing the user that the product did not exist, had several problems:</p><ul><li>It returned an HTTP 200 code, not a 404.</li><li>Each component had to implement its own logic.</li><li>If there was navigation to a non-existent URL (not handled by a component), a site error page was shown.</li></ul><p>Next, let&rsquo;s see how .NET 10 improves the handling of not found pages.</p><h2 id="features-in-.net-10-for-route-management">Features in .NET 10 for Route Management</h2><p>In .NET 10, things have changed regarding route management. Let&rsquo;s analyze what these new features are.</p><h3 id="the-router.notfoundpage-parameter">The Router.NotFoundPage Parameter</h3><p>One of the most prominent updates is the addition of the <code>NotFoundPage</code> parameter to the <code>Router</code> component, which allows specifying a Razor page component that will be displayed when a route is not found. By default, in Blazor projects starting from .NET 10, in the <code>Pages</code> folder we find a component called <code>NotFound.razor</code>, whose structure is as follows:</p><p><strong>NotFound.razor</strong></p><pre class=" language-xml"><code class="prism  language-xml">@page "/not-found"
@layout MainLayout

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">&gt;</span></span>Not Found<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>Sorry, the content you are looking for does not exist.<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
</code></pre><p><strong>Routes.razor</strong></p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Router</span> <span class="token attr-name">AppAssembly</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>typeof(Program).Assembly<span class="token punctuation">"</span></span> <span class="token attr-name">NotFoundPage</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>typeof(Pages.NotFound)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
   ...
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Router</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>The preview of this code gives the following result:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/default-notfoundpage-component-net10.png?sfvrsn=8d90d029_2" alt="Default NotFoundPage component behavior in .NET 10" /></p><p>In the code above, we can see that the 404 error page must include the <code>@page</code> directive for it to function correctly.</p><h3 id="customizing-the-notfound-component">Customizing the NotFound Component</h3><p>With the <code>NotFound.razor</code> component showing us how to implement a 404 page in our Blazor apps, the next step is to customize it. There are several ways to do this. For example, by adding support for contextual messages through a service class called <code>NotFoundContext</code>:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">NotFoundContext</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Heading <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span><span class="token operator">?</span> Message <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">UpdateContext</span><span class="token punctuation">(</span><span class="token keyword">string</span> heading<span class="token punctuation">,</span> <span class="token keyword">string</span> message<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        Heading <span class="token operator">=</span> heading<span class="token punctuation">;</span>
        Message <span class="token operator">=</span> message<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>This class will allow you to show a custom heading and message based on the operation the user has requested. For it to work correctly, you need to register the service in <code>Program.cs</code>:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> builder <span class="token operator">=</span> WebApplication<span class="token punctuation">.</span><span class="token function">CreateBuilder</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddScoped<span class="token punctuation">&lt;</span>NotFoundContext<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
<span class="token keyword">var</span> app <span class="token operator">=</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Now, let&rsquo;s give a bit more design to the <code>NotFound.razor</code> template:</p><pre class=" language-xml"><code class="prism  language-xml">@page "/not-found"
@layout MainLayout
@using NotFoundManagementNET102.Services
@inject NotFoundContext NotFoundContext

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PageTitle</span><span class="token punctuation">&gt;</span></span>Not Found<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>PageTitle</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>container mt-5<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>row justify-content-center<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-md-8 col-lg-6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card shadow-sm border-0 text-center<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-body p-5<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mb-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>80<span class="token punctuation">"</span></span> <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>#6c757d<span class="token punctuation">"</span></span>
                             <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>bi bi-exclamation-triangle<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>0 0 16 16<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>M7.938 2.016A.13.13 0 0 1 8.002 2a.13.13 0 0 1 .063.016...<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-title text-dark mb-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        @(NotFoundContext.Heading ?? "Page Not Found")
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">&gt;</span></span>

                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>card-text text-muted mb-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        @(NotFoundContext.Message ?? "Sorry, the content you are looking for does not exist.")
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>

                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>d-flex justify-content-center gap-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/products<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>btn btn-primary<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                            <span class="token entity" title="←">&amp;larr;</span> Back to Products
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>btn btn-outline-secondary<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                            Go Home
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>In the code above, you can notice that it checks whether the user sets a value for <code>Heading</code> and <code>Message</code>, in order to display either a generic message or a personalized one. Let&rsquo;s see next how to display custom pages based on the user&rsquo;s context.</p><h3 id="handling-not-found-pages-with-navigationmanager.notfound-and-onnotfound">Handling Not Found Pages with NavigationManager.NotFound() and OnNotFound</h3><p>In .NET 9, <code>NavigationManager.NotFound()</code> and <code>OnNotFound</code> were introduced as part of the framework improvements for handling not found pages. We can combine both with the <code>NotFoundContext</code> service to display a 404 response and custom messages, through the following code:</p><pre class=" language-csharp"><code class="prism  language-csharp">@page <span class="token string">"/product/{Id:int}"</span>
@<span class="token keyword">using</span> NotFoundManagementNET102<span class="token punctuation">.</span>Models
@<span class="token keyword">using</span> NotFoundManagementNET102<span class="token punctuation">.</span>Services
@implements <span class="token class-name">IDisposable</span>
@inject ProductService ProductService
@inject NavigationManager Navigation
@inject NotFoundContext NotFoundContext

<span class="token operator">&lt;</span>PageTitle<span class="token operator">&gt;</span>@<span class="token punctuation">(</span>product<span class="token operator">?</span><span class="token punctuation">.</span>Name <span class="token operator">?</span><span class="token operator">?</span> <span class="token string">"Product Detail"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>PageTitle<span class="token operator">&gt;</span>

@<span class="token keyword">if</span> <span class="token punctuation">(</span>product <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"container mt-4"</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>nav aria<span class="token operator">-</span>label<span class="token operator">=</span><span class="token string">"breadcrumb"</span><span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>ol <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"breadcrumb"</span><span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>li <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"breadcrumb-item"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span>a href<span class="token operator">=</span><span class="token string">"/products"</span><span class="token operator">&gt;</span>Products<span class="token operator">&lt;</span><span class="token operator">/</span>a<span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>li<span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>li <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"breadcrumb-item active"</span> aria<span class="token operator">-</span>current<span class="token operator">=</span><span class="token string">"page"</span><span class="token operator">&gt;</span>@product<span class="token punctuation">.</span>Name<span class="token operator">&lt;</span><span class="token operator">/</span>li<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>ol<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span><span class="token operator">/</span>nav<span class="token operator">&gt;</span>

        <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"card shadow-sm"</span><span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"card-body"</span><span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"row"</span><span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"col-md-8"</span><span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>h2 <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"card-title"</span><span class="token operator">&gt;</span>@product<span class="token punctuation">.</span>Name<span class="token operator">&lt;</span><span class="token operator">/</span>h2<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>span <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"badge bg-secondary mb-3"</span><span class="token operator">&gt;</span>@product<span class="token punctuation">.</span>Category<span class="token operator">&lt;</span><span class="token operator">/</span>span<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span>p <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"card-text text-muted"</span><span class="token operator">&gt;</span>@product<span class="token punctuation">.</span>Description<span class="token operator">&lt;</span><span class="token operator">/</span>p<span class="token operator">&gt;</span>

                        <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"row mt-4"</span><span class="token operator">&gt;</span>
                            <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"col-6"</span><span class="token operator">&gt;</span>
                                <span class="token operator">&lt;</span>h5<span class="token operator">&gt;</span>Price<span class="token operator">&lt;</span><span class="token operator">/</span>h5<span class="token operator">&gt;</span>
                                <span class="token operator">&lt;</span>p <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"fs-4 text-primary fw-bold"</span><span class="token operator">&gt;</span>$@product<span class="token punctuation">.</span>Price<span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token string">"N2"</span><span class="token punctuation">)</span><span class="token operator">&lt;</span><span class="token operator">/</span>p<span class="token operator">&gt;</span>
                            <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
                            <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"col-6"</span><span class="token operator">&gt;</span>
                                <span class="token operator">&lt;</span>h5<span class="token operator">&gt;</span>Stock<span class="token operator">&lt;</span><span class="token operator">/</span>h5<span class="token operator">&gt;</span>
                                <span class="token operator">&lt;</span>span <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"badge @(product.Stock &gt; 30 ? "</span>bg<span class="token operator">-</span>success<span class="token string">" : product.Stock &gt; 10 ? "</span>bg<span class="token operator">-</span>warning<span class="token string">" : "</span>bg<span class="token operator">-</span>danger<span class="token string">") fs-6"</span><span class="token operator">&gt;</span>
                                    @product<span class="token punctuation">.</span>Stock units
                                <span class="token operator">&lt;</span><span class="token operator">/</span>span<span class="token operator">&gt;</span>
                            <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
                        <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
                    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>div <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"card-footer"</span><span class="token operator">&gt;</span>
                <span class="token operator">&lt;</span>TelerikButton OnClick<span class="token operator">=</span><span class="token string">"GoBack"</span>
                               ThemeColor<span class="token operator">=</span><span class="token string">"@ThemeConstants.Button.ThemeColor.Primary"</span><span class="token operator">&gt;</span>
                    <span class="token operator">&amp;</span>larr<span class="token punctuation">;</span> Back to Products
                <span class="token operator">&lt;</span><span class="token operator">/</span>TelerikButton<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
<span class="token punctuation">}</span>

@code <span class="token punctuation">{</span>
    <span class="token punctuation">[</span>Parameter<span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token keyword">private</span> Product<span class="token operator">?</span> product<span class="token punctuation">;</span>

    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">void</span> <span class="token function">OnInitialized</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token comment">// 1.</span>
        Navigation<span class="token punctuation">.</span>OnNotFound <span class="token operator">+</span><span class="token operator">=</span> HandleNotFound<span class="token punctuation">;</span>

        <span class="token comment">// 2.</span>
        product <span class="token operator">=</span> ProductService<span class="token punctuation">.</span><span class="token function">GetProductById</span><span class="token punctuation">(</span>Id<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>product <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token comment">// 3.            </span>
            Navigation<span class="token punctuation">.</span><span class="token function">NotFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">HandleNotFound</span><span class="token punctuation">(</span><span class="token keyword">object</span><span class="token operator">?</span> sender<span class="token punctuation">,</span> NotFoundEventArgs e<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token comment">// 4.</span>
        NotFoundContext<span class="token punctuation">.</span><span class="token function">UpdateContext</span><span class="token punctuation">(</span>
            <span class="token string">"Product Not Found"</span><span class="token punctuation">,</span>
            $<span class="token string">"The product with ID {Id} does not exist in our catalog."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">GoBack</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        Navigation<span class="token punctuation">.</span><span class="token function">NavigateTo</span><span class="token punctuation">(</span><span class="token string">"/products"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token comment">// 5.</span>
        Navigation<span class="token punctuation">.</span>OnNotFound <span class="token operator">-</span><span class="token operator">=</span> HandleNotFound<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Let&rsquo;s analyze in more detail what happens in the code above:</p><ol><li>We subscribe to <code>Navigation.OnNotFound</code> to monitor when a product is not found.</li><li>The product is searched by its ID.</li><li>If the product does not exist, <code>Navigation.NotFound</code> is invoked, allowing the rendering of the component with an HTTP 404 code.</li><li>In the <code>HandleNotFound</code> handler, we set a custom title and message.</li><li>We unsubscribe from the event to avoid memory leaks.</li></ol><p>With the previous implementation, if we navigate to a nonexistent URL such as https://localhost:7038/not-found-page, a generic message will be displayed:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/generic-message-custom-component.png?sfvrsn=1642c57c_2" alt="Generic message displayed by the custom component" /></p><p>However, when trying to access the URL of a nonexistent product such as https://localhost:7038/product/122, we will find a custom message:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/custom-message-custom-component.png?sfvrsn=27ee4062_2" alt="Custom message rendered through a personalized component" /></p><p>With the above, we have confirmed that it is now much easier to handle not found routes.</p><h3 id="improvements-in-url-detection-using-navlinkmatch.all">Improvements in URL Detection using NavLinkMatch.All</h3><p>Before .NET 10, when using <code>Match="NavLinkMatch.All"</code>, Blazor was very strict in detecting links. For example, suppose you had something like this in your navigation menu:</p><p><code>&lt;NavLink href="https://www.telerik.comproducts" Match="NavLinkMatch.All"&gt;Products&lt;/NavLink&gt;</code></p><p>If you visited the <code>/products</code> page, the UI would mark an active link in the menu. However, if you navigated to a different URL such as <code>/products?price=cheap</code> or <code>/products#offers</code>, the UI would mark the section as inactive, leading to a poor user experience. This behavior is currently happening in our application:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/navigation-url-change-focus-loss.gif?sfvrsn=5b397a86_2" alt="Focus loss during navigation URL change" /></p><p>In the image above, you can see how the focus is lost when navigating to a product details page. This happens because the main URL is <code>http://localhost:5084/products</code>, while the product URL looks like this: <code>http://localhost:5084/product/2</code>.</p><p>Fortunately, in .NET 10 we can inherit from <code>NavLink</code> and write custom logic to handle matching the URLs we need:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CustomNavLink</span> <span class="token punctuation">:</span> NavLink
<span class="token punctuation">{</span>
    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">bool</span> <span class="token function">ShouldMatch</span><span class="token punctuation">(</span><span class="token keyword">string</span> currentUriAbsolute<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token comment">// 1.</span>
        <span class="token keyword">var</span> baseUri <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Uri</span><span class="token punctuation">(</span>NavigationManagerBase<span class="token punctuation">.</span>BaseUri<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> currentUri <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Uri</span><span class="token punctuation">(</span>currentUriAbsolute<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> relativePath <span class="token operator">=</span> currentUri<span class="token punctuation">.</span>AbsolutePath<span class="token punctuation">.</span><span class="token function">TrimStart</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToLowerInvariant</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token comment">// 2.</span>
        <span class="token keyword">var</span> href <span class="token operator">=</span> AdditionalAttributes <span class="token operator">!=</span> <span class="token keyword">null</span>
            <span class="token operator">&amp;&amp;</span> AdditionalAttributes<span class="token punctuation">.</span><span class="token function">TryGetValue</span><span class="token punctuation">(</span><span class="token string">"href"</span><span class="token punctuation">,</span> <span class="token keyword">out</span> <span class="token keyword">var</span> hrefValue<span class="token punctuation">)</span>
            <span class="token operator">?</span> hrefValue<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">ToString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">TrimStart</span><span class="token punctuation">(</span><span class="token string">'/'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToLowerInvariant</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">:</span> <span class="token keyword">null</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrEmpty</span><span class="token punctuation">(</span>href<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token keyword">base</span><span class="token punctuation">.</span><span class="token function">ShouldMatch</span><span class="token punctuation">(</span>currentUriAbsolute<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token comment">// 3.</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>href <span class="token operator">==</span> <span class="token string">"products"</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">return</span> relativePath <span class="token operator">==</span> <span class="token string">"products"</span>
                <span class="token operator">||</span> relativePath<span class="token punctuation">.</span><span class="token function">StartsWith</span><span class="token punctuation">(</span><span class="token string">"product/"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> <span class="token keyword">base</span><span class="token punctuation">.</span><span class="token function">ShouldMatch</span><span class="token punctuation">(</span>currentUriAbsolute<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token punctuation">[</span>Inject<span class="token punctuation">]</span>
    <span class="token keyword">private</span> NavigationManager NavigationManagerBase <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">default</span><span class="token operator">!</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code above:</p><ol><li>We obtain the relative path from the absolute URI.</li><li>We get the href.</li><li>We perform a custom match so that <code>NavLink</code> is active both in <code>/products</code> and <code>/product/{id}</code>.</li></ol><p>For the previous implementation to work in the project, we need to swap <code>NavLink</code> for <code>CustomNavLink</code> in <code>NavMenu.razor</code>:</p><pre class=" language-xml"><code class="prism  language-xml">...
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>nav-item px-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>CustomNavLink</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>nav-link<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>products<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>bi bi-bag-fill-nav-menu<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span> Products
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>CustomNavLink</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
...
</code></pre><p>The result of the implementation is that the user knows at all times where they are, thanks to the implementation of a custom <code>NavLink</code>:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/custom-navlink-logic.gif?sfvrsn=7067cad3_2" alt="Implementing custom logic for the NavLink component" /></p><h2 id="conclusion">Conclusion</h2><p>Throughout this article, you have learned about some new features in .NET 10 that are particularly useful for handling routes.</p><p>This includes creating a context for managing information on an error page and how to use this context to show users specific error pages, in addition to an inheritance of NavLink to define custom rules for better navigation.</p><p>Undoubtedly, these new features were much needed in Blazor. Now it&rsquo;s your time to use them and create better user experiences utilizing them alongside Progress Telerik <a target="_blank" href="https://www.telerik.com/blazor-ui">components for Blazor</a>.</p><img src="https://feeds.telerik.com/link/23050/17334920.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:354321ce-fa28-42ad-be8e-5ef8fe72a13c</id>
    <title type="text">Loading UI/UX Patterns for AI Applications</title>
    <summary type="text">Give users appropriate loading feedback for the AI process going on. Here are some patterns aligned to the wait time, with implementation ideas for Blazor.</summary>
    <published>2026-04-28T16:39:10Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Ed Charbeneau </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17327232/loading-ui-ux-patterns-ai-applications"/>
    <content type="text"><![CDATA[<p><span class="featured">Give users appropriate loading feedback for the AI process going on. Here are some patterns aligned to the wait time, with implementation ideas for Blazor.</span></p><p>AI-driven applications introduce a new set of challenges for user experience design. Traditional web applications operate within predictable boundaries, where requests complete in a consistent and measurable way. AI features change that dynamic, with response times ranging from near-instant to long-running operations that can take minutes. Designing for that variability is no longer optional, it is a core part of building reliable AI-powered applications.</p><p>The challenge extends beyond implementation into perception. Users do not experience time objectively; they react to how it feels. A blank screen creates doubt, while visible progress keeps the experience grounded. When feedback aligns with the effort required, the interaction feels natural. When it does not, friction builds quickly. Thoughtful loading design addresses this gap by communicating progress, setting expectations and reinforcing the value of the task in motion.</p><p>This guide focuses on practical loading UI patterns for AI scenarios, organized by real-world wait times. Each pattern is implemented with <a target="_blank" href="https://www.telerik.com/blazor-ui">Blazor and Progress Telerik UI components</a>, keeping the discussion grounded in tools developers already use. The goal is simple: create loading experiences that do more than fill time, improving perceived performance and building confidence in the system behind the scenes.
</p><h2>Understanding AI Application Loading Patterns</h2><p>AI applications introduce a different class of loading behavior. Traditional web requests are relatively predictable, but AI workloads are not. Inference, natural language processing and computer vision all introduce variability based on model complexity, input size and system load. As powerful as these capabilities are, they require a more deliberate approach to how progress is communicated.</p><p>Effective loading experiences center on managing expectations through clear, continuous feedback. Every loading state should communicate what is happening now, what has completed and what comes next, keeping users oriented as the system works. With this foundation in place, loading patterns can be tailored to specific wait times, keeping the experience responsive and understandable regardless of how long processing takes.</p><h2>Near Instantaneous Responses (Less Than 1 Second)</h2><p>For interactions under one second, traditional loading indicators often add more friction than value. Quick flashes of spinners or progress bars create a visual glitch that feels broken rather than helpful. Instead, the interface should respond immediately with subtle visual feedback that confirms the action without interrupting flow.</p><p>One exception stands out: when a fast action represents the final step in a longer workflow. In these cases, introducing a brief, intentional delay can create a sense of completion, giving users a moment to recognize the result of their effort before moving on.</p><h3>AI Task Examples</h3><p>AI tasks that typically complete in under one second include:</p><ul><li>Real-time text suggestions and autocomplete</li><li>Simple sentiment analysis on short text</li><li>Basic image classification with lightweight models</li><li>Cached AI responses</li><li>Rule-based chatbot responses</li><li>Simple recommendation filtering</li></ul><h3>Blazor Implementation Strategy</h3><p>Implementing instantaneous feedback in Blazor requires careful coordination between state changes and visual cues. The goal is to reflect user actions immediately while avoiding flicker from overly brief loading states. In practice, this often means enforcing a minimal display duration for indicators so that interactions feel smooth and intentional. </p><p>Try the A/B samples below by clicking the &ldquo;Zoom and Enhance&rdquo; buttons twice. The A sample should feel smoother on short intervals because of the intentional delay.</p><iframe width="100%" height="500px" src="https://blazorrepl.telerik.com/repl/embed/mAkScmvQ58rIAkuM38?editor=true&amp;result=true&amp;errorList=false"></iframe><h3>UX Strategy</h3><p>Instant interactions benefit from confirmation rather than explanation. Subtle cues like button state changes, input highlighting or lightweight animations signal that the system has responded, keeping the experience fluid and uninterrupted. When these signals are consistent and immediate, users stay oriented without feeling like they are waiting.</p><h2>Short Wait Times (1&ndash;3 Seconds)</h2><p>Short waits call for lightweight, indeterminate indicators that acknowledge progress without interrupting flow. Simple spinners or skeleton screens work well because they provide immediate feedback while keeping the interface responsive. More elaborate animations tend to work against this, as users do not have enough time to process them and the interaction can feel slower than it is.</p><p>Skeleton screens are especially effective in AI scenarios because they establish structure upfront. By rendering a placeholder version of the final layout, the interface maintains continuity and gives users a clear sense of what is coming next as content is generated.</p><h3>AI Task Examples</h3><p>AI operations in the 1&ndash;3 second range include:</p><ul><li>Text generation for short responses</li><li>Image enhancement or basic filtering</li><li>Summarizing a document of moderate length</li><li>Translation of short to medium text</li><li>Basic data analysis and insights generation</li><li>Voice-to-text conversion for brief audio</li></ul><h3>Blazor Implementation Strategy</h3><h4>Chat Progress and Thinking</h4><p>The Telerik UI for&nbsp;<a href="https://www.telerik.com/blazor-ui/skeleton" target="_blank">Blazor Skeleton</a> component provides a straightforward way to mirror final layouts while AI-generated content is loading and the agent is thinking.</p><iframe width="100%" height="500px" src="https://blazorrepl.telerik.com/repl/embed/wqkymwFI34Q3Bj4H27?editor=true&amp;result=true&amp;errorList=false"></iframe><h4>Smart Paste Loading</h4><p>The following demo shows a <a href="https://www.telerik.com/blazor-ui/smartpastebutton" target="_blank">smart paste interaction</a> that incorporates loading indicators to signal the process taking place. The Loading Container is displayed over the form fields being populated, while a button loader indicates the action is taking place once the button is clicked.</p><iframe width="100%" height="500px" src="https://blazorrepl.telerik.com/repl/embed/mquemabj125i6dVw38?editor=true&amp;result=true&amp;errorList=false"></iframe><h3>UX Strategy</h3><p>Short waits benefit from clarity without added friction. Contextual messaging that reflects the task in progress, such as &ldquo;Thinking&rdquo; or &ldquo;Generating recommendations,&rdquo; helps users understand what is happening without slowing the experience down. When combined with progressive disclosure, where partial results are rendered as they become available, the interface stays active and responsive, reducing perceived wait time while making the system&rsquo;s work visible.</p><h2>Medium Wait Times (3&ndash;10 Seconds)</h2><p>Users begin to question responsiveness, and a lack of visible progress quickly turns into doubt. Multi-agent workflows or agentic workflows with multiple indeterminate steps can be difficult to navigate. Agents can make requests to external tools that have asynchronous processes. While these processes run, it is important to report the activity in a manageable way without losing focus of the main content.</p><h3>Blazor Implementation</h3><p>Provide feedback for AI operations that expose measurable progress. The example below shows a chat process where the agent uses external tools. Loading indicators are exposed in a collapsible panel to allow the user to see the progress but also hide it when the details are no longer necessary.</p><iframe width="100%" height="500px" src="https://blazorrepl.telerik.com/repl/embed/QqEoGwvA09jzuqBe29?editor=true&amp;result=true&amp;errorList=false"></iframe><h3>UX Strategy</h3><p>Medium waits benefit from a balance of clarity and momentum. Directional time estimates, even something as simple as &ldquo;This may take about a minute,&rdquo; help users decide whether to stay engaged or return later, while continuously moving progress indicators reinforce that the system is still working. When paired with contextual messaging that explains the current step or highlights what is being processed, the experience shifts from passive waiting to a guided interaction that keeps users oriented and informed.</p><h2>Extended Wait Times (10+ Seconds)</h2><p>Extended wait times require a different approach. Clear visibility into progress matters, but so does the freedom to move on without losing track of what is happening. Combining progress indicators with background processing patterns allows the system to stay transparent while keeping the rest of the application usable.</p><p>Percent-complete indicators play a central role here by giving users a sense of scale. Seeing measurable progress helps them judge how much work remains and whether it is worth continuing to wait. At the same time, providing cancel or abort options keeps users in control rather than locked into a long-running process.</p><p>Step-based indicators work especially well for multi-stage AI operations by breaking the process into clear, understandable phases. Even when exact timing is uncertain, each completed step provides direction and reinforces forward movement.</p><h3>Blazor Implementation</h3><p>The following example uses a <a href="https://www.telerik.com/blazor-ui/grid" target="_blank">grid</a> format to display and manage agent activity. Each element displays important information about the task&rsquo;s state. In addition, failure modes are included and made actionable from the interface. This type of UX is great for dashboard-like scenarios where users can launch agent workflows and return later to see the task completion status.</p><iframe width="100%" height="500px" src="https://blazorrepl.telerik.com/repl/embed/QKOSGwlH59MIvvBw27?editor=true&amp;result=true&amp;errorList=false"></iframe><p>If the user may encounter a long and undetermined loading state, a looping indicator can be used to communicate the overall steps that are running in the background, without labeling exactly what stage the process is at now. This nondeterministic progress indicator displays a carousel of information while the user waits. </p><p>With agentic applications, long processes can give the impression of wasted time, even though a process that takes minutes for an agent may have taken a human hour to complete manually. This loading pattern reminds users of the work going on behind the scenes.&nbsp;While some consider this a &ldquo;dark pattern&rdquo; when used to obscure the process details, it can be quite useful when used correctly to share honest information about the value the process provides.</p><p><iframe width="100%" height="500px" src="https://blazorrepl.telerik.com/repl/embed/GKkImcbe33AOXWRF16?editor=true&amp;result=true&amp;errorList=false">&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;nbsp;</iframe></p><p>Extended waits benefit from a design that prioritizes flexibility and user control. Background task patterns, such as persistent status panels or drawers, allow users to continue working while keeping progress visible, reducing the friction of being forced to wait in place. When paired with notification systems that signal completion, the need to actively monitor progress is removed entirely, giving users confidence that the system will follow through.</p><p>Longer waits create space to surface meaningful insights, explain processing steps or provide guidance for improving future results. When these elements are combined effectively, the experience shifts from a blocking delay to a managed, transparent process that keeps users informed without interrupting their workflow.</p><h2>Advanced Patterns for AI Applications</h2><h3>Contextual Loading Messages</h3><p>AI applications benefit from contextual loading messages that explain current processing steps. Replace generic &ldquo;Loading...&rdquo; text with specific descriptions: &ldquo;Analyzing image composition,&rdquo; &ldquo;Generating creative variations&rdquo; or &ldquo;Cross-referencing knowledge base.&rdquo; These messages build user understanding of AI capabilities while setting realistic expectations for output quality.</p><h3>Streaming and Incremental Results</h3><p>Many modern AI applications support streaming responses, particularly for text generation. Implement progressive disclosure patterns that display AI outputs as they generate, reducing perceived wait time while demonstrating active processing. This approach works particularly well for conversational AI interfaces and content-generation tools.</p><h2>Summary</h2><p>Designing loading experiences for AI applications is no longer about handling delays. It is about shaping how users understand and interact with the system. When feedback aligns with the work being performed, users stay engaged and in control regardless of how long processing takes.</p><p>By matching loading strategies to expected wait times, developers can move beyond generic indicators and deliver feedback that is intentional and informative. From immediate visual confirmation to multi-stage progress tracking, each pattern helps make AI interactions feel predictable and responsive.</p><p>Blazor and Telerik UI components provide a practical foundation for implementing these patterns, enabling teams to build consistent loading experiences without introducing unnecessary complexity. The advantage comes from how these tools are applied, not just their availability.</p><p>As AI continues to shape modern applications, loading design becomes a defining part of the user experience. Applications that communicate clearly during processing will feel faster and more reliable, regardless of the work happening behind the scenes.</p><h3>Get Started with These Examples</h3><p>Use any of the Telerik UI for <a target="_blank" href="https://www.telerik.com/blazor-ui">Blazor components</a> shown above, plus the <a target="_blank" href="https://www.telerik.com/blazor-mcp-servers">Blazor MCP servers</a> free with the 30-day trial.</p><p><a href="https://www.telerik.com/try/ui-for-blazor" target="_blank" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/23050/17327232.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:e67bd1e0-6dce-4ba4-92de-7c7975a18492</id>
    <title type="text">Blazor Basics: Getting Started with Blazor Development in VS Code</title>
    <summary type="text">Learn how to use Visual Studio Code for Blazor web development. VS Code is a free, lightweight code editor, and it is available for Mac and Linux users (unlike its more robust sibling Visual Studio, which is exclusive to Windows).</summary>
    <published>2026-04-21T16:22:45Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Claudio Bernasconi </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17323034/blazor-basics-getting-started-blazor-development-vs-code"/>
    <content type="text"><![CDATA[<p><span class="featured">Learn how to use Visual Studio Code for Blazor web development. VS Code is a free, lightweight code editor, and it is available for Mac and Linux users (unlike its more robust sibling Visual Studio, which is exclusive to Windows).</span></p><p>While many developers using Windows love the fully integrated development experience Visual Studio provides, it&rsquo;s not an option for some of us.</p><p>We will learn how to set up Visual Studio Code for Blazor web application development, a few first-hand tips and how to conveniently debug.</p><h2 id="why-use-visual-studio-code-for-blazor-web-development">Why Use Visual Studio Code for Blazor Web Development?</h2><p>Maybe you or your team already use <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> for software development because of its excellent code editor or GIT integration. Why learn something new if you are happy with what you already have?</p><p>Another reason that comes to mind is your preferred operating system.</p><p><a target="_blank" href="https://visualstudio.microsoft.com/">Visual Studio</a> is only available for <strong>Windows</strong>. While I am a Windows user and have always loved the fully fledged feature set provided by Visual Studio for .NET development, I understand that there are other options.</p><p>For <strong>macOS</strong> or <strong>Linux</strong>, you do not have the option to use Visual Studio. Visual Studio Code (VS Code) is a great, lightweight, and free alternative.</p><p>Also, licensing can be an issue. While the Visual Studio Community edition has limitations, VS Code is free for .NET development, both personal and commercial.</p><p>Last but not least, VS Code uses less RAM, hard-drive space and CPU compared to fully fledged IDEs, such as Visual Studio. For hardware constraint situations, it&rsquo;s an ideal choice.</p><h2 id="getting-started-the-setup">Getting Started: The Setup</h2><p>We need two essential things before we can start developing Blazor web applications with VS Code.</p><ol><li>Obviously, we need the latest <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> version installed</li><li>We also need the <a target="_blank" href="https://dotnet.microsoft.com/">latest .NET SDK</a> installed</li></ol><p>Make sure you can execute the <code>dotnet --version</code> command in a terminal. If it doesn&rsquo;t show the version of the installed <strong>.NET SDK</strong>, add the <strong>.NET CLI</strong> to the <code>PATH</code> variable of your operating system and restart your computer before trying again.</p><h2 id="installing-the-c-dev-kit">Installing the C# Dev Kit</h2><p>Next, we want to install the <a target="_blank" href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit">C# Dev Kit extension</a> for Visual Studio Code.</p><p><img title="C# Dev Kit Visual Studio Code extension" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/csharp-dev-kit-extension.png?sfvrsn=55b57e85_2" alt="The marketplace page of the C# Dev Kit Visual Studio Code extension." /></p><p>This extension adds C# development features, including project and solution management, debugging, syntax highlighting, code navigation, refactorings and more, to VS Code. It&rsquo;s a mature extension with more than 13 million downloads.</p><blockquote><p><strong>Hint:</strong> The C# Dev Kit extension not only adds support for Blazor web development but for all C# project types, including desktop, mobile, web, or cloud-native applications.</p></blockquote><h2 id="creating-a-blazor-web-app-in-vs-code">Creating a Blazor Web App in VS Code</h2><p>If you open an empty VS Code instance, you&rsquo;ll get the Create .NET Project menu option in the Explorer panel.</p><p><img title="Visual Studio Code: Explorer" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/explorer-create-new-dotnet-project.png?sfvrsn=7487329a_2" alt="The Explorer panel in Visual Studio Code in an empty project shows the Create .NET Project button." /></p><p>Pressing the <strong>Create .NET Project</strong> button will execute a VS Code command that allows you to select the project type. We select the default <strong>Blazor Web App</strong> project template and choose the destination folder on the hard drive.</p><p>Depending on the project template you choose, you&rsquo;ll be able to add additional information to influence the outcome of the created project.</p><p>After a few seconds, the Explorer now shows the project folder containing the created solution file, the project file and the <code>Components</code> folder containing the example pages and layouts.</p><p><img title="VS Code: Explorer" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/visual-studio-code-solution-explorer.png?sfvrsn=72c36ed7_2" alt="The Explorer panel in VS Code in a Blazor Web App shows the folders, such as the Components folder and all the required code files." /></p><blockquote><p><strong>Hint:</strong> We won&rsquo;t explore the structure and architecture of the Blazor Web application.</p></blockquote><p>Another option for creating a .NET project is using the .NET CLI from the terminal. The <code>dotnet new blazor</code> command creates the same project as shown above.</p><h2 id="running-the-blazor-web-application-in-vs-code">Running the Blazor Web Application in VS Code</h2><p>Running a Blazor Web Application is no different from running any other .NET-based application. In a terminal, you can use the <code>dotnet run</code> or <code>dotnet watch run</code> command to run it.</p><p>The <code>dotnet watch run</code> command automatically rebuilds and refreshes the browser (hot reload) when you edit the code and save the file.</p><p><img title="VS Code: Running a Blazor Web App" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/dotnet-watch-run-command.png?sfvrsn=94413184_2" alt="A terminal executing the dotnet watch run command in the folder of a Blazor Web App." /></p><p>The application runs the same whether you run it in Visual Studio or from the command line without VS Code. All three options use the .NET CLI under the hood.</p><p>Compared to Visual Studio, VS Code is noticeably faster at applying simple changes to components, such as the text of the default Home page component.</p><blockquote><p><strong>Tip:</strong> Using hot reload saves a lot of time. Use the <code>dotnet watch run</code> command and open the <code>Counter.razor</code> component. Change the code in the <code>IncrementCount</code> method from <code>currentCount++</code> to <code>currentCount+=2</code> and save the file. Whenever you hit the <strong>Click Me</strong> button, the counter now increases the value by <strong>two</strong> instead of <strong>one</strong>.</p></blockquote><h2 id="debugging-c-code-in-vs-code">Debugging C# Code in VS Code</h2><p>VS Code not only supports running the Blazor web app, but also debugging it.</p><p>To enable debugging:</p><ol><li>Open the <strong>Run and Debug</strong> panel (CTRL+SHIFT+D)</li><li>Click the <strong>Run and Debug</strong> button.</li><li>Select <strong>C#</strong></li><li>Select one of the available project options, or <strong>launch the Startup</strong> project.</li><li>Click on <strong>Start Debugging</strong> (Green Triangle), or press <strong>F5</strong>.</li></ol><p>You can click left of any C# code line to add a breakpoint. A yellow background highlights the code line when the debugger stops the execution. And you can use the toolbar to step through your code and hover over variables to see their values.</p><p><img title="VS Code: Debugging a Blazor Web App" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/debugging-blazor-web-app.png?sfvrsn=dfa894d3_2" alt="The VS Code debugger stopping at a code line and showing the local variables and the toolbar to control the program execution." /></p><p>The debugging experience is comparable to that of other fully fledged IDEs and allows you to find bugs and understand code execution.</p><p>You can add variables to the watch view to keep their values visible as you step through program execution. You also see the call stack whenever the program execution halts, and you can enable or disable the breakpoints.</p><blockquote><p><strong>Hint:</strong> Make sure the application isn&rsquo;t running from a terminal, such as using the dotnet watch run command.</p></blockquote><h2 id="conclusion">Conclusion</h2><p>While I personally prefer using Visual Studio 2026 for most of my development work, I hopefully was able to have shown that Visual Studio Code also offers a first-class development environment.</p><p>For Linux or macOS, VS Code would definitely be my first choice because it provides most of the tools I regularly use and contains much less bloat than a fully fledged IDE. VS Code is free for personal and commercial use.</p><p>Depending on the scope of your project and how you want to work, VS Code is a good choice to get started with Blazor web development. Especially if you already use VS Code for other projects, such as React or Angular web application development.</p><p>If you want to learn more about Blazor development, watch my <a target="_blank" href="https://www.youtube.com/playlist?list=PLwISgxnkpZGL_LhTQCWwp-WCzupv7lcp0">free Blazor Crash Course</a> on YouTube. And stay tuned to the Telerik blog for more <a target="_blank" href="https://www.telerik.com/blogs/tag/blazor-basics">Blazor Basics</a>.</p><img src="https://feeds.telerik.com/link/23050/17323034.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:9bf61f7e-e7a4-44d4-aba4-e8c1fc74615b</id>
    <title type="text">Progress Telerik Agentic UI Generator vs. Syncfusion Agentic UI Builder</title>
    <summary type="text">See how the AI-based UI creation tools from devtools powerhouses Progress and Syncfusion stack up when they go head to head.</summary>
    <published>2026-04-08T16:38:33Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Hassan Djirdeh </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17315934/progress-telerik-agentic-ui-generator-vs-syncfusion-agentic-ui-builder"/>
    <content type="text"><![CDATA[<p><span class="featured">See how the AI-based UI creation tools from devtools powerhouses Progress and Syncfusion stack up when they go head to head. </span></p><p>AI-powered code generation has become a significant part of how developers build user interfaces. The <a target="_blank" href="https://survey.stackoverflow.co/2025/ai/#1-ai-tools-in-the-development-process">2025 Stack Overflow Developer Survey</a> found that 84% of developers are now using or planning to use AI tools in their development process.</p><p>Original AI adoption has been through tools like code autocompletion and chat-based assistants, but agentic UI tools take things further. Rather than suggesting the next line of code, they can take a natural language prompt and produce working, styled UI code in seconds.</p><p>However, not all AI generation tools are created equal. The quality of the output, how reliably it builds on the first attempt, how fast it runs and how well it handles things like accessibility and theming can vary quite a bit between tools. These differences matter because a tool that requires a large number of follow-up prompts to correct errors or produce code that doesn&rsquo;t match the original requirements isn&rsquo;t truly saving us time.</p><p>In this article, we&rsquo;ll look at a head-to-head comparison between two agentic UI generation tools: the <strong>Progress Telerik Agentic UI Generator</strong> which includes the <a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/ai/agentic-ui-generator/getting-started">Blazor Agentic UI Generator for .NET/Blazor</a> and the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/ai-tools/agentic-ui-generator/getting-started">Kendo UI Generator for Angular and React</a>, and the <a target="_blank" href="https://www.syncfusion.com/explore/agentic-ui-builder/"><strong>Syncfusion Agentic UI Builder</strong></a>. We&rsquo;ll walk through the results of extensive testing across multiple frameworks, covering output quality, performance, stability, accessibility, theming and more.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/progress-vs-syncfusion.jpg?sfvrsn=d281e72b_2" alt="Progress Telerik vs. Syncfusion" /></p><h2 id="how-the-comparison-was-conducted">How the Comparison Was Conducted</h2><p>To make this comparison as fair and thorough as possible, both tools were tested across multiple frameworks using two evaluation approaches.</p><h3 id="blazor.net">Blazor/.NET</h3><p>On the Blazor/.NET side, 23 prompts were run through the Telerik Agentic UI Generator and the Syncfusion Agentic UI Builder. These prompts ranged from simple single-component requests to complex multi-section layouts like e-commerce catalog pages and CMS admin dashboards. Each prompt&rsquo;s output was rated on a 0 to 7 scale:</p><ul><li><strong>0-1</strong>: Doesn&rsquo;t build or crashes with no UI</li><li><strong>2-3</strong>: Partially renders, major sections broken</li><li><strong>4</strong>: Renders but requires multiple prompts to clear build or runtime errors</li><li><strong>5</strong>: Renders but doesn&rsquo;t match prompt requirements in several areas</li><li><strong>6</strong>: Mostly complete, minor issues</li><li><strong>7</strong>: Fully matches requirements</li></ul><p>The evaluation also tracked how many prompts were needed before the output could successfully build and render, as well as the time (in seconds) from initial prompt submission to output generation.</p><h3 id="angular-and-react">Angular and React</h3><p>For Angular and React, a dedicated evaluation was conducted comparing the Kendo Agentic UI Generator against the Syncfusion Agentic UI Builder. Rather than scoring individual prompts, this evaluation focused on a technical comparison of the tools themselves. By running many prompts and examining the context returned by each tool, the analysis compared how each MCP server helps the AI agent arrive at its output across categories like layout, styling, accessibility, documentation and component handling.</p><h2 id="output-quality-and-reliability">Output Quality and Reliability</h2><p>This is the category where the gap between the two tools becomes immediately clear.</p><p>Across the 23 Blazor prompts, the Telerik Agentic UI Generator consistently produced complete, requirement-matching output on the first attempt. The Syncfusion Agentic UI Builder told a different story. It experienced <strong>five failures</strong> where no usable UI was generated at all, and several other prompts that required follow-up prompts to fix errors before the output could even build. Let&rsquo;s look at a couple of examples.</p><h3 id="failures">Failures</h3><p>The very first prompt asked both tools to build a login screen with email/password validation and an admin dashboard with a sidebar menu, key metrics and recent activity.</p><p><strong>Prompt</strong>: <em>&ldquo;I have created an empty application that now needs a login screen and an admin dashboard. Add a login form with email/password fields and validation. After a successful login, redirect to an admin dashboard page featuring a sidebar menu and a main content area displaying key metrics and recent activity.&rdquo;</em></p><p>The Telerik Generator produced a fully working output on the first attempt.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/blazor-agentic-ui-prompt-1.png?sfvrsn=ead93eba_1" alt="Telerik UI Generator produced dashboard" /></p><p>The Syncfusion Builder returned an error along the lines of: <code>Error. This response is truncated because it is too long. Try rephrasing your question.</code> No UI was generated at all.</p><p>The second prompt told both tools to create a monitoring dashboard with system health KPIs, a log stream panel, charts for API response times and requests per service.</p><p><strong>Prompt</strong>: <em>&ldquo;Create a new page using the existing top navigation and footer. In the middle, add 3 rows with 3 responsive columns each. The top row shows system health KPIs for CPU, memory and error counts. The middle rows include a Log Stream panel, a line Chart of API response times and a bar chart of requests per service. The bottom row contains a Deployment History table, an Alerts panel and a list of open tickets.&rdquo;</em></p><p>The Telerik Generator again produced a fully working output on the first pass.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/blazor-agentic-ui-prompt-2.png?sfvrsn=150ecc0b_1" alt="Telerik Generator produced dashboard with graphs" /></p><p>The Syncfusion Builder hit the same truncation error.</p><p>These are the kinds of prompts that represent real-world use cases. A login screen with a dashboard and a monitoring page with multiple data panels aren&rsquo;t unusual requests.</p><h3 id="errors-requiring-follow-ups">Errors Requiring Follow-ups</h3><p>Not every issue is an error during the generation process. Some prompts produced output that needed additional work to get running.</p><p>We fired a prompt to ask for a product catalog page with a responsive layout, product cards, a filtering toolbar and expandable detail views. The Telerik Generator handled it cleanly on the first attempt. The Syncfusion Builder hit an error about a missing component parameter and needed a second follow-up prompt to fix it.</p><p><strong>Prompt</strong>: <em>&ldquo;Create a product catalog page with a responsive CSS layout. The layout should display product cards. Add a toolbar with filtering options, and expandable detail view for each product that work seamlessly on mobile, tablet, and desktop.&rdquo;</em></p><p>The first output by the Telerik Blazor generator:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/blazor-agentic-ui-prompt-4.png?sfvrsn=5b3d958b_1" alt="Telerik Blazor AI UI Generator produced product catalog" /></p><p>The final output from Syncfusion, after the necessary follow-up prompt:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/syncfusion-agentic-ui-prompt-4.png?sfvrsn=a91f64f0_1" alt="Syncfusion product catalog" /></p><p>The Syncfusion Builder&rsquo;s errors in these cases often involved referencing component parameters that don&rsquo;t exist, which may suggest the tool&rsquo;s AI agent is working with less reliable component information during generation.</p><h3 id="partial-results">Partial Results</h3><p>Not all issues showed up as crashes or errors needing follow-ups. Let&rsquo;s look at an example of a prompt that was made which involved a theming request.</p><p><strong>Prompt</strong>: <em>&ldquo;Generate a custom theme for a corporate blue and green color scheme with high contrast accessibility requirements.&rdquo;</em></p><p>The Telerik Agentic UI Generator created the output matching the requirements.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/blazor-agentic-ui-prompt-13.png?sfvrsn=ed53d11a_1" alt="Telerik Agentic UI generator blue n=and green" /></p><p>The Syncfusion Builder did create a custom theme, but the expected theme wasn&rsquo;t applied to the output.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/syncfusion-agentic-ui-prompt-13.png?sfvrsn=eaac1328_1" alt="Syncfusion green and blue missing" /></p><h3 id="the-bigger-picture">The Bigger Picture</h3><p>Even when we set aside the failures and only look at prompts where the Syncfusion Builder produced something usable, the outputs still frequently fell short of the original requirements.</p><p>On the Angular and React side, the findings were consistent. While the final rendered UI sometimes appeared similar at a glance, the most significant differences were in how each tool&rsquo;s MCP server helped the agent arrive at that output. The Kendo Generator&rsquo;s tools consistently delivered more relevant, targeted context to the AI agent, filtering results to what was actually needed for the prompt. The Syncfusion Builder&rsquo;s tools returned broader, less filtered responses, often including full component prop lists and governance rules regardless of what was asked.</p><h2 id="performance">Performance</h2><p>Speed matters when we&rsquo;re iterating on UI designs with AI. Waiting several minutes for a complex layout to generate can significantly slow down the development flow.</p><p>Across the Blazor evaluation, the Telerik Generator was consistently faster. The speed advantage was most noticeable on complex layout prompts. For example, on a complex e-commerce catalog page with filtering, sorting, responsive breakpoints and pagination, the Telerik Generator completed in about 799 seconds while the Syncfusion Builder took 908 seconds.</p><p>There was one outlier where Syncfusion was notably faster (a simpler single-component case), but across the full set of 23 prompts, the speed advantage consistently favored Telerik.</p><h2 id="accessibility">Accessibility</h2><p>Accessibility is often treated as an afterthought, but for enterprise applications, it&rsquo;s a requirement, not a nice-to-have!</p><p>The Kendo Generator includes a dedicated accessibility tool (<a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/ai-tools/agentic-ui-generator/prompt-library#accessibility-assistant">#kendo_accessibility_assistant</a>) that retrieves WCAG 2.2 Level AA guidelines as a mandatory step in the generation process. It also fetches component-specific accessibility requirements for each component used in the output. This leads to every piece of generated UI having accessibility considerations baked in from the start.</p><p>From what we can gather, and at the time of writing, the Syncfusion Builder doesn&rsquo;t have a strong accessibility tool like the <code>#kendo_accessibility_assistant</code>.</p><h2 id="theming-layout-and-documentation">Theming, Layout and Documentation</h2><p>Beyond output quality and speed, the Angular and React evaluation revealed some important architectural differences in how each tool handles theming, layout and documentation.</p><h3 id="theming">Theming</h3><p><strong>Theming</strong> is more than color selection. The Kendo UI Generator produces a complete design system from a single prompt, including colors, typography, spacing scales, border radius, elevation and shadows. The Syncfusion Builder&rsquo;s style tool is limited to component state colors and semantic colors. Typography, spacing and elevation aren&rsquo;t reachable through it, so the generated output may look right in terms of color but lack the design coherence that holds a full application together.</p><h3 id="layout">Layout</h3><p><strong>Layout</strong> is also where a notable dependency difference comes in. The Kendo UI Generator uses a self-contained layout system built on its own CSS utilities, introducing no external dependencies. The Syncfusion Builder relies on <a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a>, injected into every project, and works from a catalog of 265+ prebuilt blocks. When the desired layout doesn&rsquo;t match a catalog entry, the result approximates what was asked for.</p><p>For teams already using Tailwind, this might feel natural but for projects on a different CSS framework (<a target="_blank" href="https://material.angular.dev/">Angular Material</a>, for example), adding Tailwind on top can introduce style conflicts. This can also be a potential blocker for organizations that restrict third-party dependencies.</p><p>Responsiveness follows a similar split: the Kendo UI Generator has a dedicated phase with explicit breakpoint reasoning, while the Syncfusion Builder inherits responsive behavior from Tailwind&rsquo;s defaults without any first-class responsive logic of its own.</p><h3 id="documentation">Documentation</h3><p><strong>Documentation</strong> plays a bigger role in output quality than it might seem, since it directly shapes how well the AI agent understands the components it&rsquo;s generating. The Kendo UI Generator injects relevant documentation chunks directly into every component tool call, including API references with full type signatures. The Syncfusion Builder treats documentation as a separate tool, triggered only by specific keywords. At the time of the evaluation, results from this tool were inconsistent and the service had been unavailable for over 24 hours for both Angular and React.</p><p>One other important observation from the Angular and React evaluation: the Kendo UI Generator customizes its workflow per prompt using confidence scores, including only the tools and instructions relevant to each request. The Syncfusion Builder runs the same fixed sequence for most prompts regardless of complexity, and its component tools return the full prop list and approximately 250 lines of governance rules on every call. The result is that <strong>the Kendo UI agent operates on targeted, preprocessed context while the Syncfusion agent carries a heavier payload for every request</strong>.</p><h2 id="wrap-up">Wrap-up</h2><p>The comparison across Blazor, Angular and React tells a consistent story with these agentic UI generators. The Progress Telerik Agentic UI Generator outperforms the Syncfusion Agentic UI Builder across output quality, reliability, performance, accessibility, theming, layout and documentation.</p><p>On the Blazor side, the Telerik Generator had zero full failures across 23 prompts while Syncfusion had five, along with a consistent speed advantage on complex prompts and more reliable first-pass output. On the Angular and React side, the advantages show up in the underlying architecture, where the Kendo Generator&rsquo;s tools consistently deliver cleaner, more relevant context to the AI agent.</p><hr /><blockquote><p>Get started with AI-empowered development. The <a target="_blank" href="https://www.telerik.com/mcp-servers">Kendo UI and Telerik AI tools</a> are ready. And they come as part of the free trial of the component libraries.</p><br /><p><a href="https://www.telerik.com/mcp-servers#how-to-try" class="Btn" target="_blank">Try Now</a></p></blockquote><hr /><p>For more information on the Progress Telerik Agentic UI Generator, check out the following resources:</p><ul><li><a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/ai/agentic-ui-generator/getting-started">Telerik Agentic UI Generator for Blazor</a></li><li><a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/ai-tools/agentic-ui-generator/getting-started">Kendo UI Generator for Angular</a></li><li><a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/ai-tools/agentic-ui-generator/getting-started">Kendo UI Generator for React</a></li></ul><p>The full comparison data referenced in this article is available for download:</p><ul><li><a target="_blank" href="https://www.telerik.com/docs/default-source/blogs-docs//syncfusion-builder-vs-telerik-generator-comparison.xlsx">Telerik UI for Blazor Agentic UI Generator vs Syncfusion Agentic UI Builder Comparative Evaluation (XLSX)</a></li><li><a target="_blank" href="https://www.telerik.com/docs/default-source/blogs-docs//syncfusion-ui-builder-vs-kendo-ui-generator.xlsx">Kendo Agentic UI Generator vs Syncfusion Agentic UI Builder (XLSX)</a></li></ul><hr /><h2 id="summary-faqs">Summary FAQs</h2><h3 id="what-do-these-tools-do">What Do These Tools Do?</h3><h4 id="what-telerik-agentic-ui-generator-is">What Telerik Agentic UI Generator Is</h4><p>According to the <a href="https://www.telerik.com/mcp-servers" target="_blank">Progress Telerik website</a>, the Telerik Agentic UI Generator for Telerik and Kendo UI libraries is an intelligent development tool delivered through the Model Context Protocol (MCP) Server that enables UI generation from natural language prompts. Once configured and authenticated, you can use the Agentic UI Generator tool (#telerik_ui_generator) together with the available specialized MCP assistants.</p><h4 id="what-syncfusion-agentic-ui-builder-is">What Syncfusion Agentic UI Builder Is</h4><p>According to the <a target="_blank" href="https://www.syncfusion.com/explore/agentic-ui-builder/">Syncfusion site</a>, Syncfusion&rsquo;s Agentic UI Builder is designed to work inside an IDE to generate UIs, dashboards or pages with Syncfusion components with natural language commands. Syncfusion uses MCP integration to access component APIs, prebuilt UI blocks, styling configurations, icon libraries and code generation.</p><h3 id="why-does-mcp-matter-in-both-products">Why Does MCP Matter in Both Products?</h3><p>Both brands&rsquo; tools use Model Context Protocol (MCP) servers to connect AI applications directly to the software specs for the individual library. This means that the AI is informed on component documentation, best practices, workflows and tooling provided by the UI library, so the AI can create a user interface more in line with how the library was designed to work. It should result in better outputs than if an AI were cobbling together an interface without guidance.</p><p>Learn more about the individual MCP servers:</p><ul><li><a href="https://www.telerik.com/blazor-mcp-servers" target="_blank">Telerik UI for Blazor MCP Servers</a></li><li><a href="https://www.telerik.com/angular-mcp-servers" target="_blank">Kendo UI for Angular MCP Servers</a></li><li><a href="https://www.telerik.com/react-mcp-servers" target="_blank">KendoReact MCP Servers</a></li><li><a target="_blank" href="https://ej2.syncfusion.com/react/documentation/mcp-server/overview">Syncfusion EJ2 React MCP Server</a></li><li><a target="_blank" href="https://ej2.syncfusion.com/angular/documentation/mcp-server/overview">Syncfusion EJ2 Angular MCP Server</a></li><li><a target="_blank" href="https://blazor.syncfusion.com/documentation/mcp-server/overview">Syncfusion Blazor MCP Server</a></li></ul><h3 id="are-prompt-libraries-included">Are Prompt Libraries Included?</h3><h4 id="telerik-and-kendo-ui-generator-prompt-libraries">Telerik and Kendo UI Generator Prompt Libraries</h4><p>Yes! Prompt libraries are included for the Kendo UI and Telerik AI UI Generator tools:</p><ul><li>Blazor: <a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/ai/agentic-ui-generator/prompt-library">https://www.telerik.com/blazor-ui/documentation/ai/agentic-ui-generator/prompt-library</a></li><li>React: <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/ai-tools/agentic-ui-generator/getting-started">https://www.telerik.com/kendo-react-ui/components/ai-tools/agentic-ui-generator/getting-started</a></li><li>Angular: <a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/ai-tools/agentic-ui-generator/prompt-library">https://www.telerik.com/kendo-angular-ui/components/ai-tools/agentic-ui-generator/prompt-library</a></li></ul><h4 id="syncfusion-agentic-ui-builder-prompt-libraries">Syncfusion Agentic UI Builder Prompt Libraries</h4><p>Yes! Prompt libraries are included for the Syncfusion Agentic UI Builder tools:</p><ul><li>Blazor: <a target="_blank" href="https://blazor.syncfusion.com/documentation/mcp-server/agentic-ui-builder/prompt-library">https://blazor.syncfusion.com/documentation/mcp-server/agentic-ui-builder/prompt-library</a></li><li>React: <a target="_blank" href="https://ej2.syncfusion.com/react/documentation/mcp-server/agentic-ui-builder/prompt-library">https://ej2.syncfusion.com/react/documentation/mcp-server/agentic-ui-builder/prompt-library</a></li><li>Angular: <a target="_blank" href="https://ej2.syncfusion.com/angular/documentation/mcp-server/agentic-ui-builder/prompt-library">https://ej2.syncfusion.com/angular/documentation/mcp-server/agentic-ui-builder/prompt-library</a></li></ul><img src="https://feeds.telerik.com/link/23050/17315934.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:d364128b-f493-4804-8555-fe37bca196fa</id>
    <title type="text">Blazor Basics: Building Responsive Blazor Apps with CSS Media Queries</title>
    <summary type="text">Responsive design is the reason applications feel polished and user-friendly. Learn how to build responsive Blazor web applications using CSS media queries.</summary>
    <published>2026-04-06T20:25:23Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Claudio Bernasconi </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17314707/blazor-basics-building-responsive-blazor-apps-css-media-queries"/>
    <content type="text"><![CDATA[<p><span class="featured">Responsive design is the reason applications feel polished and user-friendly. Learn how to build responsive Blazor web applications using CSS media queries.</span></p><p>Today, we will learn how to build responsive Blazor web applications. Responsive design is the reason applications feel polished and user-friendly.</p><p>We will first learn the basics of responsive design and how to use CSS media queries to implement responsive layouts. Next, we will learn about what we should and should not do in our Blazor logic (C# code).</p><p>You can <a target="_blank" href="https://github.com/claudiobernasconi/BlazorMediaQueries">access the code used in this example on GitHub</a>.</p><h2 id="what-is-responsive-design-for-blazor-apps">What Is Responsive Design for Blazor Apps?</h2><p>Blazor provides a user interface rendering engine (Razor components) that lets us implement behavior using C#. Blazor components consist of an HTML and CSS template combined with C# interaction logic.</p><p>In modern web development, we want to utilize <strong>CSS media queries</strong> to let our web applications adapt to different screen sizes:</p><ul><li>Small screens, such as mobile phones</li><li>Medium screens, such as tablets</li><li>Large screens, such as desktop computers</li><li>(optional) Ultra-wide monitors</li></ul><p>Best practices for general web development also apply to implementing Blazor web applications.</p><h2 id="css-media-queries-in-blazor-components">CSS Media Queries in Blazor Components</h2><p>Let&rsquo;s learn about <strong>CSS media queries</strong> using a practical example, a Blazor web application generated from the .NET 10 Blazor Web App project template.</p><p>The <code>NavMenu</code> component contains a menu toggler. How it appears on the screen differs completely between a larger and a smaller screen.</p><p><img title="Blazor Web App: No Nav Button" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/nav-button-not-visible-on-desktop.png?sfvrsn=257d381_2" alt="A browser with a running Blazor web appliation on desktop. The nav button is not visible." /></p><p>For a larger screen, the menu is always visible on the left. For a smaller screen, the menu is collapsed, and the menu toggler is visible. The menu only appears if the user presses the hamburger menu button.</p><p>The HTML definition in the <code>NavMenu</code> component looks like this:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Navigation menu<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>navbar-toggler<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
</code></pre><p>It&rsquo;s an input element with the <code>navbar-toggler</code> CSS class attached.</p><p>Let&rsquo;s look at the CSS code defining that behavior:</p><pre class=" language-css"><code class="prism  language-css"><span class="token selector"><span class="token class">.navbar-toggler</span> </span><span class="token punctuation">{</span>
    <span class="token property">appearance</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
    <span class="token property">cursor</span><span class="token punctuation">:</span> pointer<span class="token punctuation">;</span>
    <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">3.5</span>rem<span class="token punctuation">;</span>
    <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">2.5</span>rem<span class="token punctuation">;</span>
    <span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span>
    <span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
    <span class="token property">top</span><span class="token punctuation">:</span> <span class="token number">0.5</span>rem<span class="token punctuation">;</span>
    <span class="token property">right</span><span class="token punctuation">:</span> <span class="token number">1</span>rem<span class="token punctuation">;</span>
    <span class="token property">border</span><span class="token punctuation">:</span> <span class="token number">1</span>px solid <span class="token function">rgba</span><span class="token punctuation">(</span><span class="token number">255</span>, <span class="token number">255</span>, <span class="token number">255</span>, <span class="token number">0.1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token property">background</span><span class="token punctuation">:</span> <span class="token url">url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")</span> no-repeat center/<span class="token number">1.75</span>rem <span class="token function">rgba</span><span class="token punctuation">(</span><span class="token number">255</span>, <span class="token number">255</span>, <span class="token number">255</span>, <span class="token number">0.1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>This CSS class definition defines the visual appearance of the menu toggler. It includes an inline SVG to define the three horizontal lines and a few spacing definitions.</p><p>As shown in the image above, on larger screens, this nav menu button is not visible. Let&rsquo;s look at the CSS code that implements this behavior.</p><pre class=" language-css"><code class="prism  language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 641px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
    <span class="token selector"><span class="token class">.navbar-toggler</span> </span><span class="token punctuation">{</span>
        <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The <code>@media</code> syntax introduces the <strong>CSS media query</strong> feature. In the parentheses, we can implement a filter. In this case, we want all CSS definitions placed inside the curly braces to apply in case the filter condition evaluates to true. In this case, the minimum width of the rendered web page is expected to be 641 pixels.</p><p>In other words: When the page is at least 641 pixels in width, we want the CSS definitions inside the curly braces to be applied. In this definition, we set the <code>display</code> property to <code>none</code> for the <code>navbar-toggler</code> CSS class.</p><p><strong>Learning:</strong> Using CSS media queries, we can implement conditional rules based on a filter criterion. For example, we can add additional CSS definitions for existing CSS classes.</p><p><strong>Hint:</strong> There are additional CSS definitions inside the <code>NavMenu.razor.css</code> file generated by the default .NET 10 Blazor Web App project template. For simplicity and illustration, I included only the relevant code.</p><h2 id="hiding-information-on-smaller-screens">Hiding Information on Smaller Screens</h2><p>A very useful technique is to show certain information only for larger screens.</p><p>As mentioned earlier, you want to carefully decide what information to show on the screen. Does it help the user with the current use case, or does it distract? Is it really necessary?</p><p>Consider the following screenshot of the Weather page for a desktop-sized screen.</p><p><img title="Blazor Web App: Weather Page with all columns" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/weather-page-all-columns-visible.png?sfvrsn=eff050a4_2" alt="A browser with a running Blazor web appliation on desktop. The weather page contains the Date, Temp. (C), Temp. (F), and Summary columns." /></p><p>We have data in four columns: Date, Temperature in Celsius, Temperature in Fahrenheit and Summary.</p><p>The Summary column adds a textual explanation and gives slightly more detail than the numerical temperature. Let&rsquo;s say we don&rsquo;t want to show that Summary on mobile to help the user focus on the actual temperature numbers.</p><p>Let&rsquo;s add the following class definition to the <code>th</code> and <code>td</code> elements of the table in the <code>Weather</code> page component:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>desktop-only<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Summary<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>desktop-only<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@forecast.Summary<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>We added the desktop-only CSS class to those HTML elements.</p><p>Now, we implement the CSS code inside the <code>Weather.razor.css</code> file:</p><pre class=" language-css"><code class="prism  language-css"><span class="token selector"><span class="token class">.desktop-only</span> </span><span class="token punctuation">{</span>
    <span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 768px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
    <span class="token selector"><span class="token class">.desktop-only</span> </span><span class="token punctuation">{</span>
        <span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>We set the <code>display</code> property to <code>none</code> and implement a media query to set it to block for screens larger than 768 pixels.</p><p><img title="Blazor Web App: Weather Page with limited data" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/weather-page-summary-not-visible-on-mobile.png?sfvrsn=a130b019_2" alt="A browser with a running Blazor web appliation on mobile. The weather page contains the Date, Temp. (C), and Temp. (F) columns. The Summary column does not show up on mobile." /></p><p>As you can see in the image, the Summary column doesn&rsquo;t show up for mobile users.</p><p>This is a simple yet powerful technique for conditionally rendering information based on screen size without requiring imperative C# code, such as using the <code>@if</code> directive in the component&rsquo;s template.</p><h2 id="integrating-user-interface-control-libraries">Integrating User Interface Control Libraries</h2><p>You might think that using a professional user control library, such as Progress <a target="_blank" href="https://www.telerik.com/blazor-ui">Telerik UI for Blazor</a>, solves everything for you. They provide professional-looking, tested user interface controls with accessibility and usability in mind. They also provide theming support, making it easy to apply custom colors.</p><p>However, while those components are implemented with responsive design in mind internally, you still have to arrange the layout around them, show and hide panels, and adjust spacing or information density.</p><p>You need to understand the overall structure of your web application and know when to step in and adjust the page layout to accommodate the various aspect ratios and device sizes of the modern world&mdash;from a handheld smartphone to a large 34" desktop monitor.</p><p>A good example provides the implementation of the <code>MainLayout</code> page in the default .NET 10 Blazor Web App project template:</p><pre class=" language-css"><code class="prism  language-css"><span class="token selector"><span class="token class">.page</span> </span><span class="token punctuation">{</span>
    <span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
    <span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
    <span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 641px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
    <span class="token selector"><span class="token class">.page</span> </span><span class="token punctuation">{</span>
        <span class="token property">flex-direction</span><span class="token punctuation">:</span> row<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The <code>page</code> CSS class is applied to the most outer <code>div</code> of the <code>MainLayout</code> component. It defines a relative position and a flex layout with the flex direction <code>column</code>. It means that the items will be rendered vertically below each other.</p><p>The following media query, with a minimum width of 641 pixels, sets the flex direction to <code>row</code>. It means that for screens at least 641 pixels wide, the items will be placed horizontally beside each other.</p><p><strong>This is an excellent example of how to utilize CSS media queries to structure your pages for different screen sizes.</strong></p><p>Yes, utilizing one of the proven user interface libraries will take a lot of weight off your shoulders and provide you with ready-to-use building blocks.</p><p>It will save you a lot of time and let you focus on solving your business problems rather than reinventing the wheel by implementing foundational components.</p><p>However, a fully responsive web application integrates those components to form a truly responsive web application.</p><p><strong>In short:</strong> Responsive design touches all levels of web application development. You have to consider it when implementing reusable building blocks, and when orchestrating them to form a page or application part.</p><h2 id="best-practices-for-implementing-responsive-blazor-web-applications">Best Practices for Implementing Responsive Blazor Web Applications</h2><p>The following best practices will help you with implementing your responsive Blazor web applications:</p><ol><li><strong>Use a mobile-first approach:</strong> When implementing page layouts, always start with the layout for the smallest screens. It will force you to clearly structure the information and present it in a user-friendly way. Creating the layouts for bigger screens is much simpler than starting with a big screen and trying to size it down.</li><li><strong>Use CSS Grid &amp; CSS Flexbox:</strong> They adapt naturally to screen changes, reducing the need for complex breakpoints and other complex CSS magic. I personally prefer Flexbox for internal component structure and Grid for layout.</li><li><strong>Use CSS Isolation in Blazor:</strong> Component-specific styles written in [Component].razor.css files are scoped and easily maintainable. You&rsquo;re sure that changes made in that file do not propagate to other components or your entire application code.</li><li><strong>Avoid screen-size logic in C#:</strong> Do not try to detect screen width or similar information from C# interaction code. Let CSS do the layouting and focus on loading, storing, updating and organizing data in your C# Code.</li><li><strong>Use the browser&rsquo;s developer tools:</strong> Test your responsive web app by changing resolutions and aspect ratios to simulate different devices.</li><li><strong>Use prebuilt user interface libraries:</strong> Reinventing the wheel is time-consuming, and you risk repeating mistakes others have already made and fixed. However, you still need to take care to properly integrate and arrange those third-party components to keep your web application fully responsive.</li></ol><h2 id="conclusion">Conclusion</h2><p>Blazor provides a powerful, simple user interface rendering engine. However, true responsiveness comes from combining it with modern CSS techniques, such as media queries.</p><p>Utilizing CSS Isolation will allow you to separate the scope of your CSS for individual components.</p><p>Responsive design is important for implementing modern low-level components, but also for integrating them into a full webpage or web application.</p><p>While CSS media queries are powerful and let us implement conditional behavior, it&rsquo;s still a lot of code that needs to be maintained. Be careful to extract reused code into components to minimize the effect, and use third-party user interface libraries to avoid re-inventing the wheel.</p><p>If you want to learn more about Blazor development, watch my <a target="_blank" href="https://www.youtube.com/playlist?list=PLwISgxnkpZGL_LhTQCWwp-WCzupv7lcp0">free Blazor Crash Course</a> on YouTube. And stay tuned to the Telerik blog for more <a target="_blank" href="https://www.telerik.com/blogs/tag/blazor-basics">Blazor Basics</a>.</p><img src="https://feeds.telerik.com/link/23050/17314707.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:fda2f92a-0bba-4092-b4a7-71adac14edf1</id>
    <title type="text">Instantiating Objects and Accessing Properties in Blazor</title>
    <summary type="text">Learn how to use Blazor’s interop features with JavaScript to handle live C# objects for typeable code.</summary>
    <published>2026-03-30T19:06:20Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Héctor Pérez </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17309760/instantiating-objects-accessing-properties-blazor"/>
    <content type="text"><![CDATA[<p><span class="featured">Learn how to use Blazor&rsquo;s interop features with JavaScript to handle live C# objects for typeable code.</span></p><p>Blazor is Microsoft&rsquo;s framework ideal for creating single-page applications (SPAs), complementing the stack for developing applications entirely in .NET. This is why each year we see significant updates in the framework that allows developers to create secure and robust applications. These changes, along with <a target="_blank" href="https://www.telerik.com/blazor-ui">Blazor control suites</a>&nbsp;like the one from Progress Telerik, undoubtedly accelerate the development of complete apps.</p><p>In this article, I will tell you about the new interop features with JavaScript, which certainly marks a significant improvement in handling objects from the C# language. Let&rsquo;s get started!</p><h2 id="understanding-the-problem-to-solve">Understanding the Problem to Solve</h2><p>Until .NET 10, interoperability with JS was mainly done by invoking JS functions, without being able to directly handle live objects from C# code, which involved creating wrappers and code that could often lead to failures and runtime errors due to syntax errors.</p><p>We can get an idea of the above through a practical case. Suppose that in our application, we have a <a target="_blank" href="https://www.telerik.com/blazor-ui/gridhttps://demos.telerik.com/blazor-ui/grid/overview">Blazor Data Grid</a> control that allows displaying products from an online store. Each row has a column with a button to show a lightbox popup, where images are displayed. The demonstration code, for you to gain a complete idea, looks as follows.</p><p>First, I created a JavaScript file with the necessary functionality to display the lightbox image gallery:</p><p><strong>lightboxGallery.js</strong></p><pre class=" language-javascript"><code class="prism  language-javascript">window<span class="token punctuation">.</span>lightboxGallery <span class="token operator">=</span> <span class="token punctuation">{</span>
    LightboxGallery<span class="token punctuation">:</span> <span class="token keyword">class</span> <span class="token class-name">LightboxGallery</span> <span class="token punctuation">{</span>
        <span class="token function">constructor</span><span class="token punctuation">(</span>imageUrls<span class="token punctuation">,</span> startIndex <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>images <span class="token operator">=</span> imageUrls<span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>currentIndex <span class="token operator">=</span> startIndex<span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>keyHandler <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token function">show</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">createOverlay</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">displayImage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token function">createOverlay</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>

            <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">createElement</span><span class="token punctuation">(</span><span class="token string">'div'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">.</span>className <span class="token operator">=</span> <span class="token string">'lightbox-overlay'</span><span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token template-string"><span class="token string">`
            &lt;div class="lightbox-container"&gt;
                &lt;button class="lightbox-close"&gt;&amp;times;&lt;/button&gt;
                &lt;button class="lightbox-prev"&gt;&amp;lsaquo;&lt;/button&gt;
                &lt;div class="lightbox-content"&gt;
                    &lt;img class="lightbox-image" src="https://www.telerik.com" alt="Gallery Image"&gt;
                    &lt;div class="lightbox-counter"&gt;&lt;/div&gt;
                &lt;/div&gt;
                &lt;button class="lightbox-next"&gt;&amp;rsaquo;&lt;/button&gt;
            &lt;/div&gt;
        `</span></span><span class="token punctuation">;</span>

            document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">appendChild</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">)</span><span class="token punctuation">;</span>
            
            <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.lightbox-close'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.lightbox-prev'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">prev</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.lightbox-next'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>target <span class="token operator">===</span> <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                    <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">}</span>
            <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">keyHandler</span> <span class="token operator">=</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>key <span class="token operator">===</span> <span class="token string">'Escape'</span><span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>key <span class="token operator">===</span> <span class="token string">'ArrowLeft'</span><span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">prev</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">if</span> <span class="token punctuation">(</span>e<span class="token punctuation">.</span>key <span class="token operator">===</span> <span class="token string">'ArrowRight'</span><span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span><span class="token punctuation">;</span>
            document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'keydown'</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>keyHandler<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token function">displayImage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>

            <span class="token keyword">const</span> img <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.lightbox-image'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">const</span> counter <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.lightbox-counter'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            img<span class="token punctuation">.</span>src <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>images<span class="token punctuation">[</span><span class="token keyword">this</span><span class="token punctuation">.</span>currentIndex<span class="token punctuation">]</span><span class="token punctuation">;</span>
            counter<span class="token punctuation">.</span>textContent <span class="token operator">=</span> <span class="token template-string"><span class="token string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>currentIndex <span class="token operator">+</span> <span class="token number">1</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> / </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token keyword">this</span><span class="token punctuation">.</span>images<span class="token punctuation">.</span>length<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>currentIndex <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>currentIndex <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token keyword">this</span><span class="token punctuation">.</span>images<span class="token punctuation">.</span>length<span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">displayImage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token function">prev</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>currentIndex <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>currentIndex <span class="token operator">-</span> <span class="token number">1</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>images<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token keyword">this</span><span class="token punctuation">.</span>images<span class="token punctuation">.</span>length<span class="token punctuation">;</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">displayImage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>

        <span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">this</span><span class="token punctuation">.</span>overlayElement <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>keyHandler<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                document<span class="token punctuation">.</span><span class="token function">removeEventListener</span><span class="token punctuation">(</span><span class="token string">'keydown'</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>keyHandler<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">this</span><span class="token punctuation">.</span>keyHandler <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
        <span class="token punctuation">}</span>
        
        <span class="token function">getCurrentIndex</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>currentIndex<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        
        <span class="token function">getImagesLength</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>images<span class="token punctuation">.</span>length<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><p>The above code shows the definition of a JS class <code>LightboxGallery</code> with some properties to control the gallery. In addition, methods are also defined to enable interaction with it, such as <code>next</code>, <code>prev</code>, <code>show</code>, etc.</p><p>This file needs to be registered in <code>App.razor</code> to be able to use it from the application, as I show you below:</p><pre class=" language-html"><code class="prism  language-html">....
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">&gt;</span></span>
    ...
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Routes</span> <span class="token attr-name">@rendermode</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>InteractiveServer<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ReconnectModal</span> <span class="token punctuation">/&gt;</span></span>
    &lt;script src="https://www.telerik.com@Assets["_content/Telerik.UI.for.Blazor/js/telerik-blazor.js"]"&gt;<span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
    &lt;script src="https://www.telerik.com@Assets["js/lightboxGallery.js"]"&gt;<span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
    &lt;script src="https://www.telerik.com@Assets["_framework/blazor.web.js"]"&gt;<span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>Finally, I created a Razor page where I use the Blazor Data Grid and <a target="_blank" href="https://www.telerik.com/blazor-ui/buttons">Blazor Button</a> components to create a quick and functional graphic interface.</p><pre class=" language-xml"><code class="prism  language-xml">@page "/products-traditional"
@inject IJSRuntime JSRuntime

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PageTitle</span><span class="token punctuation">&gt;</span></span>Products - Traditional JS Interop<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>PageTitle</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>container-fluid mt-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>row mb-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-12<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>display-6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Product Gallery<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>          
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>col-12<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikGrid</span> <span class="token attr-name">Data</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@Products<span class="token punctuation">"</span></span>
                         <span class="token attr-name">Pageable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>
                         <span class="token attr-name">PageSize</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>10<span class="token punctuation">"</span></span>
                         <span class="token attr-name">Sortable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>
                         <span class="token attr-name">FilterMode</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@GridFilterMode.FilterRow<span class="token punctuation">"</span></span>
                         <span class="token attr-name">Resizable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>
                         <span class="token attr-name">Height</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>600px<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumns</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(Product.Id)<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ID<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>80px<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(Product.Name)<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Product Name<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>200px<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(Product.Category)<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Category<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>150px<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(Product.Price)<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Price<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>120px<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Template</span><span class="token punctuation">&gt;</span></span>
                            @{
                                var product = context as Product;
                                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>$@product?.Price.ToString("N2")<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
                            }
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Template</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridColumn</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(Product.Stock)<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Stock<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>100px<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">Title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Actions<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Width</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>150px<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Filterable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span> 
                                <span class="token attr-name">Sortable</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>false<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Template</span><span class="token punctuation">&gt;</span></span>
                            @{
                                var product = context as Product;
                                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikButton</span> <span class="token attr-name">OnClick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(async () =&gt; await ShowImagesTraditional(product))<span class="token punctuation">"</span></span>
                                               <span class="token attr-name">ThemeColor</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ThemeConstants.Button.ThemeColor.Primary<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                                    Show Images
                                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikButton</span><span class="token punctuation">&gt;</span></span>
                            }
                        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Template</span><span class="token punctuation">&gt;</span></span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridColumn</span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>GridColumns</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikGrid</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>If we want to invoke the methods from the JS code, there are various ways, although in the following example I show you one of the easiest. To display the gallery, we would use the following code:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">private</span> <span class="token keyword">async</span> Task <span class="token function">ShowImagesTraditional</span><span class="token punctuation">(</span>Product<span class="token operator">?</span> product<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>product <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> product<span class="token punctuation">.</span>ImageUrls<span class="token punctuation">.</span>Count <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span><span class="token punctuation">;</span>
        
    <span class="token keyword">var</span> imageUrlsJson <span class="token operator">=</span> System<span class="token punctuation">.</span>Text<span class="token punctuation">.</span>Json<span class="token punctuation">.</span>JsonSerializer<span class="token punctuation">.</span><span class="token function">Serialize</span><span class="token punctuation">(</span>product<span class="token punctuation">.</span>ImageUrls<span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token comment">// Step 1</span>
    <span class="token keyword">await</span> JSRuntime<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"eval"</span><span class="token punctuation">,</span> 
        $<span class="token string">"window.__tempGallery = new lightboxGallery.LightboxGallery({imageUrlsJson}, 0)"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token comment">// Step 2</span>
    <span class="token keyword">await</span> JSRuntime<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"eval"</span><span class="token punctuation">,</span> <span class="token string">"window.__tempGallery.show()"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token comment">// Step 3</span>
    <span class="token keyword">var</span> currentIndex <span class="token operator">=</span> <span class="token keyword">await</span> JSRuntime<span class="token punctuation">.</span><span class="token generic-method function">InvokeAsync<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"eval"</span><span class="token punctuation">,</span> <span class="token string">"window.__tempGallery.currentIndex"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token comment">// Step 4</span>
    <span class="token keyword">var</span> imagesCount <span class="token operator">=</span> <span class="token keyword">await</span> JSRuntime<span class="token punctuation">.</span><span class="token generic-method function">InvokeAsync<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"eval"</span><span class="token punctuation">,</span> <span class="token string">"window.__tempGallery.images.length"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    <span class="token comment">// Step 5</span>
    Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"Traditional: Showing {imagesCount} images, starting at index {currentIndex}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    
    isGalleryOpen <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
    <span class="token function">StateHasChanged</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    
<span class="token punctuation">}</span>
</code></pre><p>Here&rsquo;s what we do in the above code:</p><ul><li><strong>Step 1</strong>: We use eval and global variables in <code>window</code> to maintain the reference to the gallery.</li><li><strong>Step 2</strong>: The method show defined in the class <code>LightboxGallery</code> is called.</li><li><strong>Step 3</strong>: We obtain the <code>index</code> of the image from the gallery.</li><li><strong>Step 4</strong>: The number of images is obtained using <code>lenght</code>.</li><li><strong>Step 5</strong>: The results are displayed based on the information obtained.</li></ul><p>The same applies to perform operations like navigating forward or backward:</p><p><strong>Navigating Forward</strong></p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">await</span> JSRuntime<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"eval"</span><span class="token punctuation">,</span> <span class="token string">"window.__tempGallery.next()"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p><strong>Navigating Backward</strong></p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">await</span> JSRuntime<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"eval"</span><span class="token punctuation">,</span> <span class="token string">"window.__tempGallery.prev()"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Or even to close the gallery:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">await</span> JSRuntime<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"eval"</span><span class="token punctuation">,</span> <span class="token string">"window.__tempGallery.close()"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">await</span> JSRuntime<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"eval"</span><span class="token punctuation">,</span> <span class="token string">"delete window.__tempGallery"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The example being executed looks as follows:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/demo-app-js-gallery.gif?sfvrsn=466ae63b_2" alt="The demo application displaying a gallery rendered with JavaScript" /></p><p>In the previous example, you can notice several problems, starting with the use of <code>eval</code>, which allows executing text as JavaScript code. Although it is an easy way to run code, it poses a danger as it opens the door to code injection. Moreover, it is slow as it cannot be optimized, is difficult to debug and is generally considered a bad practice.</p><p>Another problem is the direct use of global variables in <code>window</code>, as there is a risk of collisions with other libraries in the project; there is no management of galleries or lifecycle, plus there is no control over when to destroy it.</p><p>A third serious problem is that there can be a memory leak if the object in memory isn&rsquo;t destroyed since the browser&rsquo;s garbage collection (GC) cannot collect the reference, leaving listeners and live DOM references.</p><h2 id="new-javascript-interop-features-in-blazor-10">New JavaScript Interop Features in Blazor 10</h2><p>We could continue discussing the multiple issues in the example from the previous section, but let&rsquo;s focus on the new features of Blazor in .NET 10 to address this.</p><h2 id="the-invokeconstructorasync-method">The InvokeConstructorAsync Method</h2><p>In .NET 10, it is possible to use the method <code>InvokeConstructorAsync</code>, which allows you to invoke a JS constructor asynchronously. This method will execute the <code>new</code> operator and return a type <code>IJSObjectReference</code>. For example, to display a gallery, it would be done as follows:</p><pre class=" language-csharp"><code class="prism  language-csharp">@code <span class="token punctuation">{</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    <span class="token keyword">private</span> IJSObjectReference<span class="token operator">?</span> galleryInstance<span class="token punctuation">;</span>
    <span class="token keyword">private</span> GalleryInfo<span class="token operator">?</span> currentGalleryInfo<span class="token punctuation">;</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    <span class="token keyword">private</span> <span class="token keyword">async</span> Task <span class="token function">ShowImagesModern</span><span class="token punctuation">(</span>Product<span class="token operator">?</span> product<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>product <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> product<span class="token punctuation">.</span>ImageUrls<span class="token punctuation">.</span>Count <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span><span class="token punctuation">;</span>

        <span class="token keyword">try</span>
        <span class="token punctuation">{</span>            
            <span class="token keyword">if</span> <span class="token punctuation">(</span>galleryInstance <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">await</span> galleryInstance<span class="token punctuation">.</span><span class="token function">DisposeAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            
            <span class="token keyword">var</span> imageUrls <span class="token operator">=</span> product<span class="token punctuation">.</span>ImageUrls<span class="token punctuation">.</span><span class="token function">ToArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            
            <span class="token comment">// Step 1</span>
            galleryInstance <span class="token operator">=</span> <span class="token keyword">await</span> JSRuntime<span class="token punctuation">.</span><span class="token function">InvokeConstructorAsync</span><span class="token punctuation">(</span>
                <span class="token string">"lightboxGallery.LightboxGallery"</span><span class="token punctuation">,</span> 
                imageUrls<span class="token punctuation">,</span> 
                <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token comment">// Step 2</span>
            <span class="token keyword">await</span> galleryInstance<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"show"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token comment">// Step 3</span>
            <span class="token keyword">var</span> currentIndex <span class="token operator">=</span> <span class="token keyword">await</span> galleryInstance<span class="token punctuation">.</span><span class="token generic-method function">GetValueAsync<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"currentIndex"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            
            <span class="token comment">// Step 4</span>
            <span class="token keyword">var</span> imagesLength <span class="token operator">=</span> <span class="token keyword">await</span> galleryInstance<span class="token punctuation">.</span><span class="token generic-method function">GetValueAsync<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"images.length"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            
            <span class="token comment">//Step 5</span>
            currentGalleryInfo <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GalleryInfo</span>
            <span class="token punctuation">{</span>
                CurrentIndex <span class="token operator">=</span> currentIndex<span class="token punctuation">,</span>
                TotalImages <span class="token operator">=</span> imagesLength
            <span class="token punctuation">}</span><span class="token punctuation">;</span>

            <span class="token function">StateHasChanged</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            
            Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"Modern: Showing {imagesLength} images, starting at index {currentIndex}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            
        <span class="token punctuation">}</span>
        <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> ex<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"Error showing images: {ex.Message}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the above code, the exact same steps are followed to display the gallery, but with cleaner code. <code>InvokeConstructorAsync</code> is used to create the object and maintain its reference, which can in turn be used to invoke other methods using the traditional method <code>InvokeVoidAsync</code>. In our example, we invoke the method <code>show</code> defined in the JavaScript class.</p><p>To perform other actions, a similar approach is followed:</p><p>To advance:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">await</span> galleryInstance<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"next"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>To go back:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">await</span> galleryInstance<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"prev"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>To close the gallery:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">await</span> galleryInstance<span class="token punctuation">.</span><span class="token function">InvokeVoidAsync</span><span class="token punctuation">(</span><span class="token string">"close"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
currentGalleryInfo <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token function">StateHasChanged</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>With this new approach, we have a drastic change not only aesthetically but also internally. For example, each component has its own instance, which prevents collisions between them. Additionally, there is a controlled lifecycle with no memory leaks. You may also notice that parameter passing is much simpler and safer.</p><h2 id="the-getvalueasync-and-setvalueasync-methods">The GetValueAsync and SetValueAsync Methods</h2><p>In addition to the method for invoking a JS function constructor, we can also access the values of the instance obtained through the methods <code>GetValueAsync&lt;TValue&gt;(string identifier)</code> and <code>SetValueAsync&lt;TValue&gt;(string identifier, TValue value)</code>. A clear example of this can be seen when we obtain the value of the current image and the length for debugging purposes:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token comment">// Step 3</span>
<span class="token keyword">var</span> currentIndex <span class="token operator">=</span> <span class="token keyword">await</span> galleryInstance<span class="token punctuation">.</span><span class="token generic-method function">GetValueAsync<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"currentIndex"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Step 4</span>
<span class="token keyword">var</span> imagesLength <span class="token operator">=</span> <span class="token keyword">await</span> galleryInstance<span class="token punctuation">.</span><span class="token generic-method function">GetValueAsync<span class="token punctuation">&lt;</span><span class="token keyword">int</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token string">"images.length"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">//Step 5</span>
currentGalleryInfo <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">GalleryInfo</span>
<span class="token punctuation">{</span>
    CurrentIndex <span class="token operator">=</span> currentIndex<span class="token punctuation">,</span>
    TotalImages <span class="token operator">=</span> imagesLength
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token function">StateHasChanged</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

Console<span class="token punctuation">.</span><span class="token function">WriteLine</span><span class="token punctuation">(</span>$<span class="token string">"Modern: Showing {imagesLength} images, starting at index {currentIndex}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>In the above code, these values are displayed through an <code>Console.WriteLine</code>, but you could perfectly use them to modify other UI elements or to perform operations.</p><h2 id="conclusion">Conclusion</h2><p>Throughout this article, you have been introduced to the new JavaScript interop features available in .NET 10, which undoubtedly marks an evolution in the way interaction with JS occurs when necessary. It is time to create safer and typeable code in web apps based on Blazor.</p><hr /><p>Remember, you can try Telerik UI for Blazor free for 30 days:</p><p><a href="https://www.telerik.com/try/ui-for-blazor" target="_blank" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/23050/17309760.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:d682b921-294a-4401-b442-3aed57b43aa9</id>
    <title type="text">What’s New for Blazor in .NET 11 Preview Releases 1 and 2</title>
    <summary type="text">Tour the notable Blazor changes and new features in .NET 11 Previews 1 and 2 for an early glimpse into what’s coming for Blazor in .NET 11.</summary>
    <published>2026-03-25T16:13:51Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Jon Hilton </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17305972/whats-new-blazor-net-11-preview-releases-1-2"/>
    <content type="text"><![CDATA[<p><span class="featured">Tour the notable Blazor changes and new features in .NET 11 Previews 1 and 2 for an early glimpse into what&rsquo;s coming for Blazor in .NET 11.</span></p><p>Ever since the significant changes that landed with .NET 8, we&rsquo;ve seen incremental updates to Blazor over several releases.</p><p>This year seems set to continue that trend, with some key gaps being filled and refinements to key features. So no structural shifts, new hosting models or render modes, but we do finally get a <code>Label</code> component for forms!</p><p>Here are some notable Blazor changes and new features that landed in .NET 11 Previews 1 and 2.</p><h2 id="tempdata-for-static-ssr">TempData for Static SSR</h2><p>Ever since .NET 8, we&rsquo;ve had SSR mode for Blazor. It&rsquo;s a way of building applications using Blazor&rsquo;s Razor components, but without resorting to Blazor Server or Blazor WASM to run those components.</p><p>With SSR, when a user requests your page, Blazor runs on the server, locates the relevant page component, renders it and returns the generated HTML which is then displayed in the browser.</p><p>In this mode you can also use forms to capture user input.</p><p>But sometimes you want to take information and persist it between page navigations. For example, maybe a user enters their name in a form, and you want to show them a personalized message on the subsequent confirmation screen, &ldquo;Thanks, Jon, for your order.&rdquo;</p><p>Prior to .NET 11, this required you to pass the value along somehow, with common solutions involving query string, session state or custom cookies.</p><p>But MVC and Razor Pages developers have long been able to use something called Temp Data to store that information between screens. Temp Data is specifically for capturing short-lived messages or data that only need to survive one redirect, then disappear.</p><p>In .NET 11, Temp Data is coming to Blazor and will be available as a cascading parameter in static SSR components, with no extra registration needed beyond the usual <code>AddRazorComponents()</code>:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span>CascadingParameter<span class="token punctuation">]</span>
<span class="token keyword">public</span> ITempData<span class="token operator">?</span> TempData <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
</code></pre><p>Let&rsquo;s say you&rsquo;re about to redirect your user to a different page. You can write a value to <code>TempData</code>:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">HandleSubmit</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    TempData<span class="token punctuation">[</span><span class="token string">"SuccessMessage"</span><span class="token punctuation">]</span> <span class="token operator">=</span> $<span class="token string">"Thanks {Input.Name}, your message has been sent!"</span><span class="token punctuation">;</span>
    Navigation<span class="token punctuation">.</span><span class="token function">NavigateTo</span><span class="token punctuation">(</span><span class="token string">"/contact/confirmation"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Then pick that up on the page they&rsquo;re redirected to:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">void</span> <span class="token function">OnInitialized</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token comment">// Get() reads and marks for deletion - one-shot read</span>
    confirmationMessage <span class="token operator">=</span> TempData<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Get</span><span class="token punctuation">(</span><span class="token string">"SuccessMessage"</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Under the hood, these values are stored in a cookie in the user&rsquo;s browser.</p><p>Once you&rsquo;ve retrieved a value via <code>Get</code>, it&rsquo;s marked for deletion. So if you want to check the value without it being deleted, you can use <code>Peek</code> instead. If you want to keep something that has been marked for deletion you can call <code>Keep</code> to cancel the deletion.</p><h2 id="ihostedservice-in-blazor-webassembly">IHostedService in Blazor WebAssembly</h2><p>If you&rsquo;re using Blazor WebAssembly, you may well have realized there&rsquo;s one thing you can&rsquo;t easily do with Blazor running in the browser: schedule jobs to run in the background.</p><p>This is a challenge because sometimes you want to separate background work from your component lifecycles. For example, you might want to poll an API (maybe a pricing or notifications endpoint).</p><p>In WASM apps, prior to .NET 11, this has been tricky to achieve, often requiring the use of timers within specific components (tied to component lifecycles and only running when the component is rendered), or singleton services with a manual timer.</p><p>.NET 11 simplifies this with <code>IHostedService</code>.</p><p>You can add a hosted service to your Blazor WASM app and it will start with the app (not a specific component), and run independently of navigation.</p><p><code>AddHostedService&lt;T&gt;()</code> now works with <code>WebAssemblyHostBuilder</code>:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">var</span> builder <span class="token operator">=</span> WebAssemblyHostBuilder<span class="token punctuation">.</span><span class="token function">CreateDefault</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">;</span>
builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddHostedService<span class="token punctuation">&lt;</span>PricePollingService<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">await</span> builder<span class="token punctuation">.</span><span class="token function">Build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">RunAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The service itself is standard <code>BackgroundService</code>, identical to its server-side equivalent:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PricePollingService</span><span class="token punctuation">(</span>PriceState state<span class="token punctuation">)</span> <span class="token punctuation">:</span> BackgroundService
<span class="token punctuation">{</span>
    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">async</span> Task <span class="token function">ExecuteAsync</span><span class="token punctuation">(</span>CancellationToken stoppingToken<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token operator">!</span>stoppingToken<span class="token punctuation">.</span>IsCancellationRequested<span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">await</span> <span class="token function">FetchPricesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            state<span class="token punctuation">.</span><span class="token function">NotifyUpdate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">await</span> Task<span class="token punctuation">.</span><span class="token function">Delay</span><span class="token punctuation">(</span>TimeSpan<span class="token punctuation">.</span><span class="token function">FromSeconds</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">,</span> stoppingToken<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>This example uses a <code>Singleton</code> <code>PriceState</code> service and a component which listens for updates to that state.</p><p>The <code>PriceState</code> service acts as a shared state container which the background service writes to:</p><p><strong>Program.cs</strong></p><pre class=" language-csharp"><code class="prism  language-csharp">builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token generic-method function">AddSingleton<span class="token punctuation">&lt;</span>PriceState<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p><strong>PriceState.cs</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PriceState</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">event</span> Action<span class="token operator">?</span> OnChange<span class="token punctuation">;</span>

    <span class="token keyword">public</span> List<span class="token operator">&lt;</span>PriceQuote<span class="token operator">&gt;</span> Quotes <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> DateTimeOffset LastUpdated <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">UpdateQuotes</span><span class="token punctuation">(</span>List<span class="token operator">&lt;</span>PriceQuote<span class="token operator">&gt;</span> quotes<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        Quotes <span class="token operator">=</span> quotes<span class="token punctuation">;</span>
        LastUpdated <span class="token operator">=</span> DateTimeOffset<span class="token punctuation">.</span>UtcNow<span class="token punctuation">;</span>
        OnChange<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">Invoke</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>And then you can subscribe to the <code>OnChange</code> event when that data changes:</p><p><strong>PriceWatcher.razor</strong></p><pre class=" language-html"><code class="prism  language-html">@implements IDisposable
@inject PriceState State

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>Last updated: @State.LastUpdated<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>

<span class="token comment">&lt;!-- render price data here --&gt;</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp">@code <span class="token punctuation">{</span>
    <span class="token keyword">protected</span> <span class="token keyword">override</span> <span class="token keyword">void</span> <span class="token function">OnInitialized</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        State<span class="token punctuation">.</span>OnChange <span class="token operator">+</span><span class="token operator">=</span> HandleStateChanged<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">HandleStateChanged</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token function">InvokeAsync</span><span class="token punctuation">(</span>StateHasChanged<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        State<span class="token punctuation">.</span>OnChange <span class="token operator">-</span><span class="token operator">=</span> HandleStateChanged<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Important note: WASM is single-threaded. BackgroundService gives you a convenient way to abstract background tasks, but not true parallelism.</p><p>I/O-bound work (polling, fetching) is typically fine because it&rsquo;s mostly waiting (<code>await</code>), which frees the thread up in the meantime. But CPU-heavy work would block the UI.</p><p>But fear not, there&rsquo;s a solution for that too!</p><h2 id="web-worker-template">Web Worker Template</h2><p>For anything CPU-heavy, you want to keep the main thread (which also handles the UI) free.</p><p>.NET 11 introduces <code>dotnet new webworker</code>, which scaffolds a Razor class library with the JS plumbing needed to run .NET code in a browser web worker. Modern web browsers support web workers as a convenient way to run tasks completely separately from your main application&rsquo;s thread.</p><p>The template will create a <code>WebWorkerClient</code> class which uses a factory pattern to create worker instances. Then you can define your worker methods using <code>[JSExport]</code>.</p><p>Here&rsquo;s an example, which computes primes (a CPU-heavy task).</p><p><strong>PrimesWorker.cs</strong></p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token punctuation">[</span><span class="token function">SupportedOSPlatform</span><span class="token punctuation">(</span><span class="token string">"browser"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">partial</span> <span class="token keyword">class</span> <span class="token class-name">PrimesWorker</span>
<span class="token punctuation">{</span>
    <span class="token punctuation">[</span>JSExport<span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">string</span> <span class="token function">ComputePrimes</span><span class="token punctuation">(</span><span class="token keyword">int</span> limit<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token comment">// CPU-intensive work runs on the worker thread</span>
        <span class="token comment">// UI stays responsive</span>
        <span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token function">SieveOfEratosthenes</span><span class="token punctuation">(</span>limit<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">return</span> JsonSerializer<span class="token punctuation">.</span><span class="token function">Serialize</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">PrimeResult</span><span class="token punctuation">(</span>primes<span class="token punctuation">.</span>Count<span class="token punctuation">,</span> primes<span class="token punctuation">.</span><span class="token function">TakeLast</span><span class="token punctuation">(</span><span class="token number">5</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> sw<span class="token punctuation">.</span>ElapsedMilliseconds<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token keyword">public</span> record <span class="token function">PrimeResult</span><span class="token punctuation">(</span><span class="token keyword">int</span> Count<span class="token punctuation">,</span> <span class="token keyword">int</span><span class="token punctuation">[</span><span class="token punctuation">]</span> LastFive<span class="token punctuation">,</span> <span class="token keyword">long</span> ElapsedMs<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>You can invoke this method from a component (via a web worker).</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">private</span> <span class="token keyword">const</span> <span class="token keyword">int</span> limit <span class="token operator">=</span> 50_000_000<span class="token punctuation">;</span>

<span class="token keyword">var</span> worker <span class="token operator">=</span> <span class="token keyword">await</span> WebWorkerClient<span class="token punctuation">.</span><span class="token function">CreateAsync</span><span class="token punctuation">(</span>JSRuntime<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> result <span class="token operator">=</span> <span class="token keyword">await</span> worker<span class="token punctuation">.</span><span class="token generic-method function">InvokeAsync<span class="token punctuation">&lt;</span>PrimeResult<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>
    <span class="token string">"WebWorkerAppDemo.PrimesWorker.ComputePrimes"</span><span class="token punctuation">,</span>
    args<span class="token punctuation">:</span> <span class="token punctuation">[</span>limit<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>If you were to run that same prime number code directly in a component, it would lock UI updates for as long as it&rsquo;s running. Try it with the above approach (using web workers), and the UI will stay entirely responsive throughout.</p><p>Some important notes about web workers:</p><ul><li>Class must be static partial with <code>[SupportedOSPlatform("browser")]</code>.</li><li>Return types: primitives or strings only (complex types must be serialized to JSON).</li><li>Your project needs <code>&lt;AllowUnsafeBlocks&gt;true&lt;/AllowUnsafeBlocks&gt;</code> set in its <code>csproj</code>.</li><li>The worker loads a second .NET runtime, so startup is slow but compute is genuinely parallel.</li><li>Template produces a class library, which you can then reference from your main project.</li></ul><h2 id="relative-navigation">Relative Navigation</h2><p>This is one of those &ldquo;if you know, you know&rdquo; challenges.</p><p>In previous versions of .NET, when you want to render a relative link, things got tricky. Say you wanted a link to an FAQ page at <code>/faq</code>. .NET would assume you wanted to navigate to <code>/faq</code> at the app root. But if you were already on <code>/docs/</code>, you might well have wanted that link to go to <code>/docs/faq</code>.</p><p>The workaround up to now has been to use hardcoded full paths everywhere, which is asking for trouble when you start moving things around.</p><p><code>NavigationManager.NavigateTo()</code> and <code>NavLink</code> now accept <code>RelativeToCurrentUri</code>, which, when set to <code>true</code>, means relative paths will resolve against the current page path instead of the app root:</p><pre class=" language-html"><code class="prism  language-html">@page "/docs/getting-started"

@* .NET 11 - resolves to /docs/getting-started/overview *@
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>NavLink</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>overview<span class="token punctuation">"</span></span> <span class="token attr-name">RelativeToCurrentUri</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Overview<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>NavLink</span><span class="token punctuation">&gt;</span></span>

@* Before - had to hardcode the full path *@
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>NavLink</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/docs/getting-started/overview<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Overview<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>NavLink</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>Programmatic navigation:</p><pre class=" language-csharp"><code class="prism  language-csharp">Navigation<span class="token punctuation">.</span><span class="token function">NavigateTo</span><span class="token punctuation">(</span><span class="token string">"faq"</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">NavigationOptions</span>
<span class="token punctuation">{</span>
    RelativeToCurrentUri <span class="token operator">=</span> <span class="token keyword">true</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Note: Path must not have a leading slash for relative resolution (&ldquo;overview&rdquo; not &ldquo;/overview&rdquo;).</p><h2 id="displayname-and-label-components">DisplayName and Label Components</h2><p>This may well be one of those features that leaves you wondering how it took so long to appear, but Blazor now has a <code>Label</code> component (and a handy <code>DisplayName</code> one too).</p><p>Both components read display names from model attributes:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ProductModel</span>
<span class="token punctuation">{</span>
    <span class="token punctuation">[</span><span class="token function">Display</span><span class="token punctuation">(</span>Name <span class="token operator">=</span> <span class="token string">"Production Date"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>  <span class="token comment">// DisplayName and Label read this</span>
    <span class="token keyword">public</span> DateOnly<span class="token operator">?</span> ProductionDate <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">DisplayName</span><span class="token punctuation">(</span><span class="token string">"Unit Price (&pound;)"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>     <span class="token comment">// Also supported, but [Display] takes precedence</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> Price <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">int</span> StockCount <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>  <span class="token comment">// Falls back to raw property name</span>
<span class="token punctuation">}</span>
</code></pre><p>Here&rsquo;s how you can use them in a form:</p><pre class=" language-html"><code class="prism  language-html">@* Label wraps the input - accessible by default *@
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Label</span> <span class="token attr-name">For</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(() =&gt; Model.ProductionDate)<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>form-label<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>InputDate</span> <span class="token attr-name">@bind-Value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Model.ProductionDate<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>form-control<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Label</span><span class="token punctuation">&gt;</span></span>

@* DisplayName standalone *@
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span><span class="token punctuation">&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>DisplayName</span> <span class="token attr-name">For</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(() =&gt; Model.Price)<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>Previously you had to write your own <code>&lt;label&gt;</code> components, so this is a welcome addition.</p><h2 id="environmentboundary-component">EnvironmentBoundary Component</h2><p>Ever felt the need to show different information on the screen depending on which environment your app&rsquo;s running in? (Think UI that only shows up when you&rsquo;re running locally, or on staging.)</p><p>Now you can conditionally render content based on the hosting environment:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>EnvironmentBoundary</span> <span class="token attr-name">Include</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Development<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>Debug tools enabled<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>EnvironmentBoundary</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>EnvironmentBoundary</span> <span class="token attr-name">Exclude</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Production<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>Test banner<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>EnvironmentBoundary</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>EnvironmentBoundary</span> <span class="token attr-name">Include</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Staging,Production<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>Analytics script<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>EnvironmentBoundary</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>Previously, you had to inject <code>IWebHostEnvironment</code> and write manual conditional statements. This declarative approach is cleaner (note the matching of environment is case-insensitive).</p><h2 id="basepath-component">BasePath Component</h2><p>Another small but handy change.</p><p><code>&lt;BasePath /&gt;</code> in <code>App.razor</code> replaces the hardcoded <code>&lt;base href="https://www.telerik.com/"&gt;</code>:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">&gt;</span></span>   
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>BasePath</span> <span class="token punctuation">/&gt;</span></span>   
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>This will take the value from <code>NavigationManager.BaseUri</code> and essentially returns whatever <code>UsePathBase</code> sets (which you can configure in <strong>Program.cs</strong>).</p><p>This is handy if you&rsquo;re running your app on a subpath, as the base href will be automatically set to the correct value.</p><h2 id="signalr-configureconnection">SignalR ConfigureConnection</h2><p>If you need to configure your <code>SignalR</code> connection for Blazor Server, you now have easier config available via <code>AddInteractiveServerRenderMode</code>.</p><p>In .NET 11, it accepts a <code>ConfigureConnection</code> callback for the underlying SignalR connection:</p><pre class=" language-csharp"><code class="prism  language-csharp">app<span class="token punctuation">.</span><span class="token generic-method function">MapRazorComponents<span class="token punctuation">&lt;</span>App<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">AddInteractiveServerRenderMode</span><span class="token punctuation">(</span>options <span class="token operator">=</span><span class="token operator">&gt;</span>
    <span class="token punctuation">{</span>
        options<span class="token punctuation">.</span>ConfigureConnection <span class="token operator">=</span> connectionOptions <span class="token operator">=</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>
            connectionOptions<span class="token punctuation">.</span>ApplicationMaxBufferSize <span class="token operator">=</span> <span class="token number">512</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">;</span>
            connectionOptions<span class="token punctuation">.</span>TransportMaxBufferSize <span class="token operator">=</span> <span class="token number">512</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">;</span>
            connectionOptions<span class="token punctuation">.</span>Transports <span class="token operator">=</span> HttpTransportType<span class="token punctuation">.</span>WebSockets<span class="token punctuation">;</span>
          connectionOptions<span class="token punctuation">.</span>AllowStatefulReconnects <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
            connectionOptions<span class="token punctuation">.</span>CloseOnAuthenticationExpiration <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Previously, you had to resort to global SignalR hub config. Now you can target Blazor&rsquo;s hub specifically.</p><p>You&rsquo;ll probably find yourself spending time here if you run into errors because your components hold too much state, you want to tweak stateful reconnection logic or have other reasons to tweak how the SignalR part of Blazor Server works.</p><h2 id="what-to-watch">What to Watch</h2><p>That wraps up a quick tour of some of the highlights of .NET 11 Previews 1 and 2 for Blazor.</p><p>Here are a few things that look set to land in future previews:</p><ul><li><strong>SessionData for static SSR</strong> &ndash; The session-scoped counterpart to TempData</li><li><strong>Cache component</strong> &ndash; Component-level output caching</li><li><strong>Client-side validation without a circuit</strong> &ndash; No need to resort to Blazor Server for your statically rendered forms</li><li><strong>Aspire integration improvements</strong></li><li><strong>Virtualize with variable-height items</strong></li><li><strong><code>[SupplyParameterFromTempData]</code> attribute</strong></li></ul><p>.NET 11 will be released in November, with preview releases dropping every month until then.</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">Build Your Own AI Application Using Prebuilt Blazor Components</h4></div><div class="col-8"><p class="u-fs16 u-mb0">With <a target="_blank" href="https://www.telerik.com/blogs/build-your-own-ai-application-using-prebuilt-blazor-components">prebuilt components, you can easily build a Blazor application</a> that connects to AI model services for chat conversations. Check it out.</p></div></div></aside><img src="https://feeds.telerik.com/link/23050/17305972.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:64742007-b953-46a8-8374-913881b24dee</id>
    <title type="text">Blazor Basics: Implementing a Theme Switch in Blazor (Dark Mode)</title>
    <summary type="text">Learn how to implement dark mode and a theme switch for Blazor web applications using standardized CSS features and no JavaScript code.</summary>
    <published>2026-03-17T20:08:55Z</published>
    <updated>2026-07-07T01:11:20Z</updated>
    <author>
      <name>Claudio Bernasconi </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23050/17301106/blazor-basics-implementing-theme-switch-blazor-dark-mode"/>
    <content type="text"><![CDATA[<p><span class="featured">Learn how to implement dark mode and a theme switch for Blazor web applications using standardized CSS features and no JavaScript code.</span></p><p>In this article, we&rsquo;ll learn how to properly implement dark mode and a theme switch for Blazor web applications.</p><h2 id="introduction">Introduction</h2><p>We want to implement a minimal yet complete example of dark mode and a theme switch for Blazor web applications.</p><p>We will keep it simple and use pure CSS to style the application and Blazor to manage the state. We do not use any JavaScript code for this solution.</p><p><strong>Hint:</strong> Although the code used in the example project is based on the .NET 10 Blazor Web Application project template, which uses Bootstrap for its sample pages, we do not use Bootstrap or any other CSS library to implement the dark mode or the theme switch.</p><p>You can <a target="_blank" href="https://github.com/claudiobernasconi/BlazorDarkMode">access the code used in this example on GitHub</a>.</p><h2 id="the-concept">The Concept</h2><p>There are three core principles we want to follow:</p><ol><li>We define all colors as CSS variables.</li><li>We use a <code>.dark-theme</code> CSS class to override/replace those base colors.</li><li>We implement a Blazor component that toggles this CSS class at runtime.</li></ol><p>This solution works for <strong>Blazor Server</strong> and <strong>Blazor WebAssembly</strong>.</p><h2 id="defining-the-css-color-variables">Defining the CSS Color Variables</h2><p>In the <code>app.css</code> file or any other CSS file referenced in the <code>index.html</code> or <code>App.razor</code> file of your Blazor web application, we add the following CSS variable definitions:</p><pre class=" language-css"><code class="prism  language-css"><span class="token selector"><span class="token pseudo-class">:root</span> </span><span class="token punctuation">{</span>
    <span class="token property">--background-color</span><span class="token punctuation">:</span> <span class="token hexcode">#FFFFFF</span><span class="token punctuation">;</span>
    <span class="token property">--text-color</span><span class="token punctuation">:</span> <span class="token hexcode">#1F1F39</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.dark-theme</span> </span><span class="token punctuation">{</span>
    <span class="token property">--background-color</span><span class="token punctuation">:</span> <span class="token hexcode">#1F1F39</span><span class="token punctuation">;</span>
    <span class="token property">--text-color</span><span class="token punctuation">:</span> <span class="token hexcode">#FAFAFB</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p><strong>Hint:</strong> We keep it simple in this example and use only CSS variables for the background color and text color. In a real-world implementation, you might also want to define CSS variables for border colors, primary and secondary colors, etc.</p><h2 id="applying-the-color-variables">Applying the Color Variables</h2><p>Now that we defined the CSS variables for our desired colors, we need to apply those variables in CSS definitions. Remember that the variable definition above only defines the variables, but does not apply them.</p><pre class=" language-css"><code class="prism  language-css"><span class="token selector"><span class="token class">.theme</span> </span><span class="token punctuation">{</span>
    <span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--background-color<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--text-color<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Again, we keep it simple and apply the background color and the text color to the theme CSS class. We could go on and define colors for buttons, headings and other parts of the web application.</p><h2 id="adding-the-theme-state-to-the-layout-component">Adding the Theme State to the Layout Component</h2><p>Now that we have the basic building block for styling the component in place, we want to keep track of whether dark mode is in use.</p><p>A simple implementation alters the <code>MainLayout</code> component like this:</p><pre class=" language-html"><code class="prism  language-html">&lt;div class=@($"page theme {ThemeCSSClass}")&gt;
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>sidebar<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>NavMenu</span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>main</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>top-row px-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>btn btn-primary<span class="token punctuation">"</span></span> <span class="token attr-name">@onclick</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ToggleTheme<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                Toggle Theme
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://learn.microsoft.com/aspnet/core/<span class="token punctuation">"</span></span> <span class="token attr-name">target</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>_blank<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>About<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>article</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>content px-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            @Body
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>article</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>main</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>We add two CSS classes to the class list of the outer <code>div</code> element. In addition to the page CSS class used in the default project template, we also want to add the previously defined <code>theme</code> class, followed by a class name managed by the component.</p><p>We also add a <code>button</code> to toggle the theme.</p><p>We add the following behavior to the code section of the component:</p><pre class=" language-csharp"><code class="prism  language-csharp">@code <span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">bool</span> _darkModeEnabled <span class="token operator">=</span> <span class="token keyword">false</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">string</span> ThemeCSSClass <span class="token operator">=</span><span class="token operator">&gt;</span> _darkModeEnabled <span class="token operator">?</span> <span class="token string">"dark-theme"</span> <span class="token punctuation">:</span> <span class="token string">""</span><span class="token punctuation">;</span>

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">ToggleTheme</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _darkModeEnabled <span class="token operator">=</span> <span class="token operator">!</span>_darkModeEnabled<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The code defines a <code>_darkModeEnabled</code> variable that tracks the user&rsquo;s theme choice. We conditionally return <code>dark-theme</code> or an empty string for the <code>ThemeCSSClass</code> property, which is then rendered as part of the CSS class name list for the Layout component&rsquo;s outer <code>div</code>.</p><p>The <code>ToggleTheme</code> method is triggered when the user presses the <code>button</code> and wants to switch the theme. It flips the value of the <code>_darkModeEnabled</code> boolean variable.</p><h2 id="theming-in-action">Theming in Action</h2><p>The user can now toggle the light and dark themes using the button in the page header.</p><p><img title="Blazor web Application using light mode" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/blazor-light-mode.png?sfvrsn=d20bcc54_2" alt="A browser with a Blazor web application using a light mode theme with a dark text color and a white background." /></p><p><img title="Blazor web Application using light mode" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/blazor-dark-mode.png?sfvrsn=9b1faed_2" alt="A browser with a Blazor web application using a dark mode theme with a light text color and a dark background." /></p><h2 id="why-this-solution-works-well-in-blazor">Why This Solution Works Well in Blazor</h2><p>This solution leverages how Razor component rendering works. Whenever the state of a component changes, the component is re-rendered.</p><p>For the layout component, this means that whenever the user presses the button, the component&rsquo;s state changes, so it will be re-rendered. As part of the rendering process, the correct CSS classes will be applied.</p><p>A declarative user interface definition combined with state-driven component rendering is the strength of Blazor, and our solution therefore fits it perfectly.</p><h2 id="improving-the-user-experience">Improving the User Experience</h2><p>There are two main areas that could be improved to take our simple solution to the next level:</p><ul><li>The theme state is currently attached to the <code>MainLayout</code> component. It means that if the user closes the browser tab or refreshes the page, the state is lost. Persisting the theme choice using local storage is a great idea to keep the user experience consistent. Learn more about <a target="_blank" href="https://www.telerik.com/blogs/blazor-basics-accessing-browser-storage-blazor-web-applications">accessing the local storage for Blazor Server web applications</a> or how to use <a target="_blank" href="https://www.telerik.com/blogs/blazor-basics-webassembly-using-local-storage-offline-scenarios">local Storage in Blazor WebAssembly applications</a>.</li><li>You could respect the system preferences and apply them when a particular user starts the web application for the first time. CSS provides the <code>preferes-color-scheme</code> media feature. The code would look like this:</li></ul><pre class=" language-css"><code class="prism  language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
    <span class="token selector"><span class="token pseudo-class">:root</span> </span><span class="token punctuation">{</span>
        <span class="token comment">/* variable definitions */</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> light<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
    <span class="token selector"><span class="token pseudo-class">:root</span> </span><span class="token punctuation">{</span>
        <span class="token comment">/* variable definitions */</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h2 id="the-limitations-of-this-approach">The Limitations of This Approach</h2><p>As shown in the previous chapter on improving the user experience of our simple solutions, our approach is basic and doesn&rsquo;t scale well. The following imminent limitations come to mind:</p><ul><li>Handling <strong>system preferences</strong> already adds complexity to our solution and requires more code to cleanly implement the expected behavior.</li><li>CSS variables and definitions only <strong>cascade down the DOM tree</strong>. In our example, we add the themed CSS classes to the MainLayout component. Components rendered outside the <code>MainLayout</code> component element tree are out of scope for theming and must be treated the same way (by adding the <code>theme</code> and <code>dark-mode</code> CSS classes).</li><li>Changing colors and adjusting the theme requires altering <strong>global CSS definitions</strong>, which is hard to test and can potentially have unexpected side effects. Also, making sure to apply the CSS variables in the correct applicable CSS classes doesn&rsquo;t scale.</li><li><strong>Third-party components</strong> will not automatically apply the custom CSS classes defined in the application. Meaning that if you integrate third-party components, you will need to implement additional code to integrate them with your custom theming implementation.</li><li>Implementing <strong>multiple themes</strong> becomes even harder to maintain. Working with light and dark mode should not add too much complexity, but maintaining three or four, or even 10 different themes becomes much harder.</li><li>You have to verify the selected colors work together and <strong>meet accessibility requirements</strong> (e.g., color contrast). And let&rsquo;s be honest&mdash;not all developers are good designers.</li></ul><p>For a small website, the approach shown in this article will probably work well. For larger web applications consisting of hundreds of pages and components, it can become a maintenance nightmare if the implementation is not carefully architected to properly handle state changes and apply the correct CSS classes for each component during rendering.</p><h2 id="how-telerik-ui-for-blazor-solves-theming-at-scale">How Telerik UI for Blazor Solves Theming at Scale</h2><p>In modern large-scale web applications, theming is important and helps meet accessibility requirements and expected user experience standards.</p><p>The Progress Telerik UI for <a target="_blank" href="https://www.telerik.com/blazor-ui">Blazor component library</a> provides a <a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/styling-and-themes/themebuilder">Blazor ThemeBuilder</a> tool that allows visual customization (color previews), SCSS-based design tokens and built-in dark and light themes.</p><p>And most importantly, the themes are applied consistently across all components.</p><p>It is still important to understand how theming works under the hood, but for a professional web application, using a professionally implemented theming system can prevent headaches and reduce the amount of custom code required. Plus, accessibility features are baked in.</p><p>Learn more about theming from <a target="_blank" href="https://www.telerik.com/blogs/author/peter-vogel">Peter Vogel</a> in the <a target="_blank" href="https://www.telerik.com/blogs/themes-magic-telerik-ui-blazor">Themes Magic in Telerik UI for Blazor</a> article.</p><h2 id="conclusion">Conclusion</h2><p>We learned how to implement a simple solution for a theme switch and light and dark mode in a Blazor web application. This basic solution works for Blazor Server and Blazor WebAssembly because it uses standardized CSS features and no JavaScript.</p><p>We learned how to further improve the solution and what limitations it will face for a large-scale web application.</p><p>Professionally implemented user interface component libraries, such as <a target="_blank" href="https://www.telerik.com/blazor-ui">Telerik UI for Blazor</a>, implement complex theming systems that we can leverage to get around those limitations.</p><hr /><p>If you want to learn more about Blazor development, watch my <a target="_blank" href="https://www.youtube.com/playlist?list=PLwISgxnkpZGL_LhTQCWwp-WCzupv7lcp0">free Blazor Crash Course</a> on YouTube. And stay tuned to the Telerik blog for more <a target="_blank" href="https://www.telerik.com/blogs/tag/blazor-basics">Blazor Basics</a>.</p><img src="https://feeds.telerik.com/link/23050/17301106.gif" height="1" width="1"/>]]></content>
  </entry>
</feed>
