<?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>
  <feedpress:newsletterId>telerik-blogs-web</feedpress:newsletterId>
  <link rel="hub" href="https://feedpress.superfeedr.com/"/>
  <logo>https://static.feedpress.com/logo/telerik-blogs--web-5ab52bef0a72f.jpg</logo>
  <title type="text">Telerik Blogs | Web</title>
  <subtitle type="text">The official blog of Progress Telerik - expert articles and tutorials for developers.</subtitle>
  <id>uuid:e65c1dcf-771c-4336-90c8-832dab84ccc6;id=119</id>
  <updated>2026-09-10T05:05:50Z</updated>
  <link rel="alternate" href="https://www.telerik.com/"/>
  <link rel="self" type="application/atom+xml" href="https://feeds.telerik.com/blogs/web"/>
  <entry>
    <id>urn:uuid:902e921a-cf3c-4c89-9dfb-3020b23f59ae</id>
    <title type="text">Decision Guide for Angular State Management: Signals, Services, SignalStore or NgRx?</title>
    <summary type="text">When and why should you use Signals, Services, SignalStore or NgRx to manage state in your Angular app?</summary>
    <published>2026-09-09T17:20:44Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Dany Paredes </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17443669/decision-guide-angular-state-management-signals-services-signalstore-ngrx"/>
    <content type="text"><![CDATA[<p><span class="featured">When and why should you use Signals, Services, SignalStore or NgRx to manage state in your Angular app?</span></p><p>This is the guide I wish I had before starting my first Angular project with state management.</p><p>Every developer needs to decide where data lives, how it changes and how different parts of the UI stay in sync. Get this wrong, and your app could become a mess of scattered data, broken UIs, and hours of debugging. Get it right, and your code stays clean, predictable and easy to change.</p><p>Most articles are outdated. They still push NgRx Store as the default answer or ignore Signals entirely. This guide is different. It covers the modern landscape: Signals, service stores, SignalStore and classic NgRx. It shows when each option makes sense and when to keep things simple.</p><p>We use examples from an accounting dashboard to make each concept concrete, so you can see how these patterns work in a real application.</p><blockquote><p><strong>Follow along:</strong> All the code for this dashboard is available on <a target="_blank" href="https://github.com/danywalls/accounting-dashboard-ngrx">GitHub</a>. Clone it and experiment while you read.</p></blockquote><h2 id="what-is-state-management-in-angular">What Is State Management in Angular?</h2><p>State management in practical terms means deciding how your app handles different types of data:</p><ul><li><strong>UI state</strong> &ndash; The search text a user types in a list. Only one component needs it.</li><li><strong>Server state</strong> &ndash; Data fetched from an API, like a list of bank accounts.</li><li><strong>Form state</strong> &ndash; User input before it is submitted.</li><li><strong>Shared application state</strong> &ndash; Data many parts of the app need, like the current user or an open financial period.</li></ul><p>Why does this matter more as Angular apps grow? More components will mean more async flows, more shared data and more chances for inconsistency. Without clear rules, one component might delete data another component needs, or two components might try to change the same data at once.</p><p>Consider concrete examples: authentication state controls what every component shows. Shopping cart state is shared across a product list, a cart view and a checkout form. Selected filters affect both a list and a detail view. Loading and error states need coordination across features. Each of these needs a different level of state management.</p><h3 id="why-angular-state-management-feels-different-nowadays">Why Angular State Management Feels Different Nowadays</h3><p><a target="_blank" href="https://angular.dev/guide/signals">Signals</a> changed things by making reactivity part of Angular itself. Before Signals, you had to pick between <a target="_blank" href="https://rxjs.dev/api/index/class/BehaviorSubject"><code>BehaviorSubject</code></a> in a service or use NgRx. Now you have <a target="_blank" href="https://angular.dev/guide/signals"><code>signal()</code></a>, <a target="_blank" href="https://angular.dev/guide/signals"><code>computed()</code></a> and <a target="_blank" href="https://angular.dev/guide/signals"><code>effect()</code></a> as built-in tools.</p><p>Modern Angular advice is less about libraries than before. In the past the answer was often &ldquo;just use NgRx.&rdquo; Now the community says: start simple and add complexity only when you need it. Most developers on Reddit and forums are practical. They use services with Signals or RxJS for shared state. They use NgRx only for large apps with complex async workflows.</p><p>This guide follows the current Angular and NgRx direction. Signals, service stores and SignalStore are the new normal.</p><h2 id="the-tiered-approach-to-state-management">The Tiered Approach to State Management</h2><p>Think of state in three levels: local component state, shared feature state and global application state.</p><p>The best approach is to start small. Move to a bigger solution only when you need to share data or coordinate across components. Different parts of the same app can use different approaches.</p><h3 id="local-component-state-with-signals">Local Component State with Signals</h3><p>When data lives in only one component, use <a target="_blank" href="https://angular.dev/guide/signals"><code>signal()</code></a>. Angular&rsquo;s change detection works with Signals by default. Components only update when a signal changes or a new input arrives. This makes <code>signal()</code> a good fit for local state: fast, simple and direct.</p><p>In our accounting dashboard, a journal list with a search filter is a good example. The search text belongs to this component alone. The <a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/inputs/textbox/"><code>&lt;kendo-textbox&gt;</code></a> gives us a styled search input with no CSS work.</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token keyword">import</span> <span class="token punctuation">{</span> Component<span class="token punctuation">,</span> signal<span class="token punctuation">,</span> computed <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> CurrencyPipe <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/common'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> KENDO_TEXTBOX <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@progress/kendo-angular-inputs'</span><span class="token punctuation">;</span>

<span class="token keyword">interface</span> <span class="token class-name">JournalEntry</span> <span class="token punctuation">{</span>
  id<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
  date<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  description<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  total<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@<span class="token function">Component</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  selector<span class="token punctuation">:</span> <span class="token string">'app-journal-list'</span><span class="token punctuation">,</span>
  standalone<span class="token punctuation">:</span> <span class="token keyword">true</span><span class="token punctuation">,</span>
  imports<span class="token punctuation">:</span> <span class="token punctuation">[</span>CurrencyPipe<span class="token punctuation">,</span> KENDO_TEXTBOX<span class="token punctuation">]</span><span class="token punctuation">,</span>
  template<span class="token punctuation">:</span> <span class="token template-string"><span class="token string">`
    &lt;kendo-textbox
      [value]="searchTerm()"
      (valueChange)="searchTerm.set($event)"
      placeholder="Search entries..."
      [clearButton]="true"
    /&gt;

    &lt;p&gt;Showing {{ filteredCount() }} of {{ entries().length }} entries&lt;/p&gt;

    @for (entry of filteredEntries(); track entry.id) {
      &lt;div&gt;
        {{ entry.date }} - {{ entry.description }}: {{ entry.total | currency }}
      &lt;/div&gt;
    } @empty {
      &lt;p&gt;No entries found.&lt;/p&gt;
    }
  `</span></span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">JournalListComponent</span> <span class="token punctuation">{</span>
  readonly entries <span class="token operator">=</span> signal<span class="token operator">&lt;</span>JournalEntry<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
    <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> date<span class="token punctuation">:</span> <span class="token string">'2026-07-01'</span><span class="token punctuation">,</span> description<span class="token punctuation">:</span> <span class="token string">'Monthly Subscription Sale'</span><span class="token punctuation">,</span> total<span class="token punctuation">:</span> <span class="token number">150.00</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> date<span class="token punctuation">:</span> <span class="token string">'2026-07-02'</span><span class="token punctuation">,</span> description<span class="token punctuation">:</span> <span class="token string">'Office Supplies'</span><span class="token punctuation">,</span> total<span class="token punctuation">:</span> <span class="token number">45.50</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span> date<span class="token punctuation">:</span> <span class="token string">'2026-07-05'</span><span class="token punctuation">,</span> description<span class="token punctuation">:</span> <span class="token string">'Consulting Service'</span><span class="token punctuation">,</span> total<span class="token punctuation">:</span> <span class="token number">1200.00</span> <span class="token punctuation">}</span>
  <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  readonly searchTerm <span class="token operator">=</span> <span class="token function">signal</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  readonly filteredEntries <span class="token operator">=</span> <span class="token function">computed</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">entries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>e <span class="token operator">=&gt;</span>
      e<span class="token punctuation">.</span>description<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">searchTerm</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toLowerCase</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>
  readonly filteredCount <span class="token operator">=</span> <span class="token function">computed</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">filteredEntries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>No NgRx imports, no actions, no reducers. Two <code>signal()</code> calls and a <a target="_blank" href="https://angular.dev/guide/signals"><code>computed()</code></a>. That is it.</p><p><strong>Good for:</strong> Little code, easy to read, works with Angular change detection, fits local component state.</p><p><strong>Not good for:</strong> When many unrelated components need the same data. A search filter in one component is fine. A list of accounts that the entry form and the ledger both need is not.</p><h3 id="shared-feature-state-with-services">Shared Feature State with Services</h3><p>When multiple components need the same data, use a service with <a target="_blank" href="https://angular.dev/guide/signals">Signals</a>. This is the next step after local state.</p><p>The pattern is simple: the service keeps writable state in <code>private</code> fields. It exposes read-only Signals or Observables to the outside. Components call methods on the service instead of changing state directly. The <a target="_blank" href="https://angular.dev/guide/di"><code>@Service()</code></a> decorator makes the service available app-wide and works with <code>inject()</code>.</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token keyword">import</span> <span class="token punctuation">{</span> Service<span class="token punctuation">,</span> computed<span class="token punctuation">,</span> signal <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> httpResource <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/common/http'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">Account</span> <span class="token punctuation">{</span>
  code<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  name<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  <span class="token keyword">type</span><span class="token punctuation">:</span> <span class="token string">'asset'</span> <span class="token operator">|</span> <span class="token string">'liability'</span> <span class="token operator">|</span> <span class="token string">'equity'</span> <span class="token operator">|</span> <span class="token string">'revenue'</span> <span class="token operator">|</span> <span class="token string">'expense'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@<span class="token function">Service</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">AccountService</span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> readonly _accountsRes <span class="token operator">=</span> httpResource<span class="token operator">&lt;</span>Account<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">&gt;</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 string">'/api/accounts'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">private</span> readonly _localAccounts <span class="token operator">=</span> signal<span class="token operator">&lt;</span>Account<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
    <span class="token punctuation">{</span> code<span class="token punctuation">:</span> <span class="token string">'101'</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'Cash'</span><span class="token punctuation">,</span> <span class="token keyword">type</span><span class="token punctuation">:</span> <span class="token string">'asset'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> code<span class="token punctuation">:</span> <span class="token string">'102'</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'Accounts Receivable'</span><span class="token punctuation">,</span> <span class="token keyword">type</span><span class="token punctuation">:</span> <span class="token string">'asset'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> code<span class="token punctuation">:</span> <span class="token string">'401'</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'Sales Revenue'</span><span class="token punctuation">,</span> <span class="token keyword">type</span><span class="token punctuation">:</span> <span class="token string">'revenue'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> code<span class="token punctuation">:</span> <span class="token string">'501'</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'Operating Expenses'</span><span class="token punctuation">,</span> <span class="token keyword">type</span><span class="token punctuation">:</span> <span class="token string">'expense'</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> readonly _selectedCode <span class="token operator">=</span> signal<span class="token operator">&lt;</span><span class="token keyword">string</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  readonly accounts <span class="token operator">=</span> <span class="token function">computed</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 punctuation">[</span>
    <span class="token operator">...</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>_accountsRes<span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <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 punctuation">,</span>
    <span class="token operator">...</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_localAccounts</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>
  readonly isLoading <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_accountsRes<span class="token punctuation">.</span>isLoading<span class="token punctuation">;</span>
  readonly selectedCode <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_selectedCode<span class="token punctuation">.</span><span class="token function">asReadonly</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  readonly selectedAccount <span class="token operator">=</span> <span class="token function">computed</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">accounts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span>a <span class="token operator">=&gt;</span> a<span class="token punctuation">.</span>code <span class="token operator">===</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_selectedCode</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>

  readonly revenueAccounts <span class="token operator">=</span> <span class="token function">computed</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">accounts</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>a <span class="token operator">=&gt;</span> a<span class="token punctuation">.</span><span class="token keyword">type</span> <span class="token operator">===</span> <span class="token string">'revenue'</span><span class="token punctuation">)</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token function">selectAccount</span><span class="token punctuation">(</span>code<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>_selectedCode<span class="token punctuation">.</span><span class="token keyword">set</span><span class="token punctuation">(</span>code<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">addAccount</span><span class="token punctuation">(</span>account<span class="token punctuation">:</span> Account<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>_localAccounts<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>list <span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token operator">...</span>list<span class="token punctuation">,</span> account<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>Any component can inject <code>AccountService</code> and get the same data. The <a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/dropdowns/dropdownlist/"><code>&lt;kendo-dropdownlist&gt;</code></a> gives us search, keyboard navigation and disabled state with no extra code.</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token keyword">import</span> <span class="token punctuation">{</span> Component<span class="token punctuation">,</span> inject <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> KENDO_DROPDOWNLIST <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@progress/kendo-angular-dropdowns'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> AccountService <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../account.service'</span><span class="token punctuation">;</span>

@<span class="token function">Component</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  selector<span class="token punctuation">:</span> <span class="token string">'app-journal-entry'</span><span class="token punctuation">,</span>
  standalone<span class="token punctuation">:</span> <span class="token keyword">true</span><span class="token punctuation">,</span>
  imports<span class="token punctuation">:</span> <span class="token punctuation">[</span>KENDO_DROPDOWNLIST<span class="token punctuation">]</span><span class="token punctuation">,</span>
  template<span class="token punctuation">:</span> <span class="token template-string"><span class="token string">`
    &lt;h3&gt;New Journal Entry&lt;/h3&gt;

    &lt;label&gt;Revenue Account:&lt;/label&gt;
    &lt;kendo-dropdownlist
      [data]="revenueAccounts()"
      textField="name"
      valueField="code"
      [valuePrimitive]="true"
      [defaultItem]="{ code: '', name: 'Select account...' }"
      (valueChange)="onSelectAccount($event)"
    &gt;
    &lt;/kendo-dropdownlist&gt;

    @if (selectedAccount(); as acct) {
      &lt;p&gt;Selected: {{ acct.name }} - {{ acct.type }}&lt;/p&gt;
    }
  `</span></span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">JournalEntryComponent</span> <span class="token punctuation">{</span>
  <span class="token keyword">private</span> readonly accountService <span class="token operator">=</span> <span class="token function">inject</span><span class="token punctuation">(</span>AccountService<span class="token punctuation">)</span><span class="token punctuation">;</span>

  readonly revenueAccounts <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>accountService<span class="token punctuation">.</span>revenueAccounts<span class="token punctuation">;</span>
  readonly selectedAccount <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>accountService<span class="token punctuation">.</span>selectedAccount<span class="token punctuation">;</span>

  <span class="token function">onSelectAccount</span><span class="token punctuation">(</span>code<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>code<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">this</span><span class="token punctuation">.</span>accountService<span class="token punctuation">.</span><span class="token function">selectAccount</span><span class="token punctuation">(</span>code<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><strong>Use this when:</strong> State is shared by 2 to 10 components in the same feature. Examples: chart of accounts, user settings for a module or filters shared between a list and a detail view.</p><p><strong>Move up when:</strong> You need strict rules, computed state with complex logic or DevTools for debugging. That is when SignalStore helps.</p><h4 id="signals-vs.-behaviorsubject-for-service-stores">Signals vs. BehaviorSubject for Service Stores</h4><p>Which one should you use, <a target="_blank" href="https://angular.dev/guide/signals"><code>signal()</code></a> or <a target="_blank" href="https://rxjs.dev/api/index/class/BehaviorSubject"><code>BehaviorSubject</code></a>?</p><table><style>table, th, td {
  border: 1px;
  border-color: #bdbdba;
  border-style: dotted;
  border-collapse: collapse;
  margin-right: auto;
  padding:0in 5.4pt 0in 5.4pt;
  text-align :left;
}
</style>
<thead><tr><th>Concern</th><th><code>signal()</code></th><th><code>BehaviorSubject</code></th></tr></thead><tbody><tr><td>Read current value</td><td><code>items()</code></td><td><code>items$.value</code></td></tr><tr><td>Derived values</td><td><a target="_blank" href="https://angular.dev/guide/signals"><code>computed()</code></a></td><td><code>pipe(map(...))</code></td></tr><tr><td>Template syntax</td><td><code>{{ items() }}</code></td><td><code>{{ items$ | async }}</code></td></tr><tr><td>Change detection</td><td>Automatic</td><td>Needs <code>OnPush</code> or <code>async</code> pipe</td></tr><tr><td>Async composition</td><td>Needs <a target="_blank" href="https://ngrx.io/guide/signals/signal-store/rxjs-integration"><code>rxMethod</code></a></td><td><code>switchMap</code>, <code>combineLatest</code></td></tr></tbody></table><br /><p>Use <a target="_blank" href="https://rxjs.dev">RxJS</a> when you need stream features like debounce, cancellation or WebSocket events. For most synchronous state, Signals are simpler.</p><p>For new code, Signals are the better default. They are simpler, synchronous and work well with Angular&rsquo;s change detection.</p><h3 id="ngrx-signalstore-and-signalstate">NgRx SignalStore and SignalState</h3><p><a target="_blank" href="https://ngrx.io/guide/signals/signal-store">SignalStore</a> gives you structure without the extra code of classic NgRx. It is the recommended state management for Angular apps (NgRx team, v19+).</p><p>A simple service works for basic state. But as your feature grows, you want computed values, loading states and a consistent way to update data. Without these, different team members might use different patterns.</p><p>SignalStore is between a service and a full Redux store. Think of it as a service with clear rules.</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token keyword">import</span> <span class="token punctuation">{</span> signalStore<span class="token punctuation">,</span> withState<span class="token punctuation">,</span> withComputed<span class="token punctuation">,</span> withMethods<span class="token punctuation">,</span> patchState<span class="token punctuation">,</span> withHooks <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@ngrx/signals'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> computed<span class="token punctuation">,</span> inject <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> HttpClient <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/common/http'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> rxMethod <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@ngrx/signals/rxjs-interop'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> tapResponse <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@ngrx/operators'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> pipe<span class="token punctuation">,</span> switchMap<span class="token punctuation">,</span> tap <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'rxjs'</span><span class="token punctuation">;</span>

<span class="token keyword">interface</span> <span class="token class-name">JournalLine</span> <span class="token punctuation">{</span>
  accountCode<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  debit<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
  credit<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">interface</span> <span class="token class-name">JournalEntry</span> <span class="token punctuation">{</span>
  id<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
  date<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  description<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  lines<span class="token punctuation">:</span> JournalLine<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">type</span> JournalState <span class="token operator">=</span> <span class="token punctuation">{</span>
  entries<span class="token punctuation">:</span> JournalEntry<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  isLoading<span class="token punctuation">:</span> <span class="token keyword">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> initialState<span class="token punctuation">:</span> JournalState <span class="token operator">=</span> <span class="token punctuation">{</span>
  entries<span class="token punctuation">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
      date<span class="token punctuation">:</span> <span class="token string">'2026-07-01'</span><span class="token punctuation">,</span>
      description<span class="token punctuation">:</span> <span class="token string">'Initial Opening Balance'</span><span class="token punctuation">,</span>
      lines<span class="token punctuation">:</span> <span class="token punctuation">[</span>
        <span class="token punctuation">{</span> accountCode<span class="token punctuation">:</span> <span class="token string">'101'</span><span class="token punctuation">,</span> debit<span class="token punctuation">:</span> <span class="token number">5000</span><span class="token punctuation">,</span> credit<span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span> accountCode<span class="token punctuation">:</span> <span class="token string">'301'</span><span class="token punctuation">,</span> debit<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> credit<span class="token punctuation">:</span> <span class="token number">5000</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>
      id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
      date<span class="token punctuation">:</span> <span class="token string">'2026-07-08'</span><span class="token punctuation">,</span>
      description<span class="token punctuation">:</span> <span class="token string">'Service Revenue Recognition'</span><span class="token punctuation">,</span>
      lines<span class="token punctuation">:</span> <span class="token punctuation">[</span>
        <span class="token punctuation">{</span> accountCode<span class="token punctuation">:</span> <span class="token string">'102'</span><span class="token punctuation">,</span> debit<span class="token punctuation">:</span> <span class="token number">1200</span><span class="token punctuation">,</span> credit<span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span> accountCode<span class="token punctuation">:</span> <span class="token string">'401'</span><span class="token punctuation">,</span> debit<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> credit<span class="token punctuation">:</span> <span class="token number">1200</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>
  isLoading<span class="token punctuation">:</span> <span class="token keyword">false</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">totalDebits</span><span class="token punctuation">(</span>entries<span class="token punctuation">:</span> JournalEntry<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">number</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> entries<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span>sum<span class="token punctuation">,</span> e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
    sum <span class="token operator">+</span> e<span class="token punctuation">.</span>lines<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span> l<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> s <span class="token operator">+</span> l<span class="token punctuation">.</span>debit<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">function</span> <span class="token function">totalCredits</span><span class="token punctuation">(</span>entries<span class="token punctuation">:</span> JournalEntry<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">number</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> entries<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span>sum<span class="token punctuation">,</span> e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
    sum <span class="token operator">+</span> e<span class="token punctuation">.</span>lines<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span><span class="token punctuation">(</span>s<span class="token punctuation">,</span> l<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> s <span class="token operator">+</span> l<span class="token punctuation">.</span>credit<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> JournalStore <span class="token operator">=</span> <span class="token function">signalStore</span><span class="token punctuation">(</span>
  <span class="token punctuation">{</span> providedIn<span class="token punctuation">:</span> <span class="token string">'root'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>

  <span class="token function">withState</span><span class="token punctuation">(</span>initialState<span class="token punctuation">)</span><span class="token punctuation">,</span>

  <span class="token function">withComputed</span><span class="token punctuation">(</span><span class="token punctuation">(</span>store<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>
    entryCount<span class="token punctuation">:</span> <span class="token function">computed</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> store<span class="token punctuation">.</span><span class="token function">entries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">,</span>
    totalDebits<span class="token punctuation">:</span> <span class="token function">computed</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 function">totalDebits</span><span class="token punctuation">(</span>store<span class="token punctuation">.</span><span class="token function">entries</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>
    totalCredits<span class="token punctuation">:</span> <span class="token function">computed</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 function">totalCredits</span><span class="token punctuation">(</span>store<span class="token punctuation">.</span><span class="token function">entries</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>
    isBalanced<span class="token punctuation">:</span> <span class="token function">computed</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 function">totalDebits</span><span class="token punctuation">(</span>store<span class="token punctuation">.</span><span class="token function">entries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token function">totalCredits</span><span class="token punctuation">(</span>store<span class="token punctuation">.</span><span class="token function">entries</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 punctuation">)</span><span class="token punctuation">,</span>

  <span class="token function">withMethods</span><span class="token punctuation">(</span><span class="token punctuation">(</span>store<span class="token punctuation">,</span> http <span class="token operator">=</span> <span class="token function">inject</span><span class="token punctuation">(</span>HttpClient<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>
    loadEntries<span class="token punctuation">:</span> rxMethod<span class="token operator">&lt;</span><span class="token keyword">void</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>
      <span class="token function">pipe</span><span class="token punctuation">(</span>
        <span class="token function">tap</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 function">patchState</span><span class="token punctuation">(</span>store<span class="token punctuation">,</span> <span class="token punctuation">{</span> isLoading<span class="token punctuation">:</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 function">switchMap</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
          http<span class="token punctuation">.</span><span class="token keyword">get</span><span class="token operator">&lt;</span>JournalEntry<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token string">'/api/journal-entries'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>
            <span class="token function">tapResponse</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
              next<span class="token punctuation">:</span> <span class="token punctuation">(</span>entries<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">patchState</span><span class="token punctuation">(</span>store<span class="token punctuation">,</span> <span class="token punctuation">{</span> entries<span class="token punctuation">,</span> isLoading<span class="token punctuation">:</span> <span class="token keyword">false</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
              error<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">patchState</span><span class="token punctuation">(</span>store<span class="token punctuation">,</span> <span class="token punctuation">{</span> isLoading<span class="token punctuation">:</span> <span class="token keyword">false</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 punctuation">)</span>
    <span class="token punctuation">)</span><span class="token punctuation">,</span>

    <span class="token function">addEntry</span><span class="token punctuation">(</span>entry<span class="token punctuation">:</span> JournalEntry<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">patchState</span><span class="token punctuation">(</span>store<span class="token punctuation">,</span> <span class="token punctuation">{</span> entries<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token operator">...</span>store<span class="token punctuation">.</span><span class="token function">entries</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> entry<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 punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>

  <span class="token function">withHooks</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    <span class="token function">onInit</span><span class="token punctuation">(</span>store<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      store<span class="token punctuation">.</span><span class="token function">loadEntries</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 punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p><a target="_blank" href="https://ngrx.io/guide/signals/signal-store#signalstate">SignalState</a> is the lighter option. It has state and computed values but no methods or hooks. Use it when you want signal-based state in a service without the full store structure.</p><p>SignalStore also supports <a target="_blank" href="https://ngrx.io/guide/signals/signal-store/entity-management">entity management</a> with <code>withEntities()</code> for CRUD operations and the <a target="_blank" href="https://ngrx.io/guide/signals/signal-store/events">Events Plugin</a> for cross-store communication. These features make SignalStore a good fit for features that need structure but would be too complex with classic NgRx.</p><h3 id="classic-ngrx-store">Classic NgRx Store</h3><p>The <a target="_blank" href="https://ngrx.io/guide/store">classic NgRx Store</a> (actions, reducers, selectors, effects) is best for very complex data. Use it when you need a clear history of changes and strict patterns, even if it means more code.</p><p>This approach is useful when:</p><ul><li>Many features share a lot of connected data.</li><li>Your team needs Redux DevTools for debugging.</li><li>You need a record of every state change.</li><li>You have complex async workflows with multiple services.</li></ul><p>In the accounting dashboard, managing monthly periods fits here. Opening and closing a month affects entries, the ledger and reports.</p><p><a target="_blank" href="https://ngrx.io/guide/store"><code>createActionGroup</code></a> and <a target="_blank" href="https://ngrx.io/guide/store"><code>props</code></a> come from <code>@ngrx/store</code>:</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token keyword">import</span> <span class="token punctuation">{</span> createActionGroup<span class="token punctuation">,</span> props <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@ngrx/store'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> PeriodActions <span class="token operator">=</span> <span class="token function">createActionGroup</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  source<span class="token punctuation">:</span> <span class="token string">'Financial Period'</span><span class="token punctuation">,</span>
  events<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">'Open Period'</span><span class="token punctuation">:</span> props<span class="token operator">&lt;</span><span class="token punctuation">{</span> periodId<span class="token punctuation">:</span> <span class="token keyword">number</span> <span class="token punctuation">}</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token string">'Close Period'</span><span class="token punctuation">:</span> props<span class="token operator">&lt;</span><span class="token punctuation">{</span> periodId<span class="token punctuation">:</span> <span class="token keyword">number</span> <span class="token punctuation">}</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token string">'Period Closed'</span><span class="token punctuation">:</span> props<span class="token operator">&lt;</span><span class="token punctuation">{</span> periodId<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span> closedAt<span class="token punctuation">:</span> <span class="token keyword">string</span> <span class="token punctuation">}</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token string">'Period Error'</span><span class="token punctuation">:</span> props<span class="token operator">&lt;</span><span class="token punctuation">{</span> error<span class="token punctuation">:</span> <span class="token keyword">string</span> <span class="token punctuation">}</span><span class="token operator">&gt;</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 punctuation">;</span>
</code></pre><p>Effects handle the async workflow using <a target="_blank" href="https://ngrx.io/guide/effects"><code>createEffect</code></a>, <a target="_blank" href="https://ngrx.io/guide/effects"><code>Actions</code></a> and <a target="_blank" href="https://ngrx.io/guide/effects"><code>ofType</code></a>:</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token keyword">import</span> <span class="token punctuation">{</span> createEffect<span class="token punctuation">,</span> Actions<span class="token punctuation">,</span> ofType <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@ngrx/effects'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> switchMap<span class="token punctuation">,</span> map<span class="token punctuation">,</span> catchError <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'rxjs/operators'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> <span class="token keyword">of</span> <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'rxjs'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> closePeriod$ <span class="token operator">=</span> <span class="token function">createEffect</span><span class="token punctuation">(</span>
  <span class="token punctuation">(</span>actions$ <span class="token operator">=</span> <span class="token function">inject</span><span class="token punctuation">(</span>Actions<span class="token punctuation">)</span><span class="token punctuation">,</span> ledgerService <span class="token operator">=</span> <span class="token function">inject</span><span class="token punctuation">(</span>LedgerService<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
    actions$<span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>
      <span class="token function">ofType</span><span class="token punctuation">(</span>PeriodActions<span class="token punctuation">.</span>closePeriod<span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token function">switchMap</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">{</span> periodId <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
        ledgerService<span class="token punctuation">.</span><span class="token function">closePeriod</span><span class="token punctuation">(</span>periodId<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">pipe</span><span class="token punctuation">(</span>
          <span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>closedAt<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> PeriodActions<span class="token punctuation">.</span><span class="token function">periodClosed</span><span class="token punctuation">(</span><span class="token punctuation">{</span> periodId<span class="token punctuation">,</span> closedAt <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
          <span class="token function">catchError</span><span class="token punctuation">(</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">of</span><span class="token punctuation">(</span>PeriodActions<span class="token punctuation">.</span><span class="token function">periodError</span><span class="token punctuation">(</span><span class="token punctuation">{</span> error<span class="token punctuation">:</span> err<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>
      <span class="token punctuation">)</span>
    <span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> functional<span class="token punctuation">:</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>This is more code than SignalStore. The benefit is a clear history. Every time a month is closed, it is recorded as an action with a timestamp. <a target="_blank" href="https://ngrx.io/guide/store-devtools">Redux DevTools</a> shows exactly what happened.</p><p><strong>Use classic NgRx only for complex coordination problems.</strong> SignalStore is enough for most features. NgRx gives you predictability, devtools and clear data flow. But it has more code and a harder learning curve. <a target="_blank" href="https://ngrx.io">NgRx</a> is powerful, but that&rsquo;s not always needed.</p><h2 id="other-libraries-and-why-they-rank-lower-in-modern-guidance">Other Libraries and Why They Rank Lower in Modern Guidance</h2><p><a target="_blank" href="https://github.com/salesforce/akita">Akita</a> and <a target="_blank" href="https://www.ngxs.io/">NGXS</a> still appear in older comparison articles. But Akita is now <a target="_blank" href="https://github.com/salesforce/akita?tab=readme-ov-file#-deprecated">deprecated</a> and should not be used for new projects. NGXS is still maintained but its community has not grown like NgRx, and it has less adoption in the Angular ecosystem.</p><p>For most projects today, the choice is between a Signal-based service, SignalStore and classic NgRx Store. These options have the widest community support, more shared knowledge and best alignment with Angular&rsquo;s future direction.</p><h2 id="how-to-choose-the-right-state-strategy">How to Choose the Right State Strategy</h2><p>There is no single answer that works for every case. Ask these questions:</p><table><style>table, th, td {
  border: 1px;
  border-color: #bdbdba;
  border-style: dotted;
  border-collapse: collapse;
  margin-right: auto;
  padding:0in 5.4pt 0in 5.4pt;
  text-align :left;
}
</style>
<thead><tr><th>Question</th><th>Best Fit</th></tr></thead><tbody><tr><td>Does this data belong to one component only?</td><td>Local Signals</td></tr><tr><td>Is this data shared across 2-10 components?</td><td>Service with Signals</td></tr><tr><td>Do you want structure and rules but not full Redux?</td><td>SignalStore</td></tr><tr><td>Is this global data used across many features?</td><td>SignalStore or Classic Store</td></tr><tr><td>Do you need Redux DevTools and a clear history?</td><td>Classic Store</td></tr><tr><td>Is your data complex with many relationships?</td><td>Classic Store + <a target="_blank" href="https://ngrx.io/guide/entity">@ngrx/entity</a></td></tr></tbody></table><br /><p>Apply this framework to real examples:</p><ul><li><strong>Local modal state</strong> &ndash; A dialog open/close flag. Use a <code>signal(false)</code> in the component.</li><li><strong>Feature-level product filters</strong> &ndash; Filters shared between a product list and a detail view. Use a service with Signals.</li><li><strong>Global auth and entity cache</strong> &ndash; User session and a cache of business data. Classic NgRx Store gives you the predictability and tooling this level needs.</li></ul><p>Different parts of the same app can use different approaches. A dashboard might use local Signals for UI state, a service for feature state and classic NgRx for global coordination. This is not wrong. It is about using the right tool for each job.</p><h2 id="state-management-in-real-angular-architectures">State Management in Real Angular Architectures</h2><p>The best state strategy depends on more than just data scope. The size of your architecture and how your team works together often matter more than library popularity.</p><ul><li><strong>Enterprise apps</strong> &ndash; Large teams benefit from the structure of SignalStore or classic NgRx. Consistent patterns make it easier for new team members to learn the codebase.</li><li><strong>Design systems</strong> &ndash; Component libraries should not use global state. Use inputs, outputs and local Signals. Let the app that uses the library decide on shared state.</li><li><strong>Admin dashboards</strong> &ndash;Many widgets sharing data makes service stores and SignalStore a good fit. Each widget can use local Signals for its own UI state while reading shared data from a store.</li><li><strong>Micro-frontends and Nx-based architectures</strong> &ndash; Each micro-app should own its own state. Classic NgRx at the shell level handles cross-app coordination. SignalStore inside each micro-app keeps feature code clean.</li></ul><h2 id="signals-rxjs-and-ngrx-how-they-work-together">Signals, RxJS and NgRx: How They Work Together</h2><p>You do not have to choose only one. The modern Angular approach uses all three together.</p><ul><li><strong>Signals for local and view-model state.</strong> Use signals in components, computed for derived state and read-only signals from services. This covers most day-to-day state needs.</li><li><strong>RxJS for async and event streams.</strong> HTTP requests, WebSocket connections, debounced input and complex stream transformations are still good use cases for RxJS. Use <a target="_blank" href="https://ngrx.io/guide/signals/signal-store/rxjs-integration"><code>rxMethod</code></a> to connect RxJS with SignalStore.</li><li><strong>NgRx when you need structure or tooling.</strong> Classic NgRx Store and SignalStore give you conventions, DevTools and predictable data flow for the features that need them most.</li></ul><p>These tools work together. A component can use a Signal for local state, read from a SignalStore for feature data and dispatch classic NgRx actions for global coordination. The choice is practical, not about following rules.</p><h2 id="key-takeaways-and-next-steps">Key Takeaways and Next Steps</h2><ol><li><strong>Start with the simplest solution that works.</strong> Use <code>signal()</code> for local data. Move to a service when you need to share data. Use libraries only when the logic gets complex.</li><li><strong>Signals and services are the new default.</strong> Most data does not need a complex store.</li><li><strong>Use classic NgRx only for complex problems.</strong> If you need a clear history of changes or DevTools, the extra code is worth it.</li><li><strong>Mix different tools in the same app.</strong> Each part of the app can use the approach that fits best.</li></ol><p>Modern Angular state management is about choosing what fits, not about following strict rules. The right answer depends on your project size, team and needs.</p><p>To learn more, check <a target="_blank" href="https://angular.dev/guide/signals">Angular Signals</a>, the <a target="_blank" href="https://ngrx.io/guide/signals/signal-store">NgRx SignalStore guide</a>, <a target="_blank" href="https://ngrx.io/guide/component-store">ComponentStore migration</a>, <a target="_blank" href="https://rxjs.dev">RxJS</a> and architecture guides for large Angular projects.</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">What Enterprise Teams Actually Look for in an Angular UI Library</h4></div><div class="col-8"><p class="u-fs16 u-mb0">See what mattered when a real <a target="_blank" href="https://www.telerik.com/blogs/what-enterprise-teams-actually-look-for-angular-ui-library">enterprise team was exploring which Angular component library</a> to use, and what keeps them using Kendo UI.</p></div></div></aside><img src="https://feeds.telerik.com/link/10827/17443669.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:5890abe0-8c6a-4973-85a3-623de3e0b32f</id>
    <title type="text">Telerik Support by the Numbers: Response Times, Resolution Rates and Technical Expertise</title>
    <summary type="text">When a product or tool is a crucial part of your application, support is more than just a nice-to-have. Here’s how Progress Telerik support stacks up.</summary>
    <published>2026-09-09T14:31:35Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Kathryn Grayson Nanz </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17443547/telerik-support-response-times-resolution-rates-technical-expertise"/>
    <content type="text"><![CDATA[<p><span class="featured">When a product or tool is a crucial part of your application, support is more than just a nice-to-have. Here&rsquo;s how Progress Telerik support stacks up.</span></p><p>When a product or tool is a crucial part of your application, support is more than just a nice-to-have. For enterprise development teams, support issues could mean anything from a single blocked developer to an entire delayed release. The larger and more critical the application, the more important predictable access to technical expertise becomes.</p><p>So rather than simply saying Progress provides strong Telerik and Kendo UI support, here are the numbers behind it&mdash;customer satisfaction, response and resolution times, escalation rates and the technical resources available beyond a simple or individual support case.</p><h3>Support at a Glance</h3><ul><li><strong>98%+</strong> customer satisfaction on case closure</li><li><strong>95.5%+</strong> customer satisfaction per interaction</li><li><strong>24-hour</strong> response-time Service Level Agreement (SLA)</li><li><strong>50%+</strong> of cases resolved within 14 hours</li><li><strong>&lt;14 hours</strong> median time to first response</li><li><strong>~99.8%</strong> of tickets resolved without escalation</li><li><strong>New AI-powered chat </strong>embedded in ticket submission and documentation to provide instant answers</li></ul><h2>What Does Enterprise-Grade Support Mean?</h2><p>Enterprise-grade technical support combines predictable response commitments, access to technical expertise, efficient issue resolution and self-service resources that help teams resolve problems without creating prolonged development blockers.</p><h3>Technical Support from Technical Engineers</h3><p>We believe that good support is part of the product experience. Our technical support engineers bring hands-on expertise, helping teams get from problem to solution without unnecessary delays or layers of escalation. Telerik Technical Support Engineers are technical specialists with software development expertise, equipped to investigate implementation-level issues rather than simply triage and route them. Approximately 99.8% of support tickets are resolved within Technical Support without requiring escalation to product engineering.</p><h3>Fast Turnaround</h3><p>We also understand that when a project is blocked, timing is of the essence. That&rsquo;s why our support teams provide a <strong>24-hour response-time SLA</strong>&mdash;but most of the time, you won&rsquo;t even have to wait that long. Our <strong>median time to first response is under 14 hours</strong>. </p><p>In fact, over half of support cases are <em>resolved </em>within 14 hours, helping teams move from problem to solution quickly. Most issues also stay with the support organization rather than requiring engineering escalation. And by &ldquo;most,&rdquo; I mean &ldquo;very nearly all&rdquo;: approximately 99.8% of tickets are resolved without escalation.</p><h3>Exceptional Customer Satisfaction</h3><p>More than 98% of customers report satisfaction at case closure, and a 95.5%+ CSAT score (Customer Satisfaction) per interaction reflects high quality throughout the support experience.</p><h2>What About Support Beyond Tickets?</h2><p>Support is about more than just answering one-off tickets, though. That&rsquo;s why we offer a massive collection of technical knowledge that helps developers across the community&mdash;whether they&rsquo;re troubleshooting a problem, learning a feature, evaluating an API or looking for implementation examples. Those resources mean that developers may not even need to open a support ticket in the first place! The vast library of public technical knowledge empowers developers with the knowledge they need, saving direct technical support for unique and specific edge case instances.</p><h3>Available Public Technical Knowledge</h3><ul><li><strong>290K+ </strong>answered forum threads</li><li><strong>6.5K+</strong> knowledge base articles</li><li><strong>10K+ </strong>demos</li><li><strong>24K+ </strong>conceptual documentation articles</li><li><strong>29K+</strong> API reference articles.</li><li><strong>100+ </strong>answered community questions each month</li></ul><p>These resources are publicly searchable and indexable, which allows the impact of our excellent support to scale far beyond what could be offered via tickets alone. Developers can benefit from solutions documented through years of customer and community interactions, while organizations can build their own expertise on top of a large, accessible body of technical knowledge.</p><h2>What Our Customers Say</h2><p>The numbers are impressive, but what matters more to us is what our customers have to say about their support experiences. These are just a small sample of quotes from users who have worked with our support teams and had wonderfully positive experiences.</p><h3>Clear Instructions</h3><p><em>&ldquo;I&rsquo;m incredibly grateful for the technical assistance I received. The support was not only timely but also highly effective&mdash;resolving my issue quickly and professionally. The guidance was clear and spot-on, helping me identify the missing step and implement the correct solution without delay. Thanks to this help, everything is now working perfectly. I truly appreciate the responsiveness and expertise. This kind of support makes a huge difference!&rdquo;</em></p><h3>High-Quality Responses</h3><p><em>&ldquo;Your developer support is AWESOME! I can&rsquo;t imagine moving to another control suite. You guys are the best!&rdquo;</em> </p><h3>Fast and Detailed</h3><p><em>&ldquo;Prompt and on-point with all of our communications. I appreciate the detailed responses and explanations!&rdquo;</em></p><h3>Going the Extra Mile</h3><p><em>&ldquo;Spent extra time making sure I had the resources and information to fix my problem [&hellip;] created an example project and code snippets. Thank you so much!</em></p><h2>Better Support Means Better End Products</h2><p>Faster access to expert assistance can reduce delivery risk by helping developers resolve blockers before they become prolonged delays. Strong technical support and a comprehensive knowledge base also reduces engineering time spent troubleshooting, searching for answers, going in circles with AI chatbots or developing brittle workarounds&mdash;lowering the total cost of ownership over time. Support is, therefore, not simply a service attached to the product; it's part of the risk assessment that teams should evaluate when choosing a partner they expect to rely on for years.</p><p>Enterprise teams working with Telerik and Kendo UI can have confidence that when a problem arises, there&rsquo;s a reliable solution&mdash;and an experienced team behind the technology, just waiting to help.</p><h2>FAQ</h2><h3>How Quickly Does Telerik Technical Support Respond?</h3><p>Telerik Technical Support includes a 24-hour response-time Service Level Agreement (SLA), but our median time to first response is less than 14 hours.</p><h3>How Quickly Are Telerik Support Cases Resolved?</h3><p>Over half of cases are resolved within 14 hours, with less than 1% requiring escalation.</p><h3>Are Telerik Support Cases Handled by Technical Experts?</h3><p>Yes, Telerik Technical Support Engineers are technical specialists with software development expertise, equipped to investigate implementation-level issues rather than simply triage and route them.</p><h3>What Self-Service Support Resources Does Telerik Provide?</h3><p>Our library of self-support technical knowledge resources includes extensive community forums with over 290k answered threads, over 6.5k Knowledge Base articles, over 10k demos, over 24k conceptual documentation articles, over 29k API reference articles and more than 100 new community questions answered each month.</p><hr /><h2>Want to See for Yourself?</h2><p>Our 30-day free trials include access to support. The Telerik DevCraft suite lets you pick your favorite flavor, so check it out:</p><p><a class="Btn" target="_blank" href="https://www.telerik.com/devcraft">Try DevCraft</a></p><img src="https://feeds.telerik.com/link/10827/17443547.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:e43d41a5-b849-45cc-a9d9-883473d1273b</id>
    <title type="text">DataGrids Are For... Legal Professionals</title>
    <summary type="text">As the legal profession changes, the applications that support legal professionals must also change. The Progress Telerik and Kendo UI DataGrid gives you the ability to support those evolving needs.</summary>
    <published>2026-09-08T19:54:58Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Peter Vogel </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17442924/datagrids-are-for-legal-professionals"/>
    <content type="text"><![CDATA[<p><span class="featured">As the legal profession changes, the applications that support legal professionals must also change. The DataGrid can help you evolve.</span></p><p>The legal profession is going through enormous changes, some driven by changes in the business (e.g., outsourcing) and some by technology (AI). As a result, applications created for legal professionals like lawyers and paralegals have to reflect and support those changes.</p><p>For example, thanks to outsourcing, AI and more sophisticated third-party software, the amount of time a legal professional spends on research (once a key skill in the field) is shrinking. In the same way, legal professionals are now spending less time drafting legal documents (another formerly critical skill) and more time reviewing/correcting the products of various software systems.</p><p>Many things in the profession have not changed, of course. Today&rsquo;s legal professionals still need to be able to understand their clients&rsquo; needs and analyze the issues each client faces. Legal professionals must provide solutions that are attainable while meeting their clients&rsquo; needs and be able to communicate the resulting courses of action (with their risks) both to their clients and the other parties involved.</p><hr /><blockquote><p>Actually, here&rsquo;s an interesting <a target="_blank" href="https://www.sharefile.com/resource/papers/state-of-legal">survey of legal professionals</a> that the Progress ShareFile team put together. Lawyers love the work they do&mdash;but many still face workflow friction that slows client service, delays intake and creates unnecessary complexity. Of the participating legal professionals surveyed:</p><br /><ul><li>97% say AI and automation will be important to the future of legal</li><li>84% say workflow inconsistencies impact efficiency</li><li>95% want tools that are simple and easy to use</li></ul></blockquote><hr /><p>When it comes to executing the selected actions, much remains the same: filing deadlines remain critical, as do keeping clients compliant with relevant laws and advocating for clients&rsquo; interests in criminal and civil cases.</p><p>While the business has changed, clients have also changed and are increasingly using self-serve and lower cost options (e.g., community legal workers). When working with legal professionals, clients are increasingly assessing their experience by how the client&rsquo;s interaction is personalized and customized to their needs. As a result, legal professionals are moving away from traditional hourly based billing toward value-based billing and are increasingly focused on efficiency.</p><p>The net result is an increased demand for &ldquo;legal services&rdquo; (contract life cycle management, for example) instead of &ldquo;legal advice&rdquo; like routine contract negotiation.</p><p>The Progress Telerik and Kendo UI <a target="_blank" href="https://www.telerik.com/devcraft#demo-tab-10">DataGrid</a> is an essential part of building applications that support those changing needs.</p><h2 id="retrieving-and-finding-documents">Retrieving and Finding Documents</h2><p>For example, a legal case still involves thousands of documents: client records, meeting notes, case files, pleadings, affidavits, court filings, expert reports and more. Most of those documents are now digital assets, stored in various repositories that can be accessed through the DataGrid. Using the DataGrid&rsquo;s virtualization features enables users to retrieve and review large numbers of documents quickly&mdash;essential in being responsive to clients&rsquo; needs.</p><p>The DataGrid offers multiple tools for filtering, sorting and searching for specific documents. Professionals can find documents by specific clauses, case IDs or document types, even across large repositories. The DataGrid AI-enabled Semantic Search even lets users find records just by typing in what they want (e.g., &ldquo;Show me all the active cases&rdquo;).</p><p>Once the relevant document is found, professionals can have multiple options in reviewing the document, depending on their needs. Documents can be viewed in the DataGrid, of course, but can also be reviewed in popup windows to show key information without leaving the current screen. Or users can navigate to a separate screen to view the document in full.</p><h2 id="organizing-documents-and-data">Organizing Documents and Data</h2><p>Given the variety of documents, the DataGrid&rsquo;s ability to organize those documents into groups is critical. The DataGrid can be configured to provide a predefined set of groups, and users can create their own groups as they need them by dragging and dropping columns in the grid.</p><p>Going beyond simple groups, the DataGrid can be configured as a hierarchy of document groups to organize documents by whatever criteria the user needs. Because the DataGrid has the ability to nest grids with different layouts within, for example, a document hierarchy, different document types can be displayed with different formats.</p><p>Some things remain constant, though. Even in the evolving world of legal professionals, time remains of the essence. While <em>nunc pro tunc</em> (&ldquo;now for then&rdquo;) can apply, it remains essential for filings to be made on time and correctly. DataGrid templates enable users, without leaving their current screen, to switch between a document-oriented view and a time-based view to see what&rsquo;s pending (and conditional formatting can be used to highlight filings that still need development).</p><p>The DataGrid&rsquo;s ability to organize any kind of data, in any order, to support any analysis is also essential to supporting the way the profession is evolving. Businesses now expect their legal departments to not only find ways to manage legal risk but also to provide strategic insights to improve revenues and decrease costs. Legal professionals increasingly need tools&mdash;like the DataGrid&mdash;that support exploring data to help them provide those insights.</p><p>For example, in the contract management area, clients now expect their legal representatives to be able analyze past performance of various terms in contracts to identify opportunities both for improving future contracts and renegotiating existing contracts (or identifying contracts that can&rsquo;t be improved and should be terminated).</p><h2 id="sharing-data">Sharing Data</h2><p>Client confidentiality remains a key concern in any interaction with a legal professional. The DataGrid&rsquo;s ability to integrate with code that authenticates and authorizes users to control access to data helps provide that. The DataGrid&rsquo;s UI supports hiding columns, hiding whole rows and formatting individual cells hide information, all driven by the user&rsquo;s authorized roles.</p><p>On the other hand, legal information often needs to be shared with others (courts, clients, other members of the office, opposing counsel). The DataGrid supports exporting selected rows or the whole grid in both Excel and PDF formats (these options can also be turned off to help prevent data from leaving the application&rsquo;s secure environment). Any hidden rows, columns and obfuscated data remain hidden during export and, where necessary, custom code can be inserted into the export process to further control the process.</p><h2 id="updating-information">Updating Information</h2><p>While reporting data is important, the DataGrid also provides excellent support for updating information efficiently. Users can update single rows, enter a &ldquo;bulk update&rdquo; mode that supports updating multiple rows, open popup windows for individual rows to improve the update experience or navigate from the DataGrid to full screen editing for any row.</p><p>The DataGrid also provides built-in validation to help prevent invalid data being entered and supports both synchronous validation (stopping the user from continuing until data is validated) and asynchronous validation (allowing the user to continue when validation is time-consuming but preventing updates when the user tries to save their data). Validation can be extended with custom code that can take advantage of the DataGrid&rsquo;s error-handling support (displaying error messages in tooltips, for example).</p><p>In addition to providing multiple shortcuts that enable experienced users to work efficiently, the DataGrid supports WCAG accessibility guidelines out of the box. For occasional users, the DataGrid&rsquo;s various assistants (AI Toolbar Assistant, AI Column Assistant, etc.), let users perform complex operations with natural language commands.</p><p>As the legal profession changes, the applications that support legal professionals must also change. The Telerik and Kendo UI DataGrid gives you the ability to support those evolving needs.</p><hr /><p>Check out the <a target="_blank" href="https://www.telerik.com/devcraft">Telerik DevCraft</a> suite of products to learn about the DataGrid in your favorite .NET or JavaScript flavor, like the <a target="_blank" href="https://demos.telerik.com/blazor-ui/grid/overview">Blazor Grid</a> or the <a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/grid">Angular Grid</a>.</p><p>Get your hands on the whole suite free for 30 days:</p><p><a target="_blank" href="https://www.telerik.com/try/devcraft-ultimate" class="Btn">Try Telerik DevCraft</a></p><img src="https://feeds.telerik.com/link/10827/17442924.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:6e30ec53-74c0-4de1-936d-6a2de6575ab5</id>
    <title type="text">Testing Vue Components That Use TanStack Query</title>
    <summary type="text">Here are the quirks to note about testing in TanStack Query that may differ from other Vue component testing and how to get set up so it feels like regular testing again.</summary>
    <published>2026-09-02T20:01:00Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Marina Mosti </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17438005/testing-vue-components-use-tanstack-query"/>
    <content type="text"><![CDATA[<p><span class="featured">Here are the quirks to note about testing in TanStack Query that may differ from other Vue component testing and how to get set up so it feels like regular testing again.</span></p><p>A while back, I wrote about <a target="_blank" href="https://www.telerik.com/blogs/how-manage-composition-api-refs-vue-3-unit-testing">How to Manage Composition API Refs in Vue 3 When Unit Testing</a>, and more recently we&rsquo;ve been working through the TanStack Query series, finishing up with <a target="_blank" href="https://www.telerik.com/blogs/handling-mutations-tanstack-query-vue">mutations</a> and <a href="https://www.telerik.com/blogs/pagination-infinite-queries-tanstack-query-vue" target="_blank">pagination</a>.</p><p>Now we get to the part nobody really wants to write but that pays for itself the first time a refactor doesn&rsquo;t break in production: tests. Testing components that use TanStack Query has a few specific gotchas that don&rsquo;t really come up with regular Vue components, and I want to walk through how I usually approach it.</p><h2 id="what-makes-tanstack-query-tricky-to-test">What Makes TanStack Query Tricky to Test?</h2><p>A normal component test is straightforward: mount the component, assert what&rsquo;s in the DOM, maybe simulate some clicks, assert again. Done.</p><p>A TanStack Query component adds a few wrinkles:</p><ul><li>It needs a <code>QueryClient</code> provided through the app instance. Mount it without one and you&rsquo;ll get a vague error.</li><li>It does asynchronous work as soon as it mounts, so the very first thing you assert is going to be a loading state.</li><li>The thing it&rsquo;s asynchronously talking to (<code>fetch</code>, <code>axios</code>, whatever) needs to be mocked, or your tests will hit your real API.</li><li>It caches across renders. If you don&rsquo;t reset the cache between tests, one test can pollute another.</li></ul><p>Each of these is solvable in a couple of lines. The trick is knowing where each line goes.</p><h2 id="setting-up-a-test-queryclient">Setting Up a Test QueryClient</h2><p>The first thing we need is a way to mount a component with a fresh <code>QueryClient</code> in each test. I usually wrap this in a small helper.</p><p><code>tests/utils/withQueryClient.js</code></p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">import</span> <span class="token punctuation">{</span> mount <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@vue/test-utils'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> QueryClient<span class="token punctuation">,</span> VueQueryPlugin <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@tanstack/vue-query'</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">mountWithQuery</span> <span class="token operator">=</span> <span class="token punctuation">(</span>component<span class="token punctuation">,</span> options <span class="token operator">=</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 punctuation">{</span>
  <span class="token keyword">const</span> queryClient <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">QueryClient</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    defaultOptions<span class="token punctuation">:</span> <span class="token punctuation">{</span>
      queries<span class="token punctuation">:</span> <span class="token punctuation">{</span>
        retry<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
        gcTime<span class="token punctuation">:</span> <span class="token number">0</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">return</span> <span class="token function">mount</span><span class="token punctuation">(</span>component<span class="token punctuation">,</span> <span class="token punctuation">{</span>
    <span class="token operator">...</span>options<span class="token punctuation">,</span>
    global<span class="token punctuation">:</span> <span class="token punctuation">{</span>
      <span class="token operator">...</span><span class="token punctuation">(</span>options<span class="token punctuation">.</span>global <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 punctuation">,</span>
      plugins<span class="token punctuation">:</span> <span class="token punctuation">[</span>
        <span class="token operator">...</span><span class="token punctuation">(</span>options<span class="token punctuation">.</span>global<span class="token operator">?</span><span class="token punctuation">.</span>plugins <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 punctuation">,</span>
        <span class="token punctuation">[</span>VueQueryPlugin<span class="token punctuation">,</span> <span class="token punctuation">{</span> queryClient <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 punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>A few things worth pointing out:</p><ul><li>We create a fresh <code>QueryClient</code> for each test. This is critical. If you reuse a single client across tests, the cache from one test bleeds into the next, and you&rsquo;ll spend an afternoon hunting down phantom failures.</li><li><code>retry: false</code> disables the default retry behavior. In a test, we want failures to be failures, not &ldquo;wait three seconds for two more retries before giving up.&rdquo;</li><li><code>gcTime: 0</code> makes sure cached data isn&rsquo;t kept around. Combined with the fresh client, this gives us proper isolation.</li></ul><p>In real codebases, I usually expose this as a helper from a <code>tests/utils/</code> folder and have my test files import it. Whether you use Vitest or Jest, the shape is the same.</p><h2 id="mocking-fetch">Mocking Fetch</h2><p>Now for the API. Our component reaches the outside world through <code>fetch</code>, and in a test we absolutely do not want it talking to a real server. So we swap <code>fetch</code> for a stub that returns whatever we tell it to.</p><p>Vitest makes this painless with <code>vi.stubGlobal</code>. I set it up once in a global setup file so every test starts with a fresh, fake <code>fetch</code> and the real one gets restored when the test is done.</p><p><code>tests/setup.js</code></p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">import</span> <span class="token punctuation">{</span> afterEach<span class="token punctuation">,</span> beforeEach<span class="token punctuation">,</span> vi <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vitest'</span>

<span class="token function">beforeEach</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 punctuation">{</span>
  vi<span class="token punctuation">.</span><span class="token function">stubGlobal</span><span class="token punctuation">(</span><span class="token string">'fetch'</span><span class="token punctuation">,</span> vi<span class="token punctuation">.</span><span class="token function">fn</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 function">afterEach</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 punctuation">{</span>
  vi<span class="token punctuation">.</span><span class="token function">unstubAllGlobals</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>Point Vitest at this file with <code>setupFiles: ['./tests/setup.js']</code> in your <code>vitest.config.js</code>, and every test gets a clean <code>fetch</code> mock for free.</p><p>There&rsquo;s one slightly annoying detail. <code>fetch</code> doesn&rsquo;t resolve to your data directly, it resolves to a <code>Response</code> object with <code>.ok</code>, <code>.status</code>, and a <code>.json()</code> method. Since we&rsquo;ll be building those over and over, a tiny helper keeps the tests readable.</p><p><code>tests/utils/jsonResponse.js</code></p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">jsonResponse</span> <span class="token operator">=</span> <span class="token punctuation">(</span>data<span class="token punctuation">,</span> <span class="token punctuation">{</span> ok <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">,</span> status <span class="token operator">=</span> <span class="token number">200</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 operator">=&gt;</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>
  ok<span class="token punctuation">,</span>
  status<span class="token punctuation">,</span>
  json<span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> data<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>That&rsquo;s the entire mocking setup. No extra dependencies, nothing intercepting at the network level, just a fake function that hands back the same shape <code>fetch</code> would.</p><h2 id="writing-your-first-test">Writing Your First Test</h2><p>Let&rsquo;s put it all together. Imagine we have the following component:</p><p><code>UserList.vue</code></p><pre class=" language-vue"><code class="prism  language-vue">&lt;script setup&gt;
import { useQuery } from '@tanstack/vue-query'

const fetchUsers = async () =&gt; {
  const response = await fetch('https://myapp.com/users')
  if (!response.ok) {
    throw new Error('Failed to fetch users')
  }
  return response.json()
}

const { data, isPending, isError } = useQuery({
  queryKey: ['users'],
  queryFn: fetchUsers,
})
&lt;/script&gt;

&lt;template&gt;
  &lt;p v-if="isPending"&gt;Loading...&lt;/p&gt;
  &lt;p v-else-if="isError"&gt;Error&lt;/p&gt;
  &lt;ul v-else&gt;
    &lt;li v-for="user in data" :key="user.id"&gt;{{ user.name }}&lt;/li&gt;
  &lt;/ul&gt;
&lt;/template&gt;
</code></pre><p>And here&rsquo;s the test.</p><p><code>UserList.spec.js</code></p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">import</span> <span class="token punctuation">{</span> describe<span class="token punctuation">,</span> it<span class="token punctuation">,</span> expect <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vitest'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> flushPromises <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@vue/test-utils'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> mountWithQuery <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./utils/withQueryClient'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> jsonResponse <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./utils/jsonResponse'</span>
<span class="token keyword">import</span> UserList <span class="token keyword">from</span> <span class="token string">'../src/components/UserList.vue'</span>

<span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">'UserList'</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 punctuation">{</span>
  <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'shows a loading state and then the user list'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    fetch<span class="token punctuation">.</span><span class="token function">mockResolvedValue</span><span class="token punctuation">(</span>
      <span class="token function">jsonResponse</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
        <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'Ada Lovelace'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'Grace Hopper'</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">const</span> wrapper <span class="token operator">=</span> <span class="token function">mountWithQuery</span><span class="token punctuation">(</span>UserList<span class="token punctuation">)</span>

    <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">text</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">toContain</span><span class="token punctuation">(</span><span class="token string">'Loading...'</span><span class="token punctuation">)</span>

    <span class="token keyword">await</span> <span class="token function">flushPromises</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

    <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">text</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">toContain</span><span class="token punctuation">(</span><span class="token string">'Ada Lovelace'</span><span class="token punctuation">)</span>
    <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">text</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">toContain</span><span class="token punctuation">(</span><span class="token string">'Grace Hopper'</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>Let&rsquo;s break it down. First we tell our fake <code>fetch</code> what to resolve to (because the setup file stubbed it globally, we can reach for <code>fetch</code> directly). Then we mount the component, immediately assert that the loading state is visible (the query hasn&rsquo;t resolved yet), <code>flushPromises</code> to let the microtasks run, and finally assert that the list rendered.</p><p><code>flushPromises</code> from <code>@vue/test-utils</code> is the easiest way I&rsquo;ve found to get past the async boundary. It awaits every queued promise, including the mocked <code>fetch</code> and the reactive update inside TanStack Query. If you forget it, you&rsquo;ll see the loading state forever and a confused test runner.</p><p>If <code>flushPromises</code> isn&rsquo;t enough (some flows need a few extra ticks), you can also lean on <code>vi.waitFor</code> or the equivalent in your test runner, which retries an assertion until it passes or times out.</p><h2 id="testing-the-error-path">Testing the Error Path</h2><p>Errors are easy to test once the happy path works. We just tell <code>fetch</code> to resolve to a failed response for that one test:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'shows an error state when the API fails'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  fetch<span class="token punctuation">.</span><span class="token function">mockResolvedValue</span><span class="token punctuation">(</span>
    <span class="token function">jsonResponse</span><span class="token punctuation">(</span><span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'oops'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> ok<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> status<span class="token punctuation">:</span> <span class="token number">500</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">)</span>

  <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">mountWithQuery</span><span class="token punctuation">(</span>UserList<span class="token punctuation">)</span>
  <span class="token keyword">await</span> <span class="token function">flushPromises</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">text</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">toContain</span><span class="token punctuation">(</span><span class="token string">'Error'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>Notice how our component checks <code>response.ok</code> and throws when it&rsquo;s false. That thrown error is what flows through TanStack Query into the <code>isError</code> branch, which is exactly what we&rsquo;re asserting on. This is also why <code>retry: false</code> in our helper matters so much: without it, the query would dutifully retry the failing request a few times before giving up, and your test would crawl.</p><p>Because the setup file gives every test a brand-new <code>fetch</code> mock, this <code>mockResolvedValue</code> only affects this one test. Nothing to clean up by hand.</p><h2 id="testing-mutations">Testing Mutations</h2><p>Mutations are very similar, but with a twist. Because they don&rsquo;t fire automatically on mount, we have to actually trigger them. Let&rsquo;s say we have a component that creates a user when a button is clicked.</p><p><code>CreateUser.spec.js</code></p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">import</span> <span class="token punctuation">{</span> describe<span class="token punctuation">,</span> it<span class="token punctuation">,</span> expect <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vitest'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> flushPromises <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@vue/test-utils'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> mountWithQuery <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./utils/withQueryClient'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> jsonResponse <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./utils/jsonResponse'</span>
<span class="token keyword">import</span> CreateUser <span class="token keyword">from</span> <span class="token string">'../src/components/CreateUser.vue'</span>

<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'creates a user when the form is submitted'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  fetch<span class="token punctuation">.</span><span class="token function">mockResolvedValue</span><span class="token punctuation">(</span>
    <span class="token function">jsonResponse</span><span class="token punctuation">(</span>
      <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'Ada'</span><span class="token punctuation">,</span> email<span class="token punctuation">:</span> <span class="token string">'ada@example.com'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span> status<span class="token punctuation">:</span> <span class="token number">201</span> <span class="token punctuation">}</span>
    <span class="token punctuation">)</span>
  <span class="token punctuation">)</span>

  <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">mountWithQuery</span><span class="token punctuation">(</span>CreateUser<span class="token punctuation">)</span>

  <span class="token keyword">await</span> wrapper<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'input[name="name"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setValue</span><span class="token punctuation">(</span><span class="token string">'Ada'</span><span class="token punctuation">)</span>
  <span class="token keyword">await</span> wrapper<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'input[name="email"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setValue</span><span class="token punctuation">(</span><span class="token string">'ada@example.com'</span><span class="token punctuation">)</span>
  <span class="token keyword">await</span> wrapper<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'form'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">trigger</span><span class="token punctuation">(</span><span class="token string">'submit'</span><span class="token punctuation">)</span>
  <span class="token keyword">await</span> <span class="token function">flushPromises</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token keyword">const</span> <span class="token punctuation">[</span>url<span class="token punctuation">,</span> options<span class="token punctuation">]</span> <span class="token operator">=</span> fetch<span class="token punctuation">.</span>mock<span class="token punctuation">.</span>calls<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>
  <span class="token function">expect</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toBe</span><span class="token punctuation">(</span><span class="token string">'https://myapp.com/users'</span><span class="token punctuation">)</span>
  <span class="token function">expect</span><span class="token punctuation">(</span>JSON<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>options<span class="token punctuation">.</span>body<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toEqual</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    name<span class="token punctuation">:</span> <span class="token string">'Ada'</span><span class="token punctuation">,</span>
    email<span class="token punctuation">:</span> <span class="token string">'ada@example.com'</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">text</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">toContain</span><span class="token punctuation">(</span><span class="token string">'User created'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>Two things to call out. First, because <code>fetch</code> is a <code>vi.fn()</code>, every call is recorded. <code>fetch.mock.calls[0]</code> gives us the arguments of the first call, so we can pull out the URL and the request options and assert against them. We parse <code>options.body</code> back from JSON to confirm the right payload went out the door, not just that <em>something</em> happened. That&rsquo;s the part of a mutation that actually tends to break.</p><p>Second, we&rsquo;re using two <code>await</code>s back to back: one for the form submission, and <code>flushPromises</code> to let the mutation&rsquo;s async work resolve. You&rsquo;ll see this pattern a lot. When in doubt, throw in another <code>await flushPromises()</code> and see if your assertion passes.</p><h2 id="testing-cache-invalidation">Testing Cache Invalidation</h2><p>One of the most valuable tests you can write covers the interaction between a mutation and the queries it invalidates. When you create a user, does the list actually refresh? This is the integration that breaks most often, and it&rsquo;s easy to get subtly wrong: forget <code>invalidateQueries</code> in your mutation or invalidate the wrong key, and the list just sits there showing stale data.</p><p>To test it, we mount a page that brings both pieces together. Picture a <code>UserPage</code> that renders the user list and a create form (the form is probably its own child component, but from the test&rsquo;s point of view we just mount the page and treat it as one unit). We create a user, then assert the new name appears in the list.</p><p>This is actually where mocking <code>fetch</code> directly reads quite nicely. The component makes three requests in a known order, so we queue up three responses with <code>mockResolvedValueOnce</code>.</p><p><code>UserPage.spec.js</code></p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">import</span> <span class="token punctuation">{</span> describe<span class="token punctuation">,</span> it<span class="token punctuation">,</span> expect <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vitest'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> flushPromises <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@vue/test-utils'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> mountWithQuery <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./utils/withQueryClient'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> jsonResponse <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./utils/jsonResponse'</span>
<span class="token keyword">import</span> UserPage <span class="token keyword">from</span> <span class="token string">'../src/pages/UserPage.vue'</span>

<span class="token function">it</span><span class="token punctuation">(</span><span class="token string">'refreshes the user list after creating a user'</span><span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  fetch
    <span class="token comment">// initial list load</span>
    <span class="token punctuation">.</span><span class="token function">mockResolvedValueOnce</span><span class="token punctuation">(</span><span class="token function">jsonResponse</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'Ada'</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">// the create mutation</span>
    <span class="token punctuation">.</span><span class="token function">mockResolvedValueOnce</span><span class="token punctuation">(</span>
      <span class="token function">jsonResponse</span><span class="token punctuation">(</span><span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'Grace'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> status<span class="token punctuation">:</span> <span class="token number">201</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">)</span>
    <span class="token comment">// the refetch triggered by invalidateQueries</span>
    <span class="token punctuation">.</span><span class="token function">mockResolvedValueOnce</span><span class="token punctuation">(</span>
      <span class="token function">jsonResponse</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
        <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'Ada'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
        <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'Grace'</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">const</span> wrapper <span class="token operator">=</span> <span class="token function">mountWithQuery</span><span class="token punctuation">(</span>UserPage<span class="token punctuation">)</span>
  <span class="token keyword">await</span> <span class="token function">flushPromises</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">text</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">toContain</span><span class="token punctuation">(</span><span class="token string">'Ada'</span><span class="token punctuation">)</span>
  <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">text</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span>not<span class="token punctuation">.</span><span class="token function">toContain</span><span class="token punctuation">(</span><span class="token string">'Grace'</span><span class="token punctuation">)</span>

  <span class="token keyword">await</span> wrapper<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'input[name="name"]'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setValue</span><span class="token punctuation">(</span><span class="token string">'Grace'</span><span class="token punctuation">)</span>
  <span class="token keyword">await</span> wrapper<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token string">'form'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">trigger</span><span class="token punctuation">(</span><span class="token string">'submit'</span><span class="token punctuation">)</span>
  <span class="token keyword">await</span> <span class="token function">flushPromises</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
  <span class="token keyword">await</span> <span class="token function">flushPromises</span><span class="token punctuation">(</span><span class="token punctuation">)</span>

  <span class="token function">expect</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">text</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">toContain</span><span class="token punctuation">(</span><span class="token string">'Grace'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>Let&rsquo;s break it down. The three <code>mockResolvedValueOnce</code> calls line up with the exact sequence of requests the page makes: the list loads on mount, the form submission POSTs the new user and then <code>invalidateQueries</code> fires off a fresh GET for the list.</p><p>Notice the two <code>flushPromises</code> after the submit. This is the longest async cascade in the article (submit, the mutation resolves, its <code>onSuccess</code> calls <code>invalidateQueries</code>, the list refetches, <em>then</em> the DOM updates), and that&rsquo;s more hops than a single flush reliably drains.</p><p>When a test like this comes out flaky, an extra <code>await flushPromises()</code> is almost always the fix. If you&rsquo;d rather not count ticks by hand, wrap the final assertion in <code>vi.waitFor(() =&gt; expect(wrapper.text()).toContain('Grace'))</code>, which retries until it passes or times out.</p><p>This test mirrors what an actual user does: see the list, submit a form, see the updated list. It catches a whole class of bug that narrow unit tests don&rsquo;t, like forgetting <code>invalidateQueries</code> in your mutation or invalidating the wrong key.</p><p>I&rsquo;ve found that having one or two of these per major flow (create, update, delete) catches more real regressions than a hundred isolated component tests.</p><h2 id="a-few-conventions-that-have-saved-me-hours">A Few Conventions That Have Saved Me Hours</h2><ul><li><strong>One QueryClient per test</strong>: I cannot stress this enough. The fresh-client helper at the top of this article is the single most useful thing I do.</li><li><strong>Disable retries in tests</strong>: A failing test that takes nine seconds to fail because of three retries is a debugging nightmare.</li><li><strong>Reach for the happy-path response by default, override per test for the unhappy path</strong>: <code>mockResolvedValue</code> for the common case, a one-off <code>mockResolvedValueOnce</code> or <code>mockRejectedValue</code> for the test that needs something different. Keeps each test focused on what&rsquo;s special about it.</li><li><strong>Don&rsquo;t try to test internal TanStack Query behavior</strong>: If you find yourself writing a test that asserts something about <code>staleTime</code> or refetching, pause. That&rsquo;s the library&rsquo;s responsibility, not yours. Test what your component does, not what TanStack Query does.</li></ul><p>To be quite honest with you, that last point is the one I see violated the most. People write a test that mocks the whole <code>useQuery</code> composable, then assert that their component &ldquo;calls useQuery with the right key.&rdquo; Those tests don&rsquo;t actually catch any real bugs, they just assert that the code is the code. Mock <code>fetch</code>, not the library.</p><h2 id="wrapping-up">Wrapping Up</h2><p>Once you have a <code>mountWithQuery</code> helper, a setup file that stubs <code>fetch</code> and the rhythm of <code>flushPromises</code>, testing TanStack Query components stops feeling weird and starts feeling like regular component testing. You&rsquo;re back to &ldquo;render the component, simulate a thing, assert what&rsquo;s in the DOM.&rdquo;</p><p>For more on the testing side specifically, the <a target="_blank" href="https://tanstack.com/query/latest/docs/framework/react/guides/testing">official testing guide</a> is worth a read (it&rsquo;s React-flavored but the patterns translate cleanly), and the <a target="_blank" href="https://vitest.dev/guide/mocking">Vitest mocking docs</a> are great if you want to go deeper on stubbing globals and inspecting mock calls.</p><p>Happy testing!</p><img src="https://feeds.telerik.com/link/10827/17438005.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:119310e1-d053-4714-b531-071f7c2c7c71</id>
    <title type="text">DataGrids Are For... Medical Professionals</title>
    <summary type="text">DataGrids are excellent for providing both expansive data and filtered specifics, all in a usable and familiar setup. Just what the medical industry demands.</summary>
    <published>2026-08-31T16:23:26Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Peter Vogel </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17436116/datagrids-are-for-medical-professionals"/>
    <content type="text"><![CDATA[<p><span class="featured">DataGrids are excellent for providing both expansive data and filtered specifics, all in a usable and familiar setup. Just what the medical industry demands.</span></p><p>If you&rsquo;re part of creating applications in the medical field, many (or most) of your users are going to be medical professionals: trained individuals who are licensed/certified to provide preventative, curative or rehabilitative healthcare to patients. This includes doctors, nurses, paramedics, dentists, their support staff (e.g., pharmacists) and other health professionals (e.g., physiotherapists).</p><p>Really, the group of users includes anyone who is certified to provide care (i.e., diagnosing, treating or managing) for either physical or mental illnesses. As broad as that group is, those professionals have a set of shared needs that your application will need to support.</p><p>For example, all medical professionals work in evidence-based environments where it&rsquo;s difficult to have &ldquo;too much&rdquo; information &hellip; but where it&rsquo;s essential to find the relevant details quickly. All these professionals (and the applications they use) must also adhere to various regulatory standards, including confidentiality and privacy requirements.</p><p>Many medical professionals are working in environments where time is critical and, as result, are environments best described as &ldquo;high stress&rdquo;: Too much to do, not enough time to do all of it, and frequent interruptions by new, high priority tasks. Speed and &ldquo;ease of use&rdquo; are essential here, but &ldquo;ease of use&rdquo; must also include flexibility to meet changing needs.</p><p>Finally, as that list of different people suggests, medical professionals work in highly collaborative environments, where they are constantly calling on other professionals in different specialties or departments to support their patients. The ability to share data is also critical.</p><p>You can support all those needs with the DataGrid component in your favorite Progress Telerik or Kendo UI library.</p><h2 id="reporting-the-data">Reporting the Data</h2><p>A medical professional will often need to handle large volumes of complex patient data, sometimes for multiple patients (doctor&rsquo;s offices, hospital wards) and sometimes for a single patient (reviewing patient history). Because of the time-critical nature of the field, medical professionals need to get to the data quickly either in real time or near real time.</p><p>You can leverage the DataGrid <code>OnRead</code> event and support for Observables to load initial data quickly and to respond to changes in the data as those changes happen.</p><p>Being able to find &ldquo;what matters&rdquo; in large amounts of is also going to be important to your users. Out of the box, the DataGrid allows users to reorder and pin columns, filter data and group data to pull together what matters to them right now.</p><p>See, for example, the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/sample-applications/healthcare-management-system">React Healthcare Management sample app</a>, with a Progress <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid">KendoReact DataGrid</a> displaying a filterable <a target="_blank" href="https://telerik.github.io/kendo-react/react-health-app/#/patients">list of patients</a>.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/react-healthcare-app-grid.png?sfvrsn=a5710d1d_2" alt="Patient list filtered by blood type A-" /></p><p>However, users will also want to be able quickly switch between DataGrid configurations they use frequently (grouping, column order, filters) rather than have to recreate those configurations from scratch: switching from viewing patient data grouped and sorted by patient or room to seeing the data grouped and organized by time (either historical dates or future activities). You can implement &ldquo;one-click&rdquo; switching between those settings by using DataGrid templates, or you can give users the ability to save DataGrid configurations that are useful to them.</p><p>Medical professionals are also going to value having key data highlighted as soon as that data is displayed. But you can also use the DataGrid conditional formatting (at the row or cell level) to highlight critical data. In addition, the DataGrid &ldquo;scroll to item&rdquo; option lets you programmatically put critical data on the screen for the user to review.</p><p>Much medical data is hierarchical in nature (for example, patients in a ward, patients attending a clinic in any time period, a single patient&rsquo;s medical history). Your users can take advantage of the DataGrid&rsquo;s ability to define new groups as needed by dragging and dropping columns.</p><p>And you can also leverage the DataGrid&rsquo;s ability to create nested hierarchies to allow your users to view the groups that matter to them as soon as the data is displayed. The DataGrid&rsquo;s hierarchical display then allows your users to expand those high-level groups to, for example, drill down to individual patients in a ward or to single events in a patient&rsquo;s medical history.</p><h2 id="regulatory-issues-sharing-data-and-privacy">Regulatory Issues, Sharing Data and Privacy</h2><p>Out of the box, the DataGrid (in addition to supporting assistive technologies like Jaws and ZoomText) is designed to be compliant with both the Web Content Accessibility Guidelines and the Americans with Disabilities Act accessibility guidelines. But you can also take advantage of the DataGrid&rsquo;s ability to dynamically hide columns and mask sensitive data to let you control what data is seen by users (based, for example, on the user&rsquo;s roles as retrieved from whatever authorization system their organization uses).</p><p>The DataGrid also integrates cleanly with server-side data processing, enabling you to decide what data you want to keep on the server and what you&rsquo;re willing to send to the client.</p><p>Because medical professionals are working collaboratively with other professionals to deliver patient care, exporting data is often essential. Out of the box, users can easily support export DataGrid data in either CSV or PDF format. Users can choose to export a single page from the DataGrid, export a filtered subset of the data or export all the data in the grid. The DataGrid also supports integrating your own custom code into the export process so that you can control what data is shared, helping to protect sensitive data from leaving the security parameters of your application.</p><p>Because of the nature of the data in medical applications, you&rsquo;ll also want to take advantage of DataGrid protection against Cross-Site Scripting attacks by using the <code>HtmlEncode</code> property to reduce the attack surface of your grid. When interacting with SQL data sources, using the DataGrid&rsquo;s <code>Parameter</code> elements can help protect your application&rsquo;s data from SQL injection attacks.</p><h2 id="updating-the-data">Updating the Data</h2><p>In the evidence-based environment that medical professionals work in, reporting data is important. Equally important is capturing and recording data quickly without losing accuracy.</p><p>Users can rapidly update data within the grid without, for example, having to open a separate form (though you can enable popup forms for individual rows or even navigate to another form in order provide a UI that supports more intensive data entry). The DataGrid supports row-by-row editing to enable updating a single entry in the grid (including locking out cells in a row to prevent inadvertent changes). The DataGrid also support bulk editing that allows the user to update multiple rows before saving their changes with a single button click.</p><p>The shortcuts and keyboard navigation built into the DataGrid allow experienced users to move quickly through the grid and update data without having to reach for the mouse. On top of that, the new &ldquo;smart&rdquo; AI features in the DataGrid let users enter natural language queries to find or highlight rows that meet the criteria that users provide. The DataGrid has various AI assistants (Chat, Column and Toolbar) to go further in letting users describe what they want the grid to do and have the grid respond without the user having to get involved in the detailed mouse movements.</p><p>In a medical environment verifying that only valid entries are made in patient data (including, for example, medication dosages) is essential. The DataGrid has built-in support for validating data entry, including integrating your own custom validation to support maintaining data integrity.</p><p>To put it another way: With the DataGrid, you can create applications that integrate with any clinical workflow and provide medical professionals with the tools they need to better meet the needs of their job.</p><hr /><p>You saw the KendoReact Grid above. Check out the <a target="_blank" href="https://www.telerik.com/devcraft">Telerik DevCraft</a> suite of products to learn about the DataGrid in your favorite .NET or JavaScript flavor, like the <a target="_blank" href="https://demos.telerik.com/blazor-ui/grid/overview">Blazor Grid</a> or the <a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/grid">Angular Grid</a>.</p><p>Get your hands on the whole suite free for 30 days:</p><p><a target="_blank" href="https://www.telerik.com/try/devcraft-ultimate" class="Btn">Try Telerik DevCraft</a></p><img src="https://feeds.telerik.com/link/10827/17436116.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:a5f31a91-17e7-48b5-92b9-4ddf5d9050e5</id>
    <title type="text">When to Use ListView vs. DataGrid in Real-World Projects</title>
    <summary type="text">Depending on the type of user, the number of fields and the tasks being performed, does your Blazor app need a DataGrid or a ListView?</summary>
    <published>2026-08-25T12:59:09Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Héctor Pérez </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17428155/when-use-listview-vs-datagrid-real-world-projects"/>
    <content type="text"><![CDATA[<p><span class="featured">Depending on the type of user, the number of fields and the tasks being performed, does your Blazor app need a DataGrid or a ListView?</span></p><p>Displaying data to users is common in web applications. One question you may ask, and which doesn&rsquo;t always have the same answer, is how to choose the right component to do it.</p><p>Would it be appropriate to show a table with all the columns? Or would a card gallery like those used in mobile apps be better?</p><p>For that reason, in this article we will analyze the components <a target="_blank" href="https://www.telerik.com/blazor-ui/listview">Blazor ListView</a> and <a target="_blank" href="https://www.telerik.com/blazor-ui/grid">Blazor DataGrid</a> from the Progress Telerik UI library, comparing their features. Let&rsquo;s go!</p><h2 id="the-problem-of-choosing-the-wrong-component">The Problem of Choosing the Wrong Component</h2><p>How we choose to display information in our web application depends on several variables:</p><ul><li><strong>The type of user:</strong> A user who requires detailed information about an item is not the same as someone who only needs to see general information.</li><li><strong>The amount of fields to display:</strong> Quickly viewing information is not the same as analyzing and comparing data.</li><li><strong>The task to perform with the data:</strong> You may need to perform complex operations like grouping, applying filters or making comparisons between them, or you may not.</li></ul><p>Not being clear about the purpose of using the information could lead to poor user experiences or make it difficult to work with the data.</p><h2 id="getting-to-know-the-blazor-datagrid-component">Getting to Know the Blazor DataGrid Component</h2><p>The Blazor DataGrid component allows viewing data in a tabular form. The idea of the component is to display many records in a table format with all the necessary tools for a user to analyze them.</p><p>It is a quite robust and flexible component, which in its latest versions incorporates AI features for better data understanding. Among its main capabilities we can find:</p><ul><li>Sorting by one or multiple columns</li><li>Filtering on each column</li><li>Grouping by dragging headers</li><li>Paging with traditional pagination or virtual scrolling</li><li>Inline or popup editing of records</li><li>Resizable, reorderable and lockable columns</li><li>Exporting to Excel and PDF</li><li>Single or multiple selection</li><li>Customization using templates</li><li>Drag &amp; drop support</li><li>Smart AI features that work with natural language queries</li></ul><p>From the characteristics described, you can notice that we are talking about a component that should be used when users want to see many rows and fields at the same time and be able to manipulate them.</p><p>Some ideal use cases for this component are administrative panels, ERPs, support dashboards, internal tools or any interface where data takes priority over aesthetics.</p><h2 id="the-blazor-listview-component">The Blazor ListView Component</h2><p>The Blazor ListView component can be considered a freer component than the Blazor Grid. Instead of a tabular structure, the data is provided to you as a context that you can render as you wish. This means you can customize the UI to create cards, timelines, custom rows or any Razor structure you want.</p><p>Some of the component&rsquo;s main features include:</p><ul><li>Customizable built-in paging</li><li>On-demand loading</li><li>Handling of CUD operations through built-in commands</li><li>Custom data source operations</li><li>Fully customizable templates</li></ul><p>This component is ideal when the data is not tabular or when the layout needs to adapt to multiple screen styles. Some examples of when to use this component are news feeds, visual catalogs, item galleries, among others.</p><h2 id="when-to-use-a-grid-vs.-a-listview">When to Use a Grid vs. a ListView?</h2><p>In addition to the analysis we&rsquo;ve done for each component, I&rsquo;d like to share some comparative points about when to use a Grid and when to use a ListView.</p><p>It&rsquo;s advisable to use a Grid when:</p><ul><li>You need the user to be able to compare values across rows</li><li>Each record has many fields to be analyzed</li><li>Screen space is not an issue</li><li>You require complex operations such as sorting, filtering or grouping</li><li>You need to edit data in bulk</li><li>You need to export table data</li></ul><p>On the other hand, it&rsquo;s advisable to use a ListView when:</p><ul><li>Records have few fields or are ideal for visually rich interfaces</li><li>User interaction involves viewing items one at a time, not analyzing them</li><li>You need full control over the layout of each item</li><li>You are building some type of app that includes a card-like component or similar</li></ul><p>Let&rsquo;s see how both views look in a real example.</p><h2 id="creating-a-blazor-project-with-listview-and-grid">Creating a Blazor Project with ListView and Grid</h2><p>To demonstrate the difference between the two components and see when it&rsquo;s convenient to use one over the other, let&rsquo;s create a component that loads 50 orders and allows switching the view between a ListView-style and a Grid-style.</p><p>To achieve this, start by creating a project with the <strong>Blazor Web App</strong> template, selecting <strong>Interactive render mode</strong> in <code>Server</code> and <strong>Interactivity location</strong> in <code>Global</code>. Then, you can follow the <a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/getting-started/web-app">official installation guide for Telerik UI for Blazor</a> to install the Telerik components in your project.</p><h3 id="defining-the-app-data-model">Defining the App Data Model</h3><p>Once the Telerik components are installed in the project, the next step will be to create the data model that represents a sales order in the system. For this, we will use a <code>record</code> type as follows:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> record <span class="token function">SalesOrder</span><span class="token punctuation">(</span>
    <span class="token keyword">int</span> OrderId<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Customer<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Product<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Category<span class="token punctuation">,</span>
    <span class="token keyword">int</span> Quantity<span class="token punctuation">,</span>
    <span class="token keyword">decimal</span> UnitPrice<span class="token punctuation">,</span>
    DateTime OrderDate<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Status<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> Total <span class="token operator">=</span><span class="token operator">&gt;</span> Quantity <span class="token operator">*</span> UnitPrice<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Now, let&rsquo;s create a service that will generate the fictitious orders.</p><h3 id="creating-a-data-service">Creating a Data Service</h3><p>Let&rsquo;s create a service class that will be used to generate the fictitious orders. For this, let&rsquo;s add an interface and its corresponding implementation:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">ISalesService</span>
<span class="token punctuation">{</span>
    IReadOnlyList<span class="token operator">&lt;</span>SalesOrder<span class="token operator">&gt;</span> <span class="token function">GetOrders</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> <span class="token keyword">class</span> <span class="token class-name">SalesService</span> <span class="token punctuation">:</span> ISalesService
<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> Customers <span class="token operator">=</span>
    <span class="token punctuation">[</span>
        <span class="token string">"Contoso"</span><span class="token punctuation">,</span> <span class="token string">"Fabrikam"</span><span class="token punctuation">,</span> <span class="token string">"Adventure Works"</span><span class="token punctuation">,</span>
        <span class="token string">"Northwind Traders"</span><span class="token punctuation">,</span> <span class="token string">"Telerik"</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 punctuation">(</span><span class="token keyword">string</span> Product<span class="token punctuation">,</span> <span class="token keyword">string</span> Category<span class="token punctuation">,</span> <span class="token keyword">decimal</span> Price<span class="token punctuation">)</span><span class="token punctuation">[</span><span class="token punctuation">]</span> Catalog <span class="token operator">=</span>
    <span class="token punctuation">[</span>
        <span class="token punctuation">(</span><span class="token string">"Laptop Pro 15"</span><span class="token punctuation">,</span>      <span class="token string">"Electronics"</span><span class="token punctuation">,</span> <span class="token number">1299</span><span class="token punctuation">.</span>99m<span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">(</span><span class="token string">"Wireless Mouse"</span><span class="token punctuation">,</span>     <span class="token string">"Electronics"</span><span class="token punctuation">,</span>   <span class="token number">45</span><span class="token punctuation">.</span>00m<span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">(</span><span class="token string">"4K Monitor"</span><span class="token punctuation">,</span>         <span class="token string">"Electronics"</span><span class="token punctuation">,</span>  <span class="token number">399</span><span class="token punctuation">.</span>50m<span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">(</span><span class="token string">"Mechanical Keyboard"</span><span class="token punctuation">,</span><span class="token string">"Electronics"</span><span class="token punctuation">,</span>  <span class="token number">129</span><span class="token punctuation">.</span>99m<span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">(</span><span class="token string">"Running Shoes"</span><span class="token punctuation">,</span>      <span class="token string">"Clothing"</span><span class="token punctuation">,</span>      <span class="token number">89</span><span class="token punctuation">.</span>95m<span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">(</span><span class="token string">"Winter Jacket"</span><span class="token punctuation">,</span>      <span class="token string">"Clothing"</span><span class="token punctuation">,</span>     <span class="token number">159</span><span class="token punctuation">.</span>99m<span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">(</span><span class="token string">"Organic Coffee"</span><span class="token punctuation">,</span>     <span class="token string">"Food"</span><span class="token punctuation">,</span>          <span class="token number">24</span><span class="token punctuation">.</span>50m<span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">(</span><span class="token string">"Premium Tea Box"</span><span class="token punctuation">,</span>    <span class="token string">"Food"</span><span class="token punctuation">,</span>          <span class="token number">18</span><span class="token punctuation">.</span>75m<span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">(</span><span class="token string">"Office Chair"</span><span class="token punctuation">,</span>       <span class="token string">"Furniture"</span><span class="token punctuation">,</span>    <span class="token number">249</span><span class="token punctuation">.</span>00m<span class="token punctuation">)</span><span class="token punctuation">,</span>
        <span class="token punctuation">(</span><span class="token string">"Standing Desk"</span><span class="token punctuation">,</span>      <span class="token string">"Furniture"</span><span class="token punctuation">,</span>    <span class="token number">599</span><span class="token punctuation">.</span>00m<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">static</span> <span class="token keyword">readonly</span> <span class="token keyword">string</span><span class="token punctuation">[</span><span class="token punctuation">]</span> Statuses <span class="token operator">=</span>
        <span class="token punctuation">[</span><span class="token string">"Pending"</span><span class="token punctuation">,</span> <span class="token string">"Shipped"</span><span class="token punctuation">,</span> <span class="token string">"Delivered"</span><span class="token punctuation">,</span> <span class="token string">"Cancelled"</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> IReadOnlyList<span class="token operator">&lt;</span>SalesOrder<span class="token operator">&gt;</span> <span class="token function">GetOrders</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> random <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 number">42</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> orders <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">List</span><span class="token operator">&lt;</span>SalesOrder<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator">&lt;=</span> <span class="token number">50</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span>
        <span class="token punctuation">{</span>
            <span class="token keyword">var</span> item <span class="token operator">=</span> Catalog<span class="token punctuation">[</span>random<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span>Catalog<span class="token punctuation">.</span>Length<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
            orders<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">SalesOrder</span><span class="token punctuation">(</span>
                OrderId<span class="token punctuation">:</span> <span class="token number">1000</span> <span class="token operator">+</span> i<span class="token punctuation">,</span>
                Customer<span class="token punctuation">:</span> Customers<span class="token punctuation">[</span>random<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span>Customers<span class="token punctuation">.</span>Length<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
                Product<span class="token punctuation">:</span> item<span class="token punctuation">.</span>Product<span class="token punctuation">,</span>
                Category<span class="token punctuation">:</span> item<span class="token punctuation">.</span>Category<span class="token punctuation">,</span>
                Quantity<span class="token punctuation">:</span> random<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                UnitPrice<span class="token punctuation">:</span> item<span class="token punctuation">.</span>Price<span class="token punctuation">,</span>
                OrderDate<span class="token punctuation">:</span> DateTime<span class="token punctuation">.</span>Today<span class="token punctuation">.</span><span class="token function">AddDays</span><span class="token punctuation">(</span><span class="token operator">-</span>random<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">60</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
                Status<span class="token punctuation">:</span> Statuses<span class="token punctuation">[</span>random<span class="token punctuation">.</span><span class="token function">Next</span><span class="token punctuation">(</span>Statuses<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>
        <span class="token punctuation">}</span>

        <span class="token keyword">return</span> orders<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>The code is simple, defining arrays with information that will simulate a business dataset, as well as generating 50 orders by randomly combining the arrays.</p><p>To be able to inject the service, we will go to <code>Program.cs</code>, where we will add it as <code>Singleton</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">AddSingleton<span class="token punctuation">&lt;</span>ISalesService<span class="token punctuation">,</span> SalesService<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><p>With the service ready, we can start with the visual tests.</p><h3 id="preparing-the-orders-page">Preparing the Orders Page</h3><p>In the <code>Components\Pages</code> folder we will create a new component called <code>SalesOrders.razor</code>, which will look as follows:</p><pre class=" language-xml"><code class="prism  language-xml">@page "/sales-orders"
@using SalesOrderListGridDemo.Services
@using SalesOrderListGridDemo.Models
@rendermode InteractiveServer
@inject ISalesService SalesService

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>PageTitle</span><span class="token punctuation">&gt;</span></span>Sales Orders<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 punctuation">&gt;</span></span>Sales Orders<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>
    Switch between the <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>ListView<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span> and the
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>DataGrid<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span> using the toggle below.
<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>sales-toolbar<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>TelerikButtonGroup</span> <span class="token attr-name">SelectionMode</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ButtonGroupSelectionMode.Single<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>ButtonGroupToggleButton</span> <span class="token attr-name">Selected</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(currentView == ViewMode.ListView)<span class="token punctuation">"</span></span>
                                 <span class="token attr-name">SelectedChanged</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(_ =&gt; SetView(ViewMode.ListView))<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            ListView
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ButtonGroupToggleButton</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ButtonGroupToggleButton</span> <span class="token attr-name">Selected</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(currentView == ViewMode.Grid)<span class="token punctuation">"</span></span>
                                 <span class="token attr-name">SelectedChanged</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(_ =&gt; SetView(ViewMode.Grid))<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            DataGrid
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ButtonGroupToggleButton</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikButtonGroup</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>@orders.Count orders loaded<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>

@code {
    private enum ViewMode { ListView, Grid }

    private List<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>SalesOrder</span><span class="token punctuation">&gt;</span></span> orders = new();
    private ViewMode currentView = ViewMode.ListView;

    protected override void OnInitialized()
    {
        orders = SalesService.GetOrders().ToList();
    }

    private void SetView(ViewMode view) =&gt; currentView = view;
}
</code></pre><p>In the code of the previous page, you can notice a few things:</p><ul><li>We have created a <code>enum</code> with the options <code>ListView</code> and <code>Grid</code>, which will allow us to switch between views.</li><li>We load the orders into <code>OnInitialized</code>.</li><li>The method <code>SetView</code> allows changing the view.</li><li>We use a <a target="_blank" href="https://www.telerik.com/blazor-ui/buttongroup">Blazor Button Group</a> component, ideal for handling multiple grouped buttons. In this case, we implement it to activate one view or the other through the assignment of a state with <code>Selected</code>. Also, when a button is clicked, <code>SelectedChanged</code> is fired, which invokes the method <code>SetView</code>.</li></ul><p>With the options section ready, let&rsquo;s render the ListView component.</p><h3 id="rendering-a-blazor-listview">Rendering a Blazor ListView</h3><p>The next step will be to render the ListView. To do this, we will add a conditional block and use the component <code>TelerikListView</code>:</p><pre class=" language-xml"><code class="prism  language-xml">@if (currentView == ViewMode.ListView)
{
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikListView</span> <span class="token attr-name">Data</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@orders<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>9<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 attr-name">Context</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>order<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>order-card<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>order-card-header<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>order-card-id<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>#@order.OrderId<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>status-badge status-@order.Status<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@order.Status<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>order-card-product<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@order.Product<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>order-card-meta<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@order.Customer   @order.Category<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>order-card-meta<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@order.OrderDate.ToString("MMM dd, yyyy")<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>order-card-footer<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>text-muted<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@order.Quantity   @order.UnitPrice.ToString("C")<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>order-total<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@order.Total.ToString("C")<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 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>TelerikListView</span><span class="token punctuation">&gt;</span></span>
}
</code></pre><p>In the previous code, the property <code>Data</code> binds to the service&rsquo;s list. We configure some additional properties such as paging (<code>Pageable</code>) and number of items per page (<code>PageSize</code>). Also, we use a <code>Template</code> to define a custom view, highlighting only the most important data such as product, customer, category, date, total, etc.</p><p>To make the layout look correct, we will add some visual styles in <code>wwwroot/app.css</code>:</p><pre class=" language-css"><code class="prism  language-css"><span class="token selector"><span class="token class">.sales-toolbar</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">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
    <span class="token property">gap</span><span class="token punctuation">:</span> <span class="token number">1</span>rem<span class="token punctuation">;</span>
    <span class="token property">margin-bottom</span><span class="token punctuation">:</span> <span class="token number">1</span>rem<span class="token punctuation">;</span>
    <span class="token property">flex-wrap</span><span class="token punctuation">:</span> wrap<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.order-card</span> </span><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 hexcode">#e2e8f0</span><span class="token punctuation">;</span>
    <span class="token property">border-radius</span><span class="token punctuation">:</span> <span class="token number">0.5</span>rem<span class="token punctuation">;</span>
    <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token number">1</span>rem<span class="token punctuation">;</span>
    <span class="token property">background</span><span class="token punctuation">:</span> <span class="token hexcode">#fff</span><span class="token punctuation">;</span>
    <span class="token property">box-shadow</span><span class="token punctuation">:</span> <span class="token number">0</span> <span class="token number">1</span>px <span class="token number">2</span>px <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.04</span><span class="token punctuation">)</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">gap</span><span class="token punctuation">:</span> <span class="token number">0.5</span>rem<span class="token punctuation">;</span>
    <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">100%</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.order-card-header</span>,
<span class="token class">.order-card-footer</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">justify-content</span><span class="token punctuation">:</span> space-between<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 punctuation">}</span>

<span class="token selector"><span class="token class">.order-card-product</span> </span><span class="token punctuation">{</span>
    <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token number">1.1</span>rem<span class="token punctuation">;</span>
    <span class="token property">font-weight</span><span class="token punctuation">:</span> <span class="token number">600</span><span class="token punctuation">;</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token hexcode">#0f172a</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.order-card-meta</span> </span><span class="token punctuation">{</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token hexcode">#64748b</span><span class="token punctuation">;</span>
    <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token number">0.9</span>rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.order-total</span> </span><span class="token punctuation">{</span>
    <span class="token property">font-weight</span><span class="token punctuation">:</span> <span class="token number">700</span><span class="token punctuation">;</span>
    <span class="token property">color</span><span class="token punctuation">:</span> <span class="token hexcode">#0f172a</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.status-badge</span> </span><span class="token punctuation">{</span>
    <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span>
    <span class="token property">padding</span><span class="token punctuation">:</span> <span class="token number">0.15</span>rem <span class="token number">0.6</span>rem<span class="token punctuation">;</span>
    <span class="token property">border-radius</span><span class="token punctuation">:</span> <span class="token number">999</span>px<span class="token punctuation">;</span>
    <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token number">0.75</span>rem<span class="token punctuation">;</span>
    <span class="token property">font-weight</span><span class="token punctuation">:</span> <span class="token number">600</span><span class="token punctuation">;</span>
    <span class="token property">text-transform</span><span class="token punctuation">:</span> uppercase<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.status-Pending</span>   </span><span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token hexcode">#fef3c7</span><span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token hexcode">#92400e</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token selector"><span class="token class">.status-Shipped</span>   </span><span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token hexcode">#dbeafe</span><span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token hexcode">#1e40af</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token selector"><span class="token class">.status-Delivered</span> </span><span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token hexcode">#dcfce7</span><span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token hexcode">#166534</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token selector"><span class="token class">.status-Cancelled</span> </span><span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token hexcode">#fee2e2</span><span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token hexcode">#991b1b</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

</code></pre><p>When running the application, we can see the layout we created using the ListView:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/listview-sales-orders-sample.png?sfvrsn=ffdd2cd6_2" alt="Blazor ListView displaying sample sales orders" /></p><p>In the image above, you can notice that there is no comparison between orders; rather, there is navigation between the list items. If users wanted to group items, filter them or perform complex operations, this would not be the right component. Let&rsquo;s now see how to implement a Grid.</p><h3 id="adding-a-grid-component-to-the-app">Adding a Grid Component to the App</h3><p>Let&rsquo;s see how a Grid component looks, completing the <code>else</code> branch of the conditional in the code to add a <code>TelerikGrid</code>:</p><pre class=" language-xml"><code class="prism  language-xml">else
{
    <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>@orders<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>15<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">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">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 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(SalesOrder.OrderId)<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>Order #<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(SalesOrder.Customer)<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>Customer<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(SalesOrder.Product)<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<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(SalesOrder.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>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(SalesOrder.Quantity)<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>Qty<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>90px<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(SalesOrder.UnitPrice)<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>Unit 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>130px<span class="token punctuation">"</span></span> <span class="token attr-name">DisplayFormat</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{0:C}<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(SalesOrder.Total)<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>Total<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>130px<span class="token punctuation">"</span></span> <span class="token attr-name">DisplayFormat</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{0:C}<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(SalesOrder.OrderDate)<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>Date<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 attr-name">DisplayFormat</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{0:d}<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(SalesOrder.Status)<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>Status<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>130px<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>
}
</code></pre><p>In the previous code we can see the notable difference between the two components:</p><ol><li>It has parameters like <code>Sortable</code>, <code>Groupable</code>, <code>Resizable</code>, <code>Reorderable</code>, etc., which enable capabilities a user expects in a table-like format.</li><li>The number of items shown per page is set to 15, because each row takes up less space.</li><li>If needed, we could customize the columns to display custom content.</li></ol><p>When running the application, we will have a result like the following:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/sales-orders-grid-example.gif?sfvrsn=5d6f72e4_2" alt="Blazor DataGrid view displaying sales orders with columns" /></p><p>In the image above, you can see that we perform operations such as grouping, sorting and filtering rows.</p><h2 id="final-comparison-between-components">Final Comparison Between Components</h2><p>Once we have the application assembled and have seen how each component looks, we can reach the following conclusion:</p><ul><li>Use a ListView when you want to treat each record as an individual entity, customizing its visual hierarchy. It is a component intended to be consumed by users.</li><li>Use a DataGrid when you want a spreadsheet-like experience, with operations such as sorting, grouping, filtering, etc. It is a component intended to be operated by users.</li></ul><p>Each of the views has a different purpose, although it is possible to combine their use to create mixed experiences.</p><h2 id="conclusion">Conclusion</h2><p>Throughout this article we have examined the ListView and Grid components from Progress Telerik UI for Blazor. We have discussed the best scenarios for using each of them, as well as the implementation code to use them.</p><p>We can conclude that you should use a ListView when the information is intended for end users, requires a high degree of customization and offers a unique exploratory experience.</p><p>On the other hand, a DataGrid can be used to display multiple records in a tabular form when operations that enable analysis are needed, such as grouping, sorting, filtering, etc.</p><p>Now I invite you to create spectacular experiences using both components. The whole Telerik UI for Blazor library is available in the free 30-day trial, including the ListView and the DataGrid.</p><p><a target="_blank" href="https://www.telerik.com/try/ui-for-blazor" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/10827/17428155.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:e60e98a0-0744-48f5-8739-b3a848fd124b</id>
    <title type="text">DataGrids Are For... Financial Analysts</title>
    <summary type="text">As a financial analyst, you need the ability to explore your organization’s data—the DataGrid from Progress Telerik and Kendo UI gives you that ability.</summary>
    <published>2026-08-24T15:39:48Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Peter Vogel </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17427555/datagrids-are-for-financial-analysts"/>
    <content type="text"><![CDATA[<p><span class="featured">As a financial analyst, you need the ability to explore your organization&rsquo;s data&mdash;the DataGrid from Progress Telerik and Kendo UI gives you that ability.</span></p><p>If you are a financial analyst, your responsibility is to &ldquo;understand the numbers&rdquo;&mdash;not just in terms of what &ldquo;the numbers&rdquo; tell you about the past but what those numbers can tell you about the future. That understanding drives your ability both to make informed decisions yourself and to support others in making informed decisions.</p><p>As a financial analyst, you work with a variety of data from multiple sources, some of them external (market information, financial forecasts) and many of them internal (budgets, financial statements and other organization data). Essentially, you turn data into information so that information can drive decisions by becoming business intelligence.</p><p>Each of your organization&rsquo;s applications manages part of your organization&rsquo;s data and, as a result, can provide data that you need for that process. A data exploration tool, embedded in an application&rsquo;s user interface or as a standalone tool, can automatically bring together the information for any area of business. That commitment to a set of data can eliminate the time needed to extract, transform and load the data that a more general-purpose financial analysis tool (e.g., <a target="_blank" href="https://www.cubesoftware.com/">Cube</a>) requires. You just open the application and there&rsquo;s your data, ready for you to work with.</p><p>That&rsquo;s only useful to you, however, if the application&rsquo;s user interface goes beyond simple transaction management&mdash;if the application includes a tool that enables you to work with and explore the data embedded in the application. You need a tool that, among other things, can:</p><ul><li>Work with data from a wide variety of sources</li><li>Handle large amounts of data but remain responsive (you don&rsquo;t want to have to wait on your data being displayed so that you can work on it)</li><li>Format the data to highlight key issues</li><li>Give you the ability to change the data display as you&rsquo;re working with the data to support whatever analysis you need</li><li>Integrate support for Large Language Models to let you leverage AI analytics</li><li>Export the data so that you can use it in other tools</li><li>Support looking at the &ldquo;big picture&rdquo; while also letting you drill down into the essential detail</li></ul><p>And, critically, you need a tool that works &ldquo;the way you expect&rdquo; so you don&rsquo;t have to figure out how to use the tool and can concentrate on the data the grid delivers.</p><p>The Progress Telerik and Kendo UI DataGrid does all those things. (<a href="https://demos.telerik.com/blazor-ui/grid/overview" target="_blank">See the Blazor DataGrid Demo</a> for example.) Some of those features are available in any implementation of the grid (&ldquo;works the way you expect,&rdquo; for instance). Some features will require you to specify to the application&rsquo;s developers how you want the grid to work.</p><p>If, for example, you want the DataGrid to work with a data source that includes lot of rows, you&rsquo;ll want developers to enable the DataGrid&rsquo;s &ldquo;load on demand&rdquo; feature. Similarly, if you&rsquo;re going to be working with data that others will be updating, you&rsquo;ll want your grid to be implemented using observables that let you see those changes as early as possible.</p><h2 id="the-initial-display">The Initial Display</h2><p>Typically, for example, you&rsquo;ll want to have the DataGrid configured so that, as you open the application, the grid retrieves the data that you normally expect to work with. But if you&rsquo;ll need to look at several different data sources as part of analyzing the data, then you&rsquo;ll also want to have the DataGrid configured to allow you to dynamically switch to that other data.</p><p>For that initial display, you can also have your grid act as a dashboard that highlights cells (or whole rows) that signal key issues. If there are specific values that you want highlighted in the grid, you can have those values built in. But, if you want, you can also have the ability to change the values/thresholds used to highlight the data you&rsquo;re interested in.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/grid-persona-financial-analyst-01---formatting.png?sfvrsn=33194d2f_2" alt="A Telerik DataGrid with rows and individual cells formatted in a variety of colors. Some columns are highlighted using a bar chart format by levering the cell’s background fill color; Other columns use icons to flag key numbers or changes." /></p><p>You can, of course, sort the grid&rsquo;s rows into the order you need. But you can also, at any time, reorder the grid&rsquo;s columns (or suppress columns) to pull together the data you&rsquo;re interested right now.</p><p>And, also by default, you can filter the grid to show just the rows you need for a particular analysis. You can filter by arbitrary values (e.g., &ldquo;variances greater than 5%&rdquo;) or have a checkbox option for specific columns that makes it easier to select the rows you want. The DataGrid&rsquo;s toolbar also includes a search textbox that you can use to find specific rows.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/grid-persona-financial-analyst-02---filter-sort.png?sfvrsn=debc87a1_2" alt="A Telerik DataGrid showing the sort options for a column (ascending and descending) plus the checkbox option for filtering data by value" /></p><h2 id="supporting-multiple-configurations">Supporting Multiple Configurations</h2><p>That&rsquo;s already multiple options and, as a result, a lot of opportunities for you to dynamically reconfigure the DataGrid as you&rsquo;re working with it. Fortunately, you can also have the DataGrid save and restore your configuration settings so that you can quickly switch between different grid configurations as you need them.</p><h2 id="ai-support">AI Support</h2><p>In the modern world, you&rsquo;ll also want to decide how much of the grid&rsquo;s AI support you&rsquo;ll want to have enabled. The DataGrid gives you multiple ways to access AI processors:</p><ul><li>The AI Toolbar for Data Operations lets you use plain language commands to explore your data through filtering, grouping and sorting</li><li>The AI Toolbar for Highlighting lets you flag rows that meet complicated conditions</li><li>The AI Assistant adds a column to your table with a button that opens an AI prompt window to let you ask any question that makes sense</li></ul><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/grid-persona-financial-analyst-03---artificial-intelligence.gif?sfvrsn=6086b35_2" alt="An animated GIF of the Telerik DataGrid showing a user opening the grid’s AI Assistant from. The user then selects a “Sort by Salary” quick action to resort the grid" /></p><h2 id="grouping-and-aggregating-data">Grouping and Aggregating Data</h2><p>The DataGrid also lets you organize rows into meaningful groups. You can, as you&rsquo;re viewing the data, select the rows that you want to group or drag/drop columns or rows to create the groups you need. (See <a href="https://www.telerik.com/kendo-react-ui/components/grid/grouping" target="_blank">Grouping options for KendoReact Grid</a> as an example.)</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/grid-persona-financial-analyst-04---grouping.png?sfvrsn=416bbcd3_2" alt="An animated GIF of the Telerik DataGrid showing how columns can be dynamically dragged to form groups based on the data in columns" /></p><p>Once you create groups, you can include displays of aggregate values for those groups. But you don&rsquo;t have to create groups to get aggregate values. You can, as you&rsquo;re working with the grid, select the rows and columns you want to be included in your aggregated values.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/grid-persona-financial-analyst-05---grouping-aggregrates.gif?sfvrsn=c22705fb_2" alt="An animated GIF of the Telerik DataGrid showing the user selecting rows and a set of aggregate values at the bottom of the grid" /></p><p>While the DataGrid supports the typical aggregate functions &ldquo;out of the box&rdquo; (e.g., count, sum, average, etc.), you can have more sophisticated aggregates integrated into your grids.</p><h2 id="drilling-down">Drilling Down</h2><p>You can also have the DataGrid configured as a hierarchy of high-level sections that you can drill down into to reveal nested, detailed data. You can have your data nested as many levels deep as you need, and each level can have its own, distinct set of columns and aggregates. Effectively, you can expand your high-level rows into a different grid of detail data that reflects your needs at each level.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/grid-persona-financial-analyst-06---hierarchy-aggregate.png?sfvrsn=ebd98085_2" alt="The Telerik DataGrid organized as a hierarchy. As the top level of the hierarchy is expanded, it opens a new grid with its own aggregate values" /></p><h2 id="exporting-data">Exporting Data</h2><p>The DataGrid also supports exporting data in a variety of formats so you can integrate other tools into your analysis (or you can just copy grid data so you can paste into some other tool).</p><p>You&rsquo;re not limited to copying or exporting all the data in the grid. While you&rsquo;re working with the grid, you can select the rows and columns you want you want to export into another tool (or just share with others&mdash;the grid supports multiple export formats, including PDF). If your application is using the Telerik Chart control you can export your data straight into the chart for visualization.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/grid-persona-financial-analyst-07---export.png?sfvrsn=bb05b579_2" alt="The Telerik DataGrid with two separate sets of rows selected and the export menu opened showing the Copy/Export options: (Copy, Copy with Headers, Export, Export with Headers, and Export to Chart)" /></p><p>And, if you are using the DataGrid to facilitate selecting the data to export into some other tool, the grid supports a &ldquo;high density&rdquo; mode that packs more data on the screen. (See the Kendo UI for <a href="https://www.telerik.com/kendo-angular-ui/components/grid/grouping/modes" target="_blank">Angular Grid Grouping Display Modes</a> page for example.)</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/grid-persona-financial-analyst-08---high-density.gif?sfvrsn=496c1720_2" alt="An animated GIF of the Telerik DataGrid control showing the grid shifting from a more readable view to a view with narrower columns and shorter rows to fit more data into less space" /></p><p>As an analyst, you can&rsquo;t really have too much data&mdash;provided, of course, your tools let you review and explore that data effectively. The Telerik DataGrid gives you a customizable window into the data for any application, turning the application itself into a tool for analyzing its own data. That enables you to create the business intelligence that drives better decisions, which is, after all, the purpose of being a financial analyst.</p><hr /><p>Check out the <a target="_blank" href="https://www.telerik.com/devcraft">Telerik DevCraft</a> suite of products to learn about the DataGrid in your favorite .NET or JavaScript flavor. And you can try out the whole suite free for 30 days.</p><p><a href="https://www.telerik.com/try/devcraft-ultimate" target="_blank" class="Btn">Try Telerik DevCraft</a></p><img src="https://feeds.telerik.com/link/10827/17427555.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:18a8c83a-19ac-4a74-b424-ebdf4033bd47</id>
    <title type="text">Create a Kendo UI for Angular Dinner Picker</title>
    <summary type="text">Sick of picking where to go for dinner? Build this little Angular app to help you choose!</summary>
    <published>2026-08-20T17:09:35Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Jonathan Gamble </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17424591/create-kendo-ui-angular-dinner-picker"/>
    <content type="text"><![CDATA[<p><span class="featured">Sick of picking where to go for dinner? Build this little Angular app to help you choose!</span></p><p>Do you ever argue about where to eat? If you&rsquo;re like me, you can&rsquo;t think of places half the time, and you just wish someone would pick for you. Well, here you go!</p><p><img sf-image-responsive="true" src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/chrome_v90f4i218o.gif?sfvrsn=5c1916fc_2" height="717" style="max-width:100%;height:auto;" title="Angular Restaurant Picker" width="501" alt="Angular Restaurant Picker with options for dine-in or take-out" sf-size="382850" /></p><h2 id="tldr">TL;DR</h2><p>This app will create a random dinner picker from JSON data in Angular using Progress Kendo UI. You can choose from Takeout or Dine-in.</p><h2 id="setup">Setup</h2><p>Create a new <a target="_blank" href="https://angular.dev/">Angular</a>&nbsp;app:</p><pre class=" language-html"><code class="prism  language-html">ng new angular-dinner-picker
</code></pre><h3 id="get-a-kendo-ui-license">Get a Kendo UI License</h3><p>Log in to Progress and purchase a Kendo UI for Angular license, or try it for free.</p><p><img sf-image-responsive="true" src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/image.png?sfvrsn=bece1600_2" height="840" style="max-width:100%;height:auto;" title="Kendo UI License Key" width="1195" alt="Kendo UI License Key" sf-size="107716" /></p><p>Download the actual key, as you will need it later for deployment.</p><h3 id="install-kendo-ui-licensing">Install Kendo UI Licensing</h3><pre class=" language-bash"><code class="prism  language-bash"><span class="token function">npm</span> i -S @progress/kendo-licensing
</code></pre><p>Then run:</p><pre class=" language-bash"><code class="prism  language-bash">npx kendo-ui-license activate
</code></pre><p>This will set up Kendo UI on your local machine for this project. Make sure you have a working license at this point, and to <a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/licensing">follow the correct setup</a>.</p><h3 id="install-packages">Install Packages</h3><pre class=" language-bash"><code class="prism  language-bash">ng add @progress/kendo-angular-buttons
ng add @progress/kendo-angular-dropdowns
ng add @progress/kendo-angular-layout
</code></pre><pre class=" language-bash"><code class="prism  language-bash"><span class="token function">npm</span> i -S <span class="token function">npm</span> <span class="token function">install</span> @angular/forms @progress/kendo-svg-icons @progress/kendo-theme-default
</code></pre><p> You may need to also install <code>@angular/localize</code>.</p><h3 id="install-tailwind">Install Tailwind</h3><p>Make sure Angular is configured for Tailwind. Follow the <a target="_blank" href="https://tailwindcss.com/docs/installation/framework-guides/angular">Tailwind Guide</a>.</p><h3 id="configure-styles">Configure Styles</h3><p>Make sure your styles are shown in <code>styles.css</code> correctly.</p><pre class=" language-bash"><code class="prism  language-bash">@import <span class="token string">'@progress/kendo-theme-default/dist/default-main.css'</span><span class="token punctuation">;</span>
@import <span class="token string">'tailwindcss'</span><span class="token punctuation">;</span>
</code></pre><h2 id="picker-component">Picker Component</h2><p>Generate a new picker component.</p><pre class=" language-bash"><code class="prism  language-bash">ng g c picker
</code></pre><h3 id="model">Model</h3><p>Create a model at <code>picker.model.ts</code>.</p><pre class=" language-bash"><code class="prism  language-bash"><span class="token function">export</span> <span class="token function">type</span> RestaurantMode <span class="token operator">=</span> <span class="token string">'dine-in'</span> <span class="token operator">|</span> <span class="token string">'takeout'</span><span class="token punctuation">;</span>

<span class="token function">export</span> <span class="token function">type</span> Restaurant <span class="token operator">=</span> <span class="token punctuation">{</span>
  id: number<span class="token punctuation">;</span>
  name: string<span class="token punctuation">;</span>
  cuisine: string<span class="token punctuation">;</span>
  mode: RestaurantMode<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token function">export</span> <span class="token function">type</span> RestaurantModeFilter <span class="token operator">=</span> <span class="token string">'all'</span> <span class="token operator">|</span> RestaurantMode<span class="token punctuation">;</span>
</code></pre><h3 id="data">Data</h3><p>You can declare your data in the <code>restaurants.ts</code>. You could obviously get more sophisticated and import data from a database if you like.</p><pre class=" language-bash"><code class="prism  language-bash"><span class="token function">import</span> <span class="token punctuation">{</span> Restaurant <span class="token punctuation">}</span> from <span class="token string">"./picker.model"</span><span class="token punctuation">;</span>

<span class="token function">export</span> const restaurants: Restaurant<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 string">"id"</span><span class="token keyword">:</span> 1,
    <span class="token string">"name"</span><span class="token keyword">:</span> <span class="token string">"Monjunis"</span>,
    <span class="token string">"cuisine"</span><span class="token keyword">:</span> <span class="token string">"Italian"</span>,
    <span class="token string">"mode"</span><span class="token keyword">:</span> <span class="token string">"dine-in"</span>
  <span class="token punctuation">}</span>,
  <span class="token punctuation">{</span>
    <span class="token string">"id"</span><span class="token keyword">:</span> 2,
    <span class="token string">"name"</span><span class="token keyword">:</span> <span class="token string">"Strawn's Eat Shop"</span>,
    <span class="token string">"cuisine"</span><span class="token keyword">:</span> <span class="token string">"Southern Diner"</span>,
    <span class="token string">"mode"</span><span class="token keyword">:</span> <span class="token string">"dine-in"</span>
  <span class="token punctuation">}</span>,
  <span class="token punctuation">{</span>
    <span class="token string">"id"</span><span class="token keyword">:</span> 3,
    <span class="token string">"name"</span><span class="token keyword">:</span> <span class="token string">"Country Tavern"</span>,
    <span class="token string">"cuisine"</span><span class="token keyword">:</span> <span class="token string">"BBQ"</span>,
    <span class="token string">"mode"</span><span class="token keyword">:</span> <span class="token string">"dine-in"</span>
  <span class="token punctuation">}</span>,
  
  <span class="token punctuation">..</span>.
  
<span class="token punctuation">]</span><span class="token punctuation">;</span>
</code></pre><p> I used my city&rsquo;s info so I can actually use the app! You can customize this and deploy it separately for different situations or cities!</p><h3 id="picker-class">Picker Class</h3><p>Create the picker classes at <code>picker.ts</code>.</p><pre class=" language-bash"><code class="prism  language-bash"><span class="token function">import</span> <span class="token punctuation">{</span> ChangeDetectionStrategy, Component <span class="token punctuation">}</span> from <span class="token string">'@angular/core'</span><span class="token punctuation">;</span>
<span class="token function">import</span> <span class="token punctuation">{</span> FormsModule <span class="token punctuation">}</span> from <span class="token string">'@angular/forms'</span><span class="token punctuation">;</span>

<span class="token function">import</span> <span class="token punctuation">{</span> ButtonsModule <span class="token punctuation">}</span> from <span class="token string">'@progress/kendo-angular-buttons'</span><span class="token punctuation">;</span>
<span class="token function">import</span> <span class="token punctuation">{</span> DropDownsModule <span class="token punctuation">}</span> from <span class="token string">'@progress/kendo-angular-dropdowns'</span><span class="token punctuation">;</span>

<span class="token function">import</span> <span class="token punctuation">{</span> restaurants <span class="token punctuation">}</span> from <span class="token string">'./restaurants'</span><span class="token punctuation">;</span>
<span class="token function">import</span> <span class="token punctuation">{</span> Restaurant, RestaurantModeFilter <span class="token punctuation">}</span> from <span class="token string">'./picker.model'</span><span class="token punctuation">;</span>

<span class="token function">type</span> ModeOption <span class="token operator">=</span> <span class="token punctuation">{</span>
  label: string<span class="token punctuation">;</span>
  value: RestaurantModeFilter<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

@Component<span class="token punctuation">(</span><span class="token punctuation">{</span>
  selector: <span class="token string">'app-picker'</span>,
  standalone: true,
  imports: <span class="token punctuation">[</span>
    FormsModule,
    ButtonsModule,
    DropDownsModule
  <span class="token punctuation">]</span>,
  templateUrl: <span class="token string">'./picker.html'</span>,
  changeDetection: ChangeDetectionStrategy.OnPush
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token function">export</span> class Picker <span class="token punctuation">{</span>
  <span class="token function">readonly</span> restaurants <span class="token operator">=</span> restaurants<span class="token punctuation">;</span>

  <span class="token function">readonly</span> modeOptions: ModeOption<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>
      label: <span class="token string">'Any'</span>,
      value: <span class="token string">'all'</span>
    <span class="token punctuation">}</span>,
    <span class="token punctuation">{</span>
      label: <span class="token string">'Dine-in'</span>,
      value: <span class="token string">'dine-in'</span>
    <span class="token punctuation">}</span>,
    <span class="token punctuation">{</span>
      label: <span class="token string">'Takeout'</span>,
      value: <span class="token string">'takeout'</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">]</span><span class="token punctuation">;</span>

  selectedMode: RestaurantModeFilter <span class="token operator">=</span> <span class="token string">'all'</span><span class="token punctuation">;</span>
  selectedRestaurant: Restaurant <span class="token operator">|</span> null <span class="token operator">=</span> null<span class="token punctuation">;</span>

  get filteredRestaurants<span class="token punctuation">(</span><span class="token punctuation">)</span>: Restaurant<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>this.selectedMode <span class="token operator">==</span><span class="token operator">=</span> <span class="token string">'all'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> this.restaurants<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">return</span> this.restaurants.filter<span class="token punctuation">((</span>restaurant<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> restaurant.mode <span class="token operator">==</span><span class="token operator">=</span> this.selectedMode<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  pickRestaurant<span class="token punctuation">(</span><span class="token punctuation">)</span>: void <span class="token punctuation">{</span>
    const choices <span class="token operator">=</span> this.filteredRestaurants<span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span>choices.length <span class="token operator">==</span><span class="token operator">=</span> 0<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      this.selectedRestaurant <span class="token operator">=</span> null<span class="token punctuation">;</span>
      <span class="token keyword">return</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    const pickableChoices <span class="token operator">=</span>
      this.selectedRestaurant <span class="token operator">&amp;&amp;</span> choices.length <span class="token operator">&gt;</span> 1
        ? choices.filter<span class="token punctuation">((</span>restaurant<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span> restaurant.id <span class="token operator">!=</span><span class="token operator">=</span> this.selectedRestaurant?.id<span class="token punctuation">)</span>
        <span class="token keyword">:</span> choices<span class="token punctuation">;</span>

    const index <span class="token operator">=</span> Math.floor<span class="token punctuation">(</span>Math.random<span class="token punctuation">(</span><span class="token punctuation">)</span> * pickableChoices.length<span class="token punctuation">)</span><span class="token punctuation">;</span>

    this.selectedRestaurant <span class="token operator">=</span> pickableChoices<span class="token punctuation">[</span>index<span class="token punctuation">]</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><ul><li>Pick the restaurant with <code>pickResaturant()</code> function choosing a random JSON entry from the filtered results.</li><li>We have <code>any</code>, <code>dine-in</code> and <code>takeout</code>.</li><li>We will use the filtered results in the html template from <code>filteredResautrants()</code>.</li></ul><h3 id="picker-template">Picker Template</h3><pre class=" language-bash"><code class="prism  language-bash"><span class="token operator">&lt;</span>main
  class<span class="token operator">=</span><span class="token string">"grid min-h-screen place-items-center bg-[radial-gradient(circle_at_top,rgba(255,255,255,0.95),rgba(255,255,255,0)_28%),linear-gradient(180deg,#f7efe7_0%,#eef2f7_100%)] p-4 sm:p-6"</span><span class="token operator">&gt;</span>
  <span class="token operator">&lt;</span>section
    class<span class="token operator">=</span><span class="token string">"w-full max-w-sm rounded-4xl border border-slate-300/40 bg-white/92 px-6 py-7 shadow-[0_24px_60px_rgba(15,23,42,0.12)] backdrop-blur-[14px] sm:px-8 sm:py-8"</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>header class<span class="token operator">=</span><span class="token string">"space-y-3 text-center"</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>h1 class<span class="token operator">=</span><span class="token string">"m-0 text-[2.05rem] font-semibold leading-[0.98] tracking-tight text-slate-800 sm:text-[2.3rem]"</span><span class="token operator">&gt;</span>
        Restaurant Picker
      <span class="token operator">&lt;</span>/h1<span class="token operator">&gt;</span>

      <span class="token operator">&lt;</span>p class<span class="token operator">=</span><span class="token string">"mx-auto max-w-64 text-[1rem] leading-7 text-slate-500"</span><span class="token operator">&gt;</span>
        Pick where to eat without thinking about it.
      <span class="token operator">&lt;</span>/p<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>/header<span class="token operator">&gt;</span>

    <span class="token operator">&lt;</span>div class<span class="token operator">=</span><span class="token string">"my-6 h-px bg-slate-200/80"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span>/div<span class="token operator">&gt;</span>

    <span class="token operator">&lt;</span>div class<span class="token operator">=</span><span class="token string">"space-y-6"</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>div class<span class="token operator">=</span><span class="token string">"space-y-3 text-center"</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>label for<span class="token operator">=</span><span class="token string">"mode"</span> class<span class="token operator">=</span><span class="token string">"block text-[1.15rem] font-semibold leading-tight text-slate-800"</span><span class="token operator">&gt;</span>
          What kind?
        <span class="token operator">&lt;</span>/label<span class="token operator">&gt;</span>

        <span class="token operator">&lt;</span>kendo-dropdownlist id<span class="token operator">=</span><span class="token string">"mode"</span> class<span class="token operator">=</span><span class="token string">"w-full text-left"</span> <span class="token punctuation">[</span>data<span class="token punctuation">]</span><span class="token operator">=</span><span class="token string">"modeOptions"</span> textField<span class="token operator">=</span><span class="token string">"label"</span> valueField<span class="token operator">=</span><span class="token string">"value"</span>
          <span class="token punctuation">[</span>valuePrimitive<span class="token punctuation">]</span><span class="token operator">=</span><span class="token string">"true"</span> <span class="token punctuation">[</span><span class="token punctuation">(</span>ngModel<span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token operator">=</span><span class="token string">"selectedMode"</span> /<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>/div<span class="token operator">&gt;</span>

      <span class="token operator">&lt;</span>button kendoButton class<span class="token operator">=</span><span class="token string">"min-h-14 w-full text-base font-semibold"</span> themeColor<span class="token operator">=</span><span class="token string">"primary"</span> size<span class="token operator">=</span><span class="token string">"large"</span>
        rounded<span class="token operator">=</span><span class="token string">"large"</span> <span class="token punctuation">[</span>disabled<span class="token punctuation">]</span><span class="token operator">=</span><span class="token string">"filteredRestaurants.length === 0"</span> <span class="token punctuation">(</span>click<span class="token punctuation">)</span><span class="token operator">=</span><span class="token string">"pickRestaurant()"</span><span class="token operator">&gt;</span>
        Pick Restaurant
      <span class="token operator">&lt;</span>/button<span class="token operator">&gt;</span>

      @if <span class="token punctuation">(</span>selectedRestaurant<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token operator">&lt;</span>section
        class<span class="token operator">=</span><span class="token string">"rounded-[1.75rem] border border-rose-200/80 bg-[linear-gradient(135deg,rgba(255,241,242,0.92),rgba(255,255,255,0.98))] px-6 py-6 shadow-[inset_0_1px_0_rgba(255,255,255,0.7)]"</span>
        aria-live<span class="token operator">=</span><span class="token string">"polite"</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>div class<span class="token operator">=</span><span class="token string">"grid justify-items-center gap-4 text-center"</span><span class="token operator">&gt;</span>
          <span class="token operator">&lt;</span>p class<span class="token operator">=</span><span class="token string">"m-0 text-[0.78rem] font-bold uppercase tracking-[0.2em] text-rose-700"</span><span class="token operator">&gt;</span>Tonight<span class="token string">'s pick&lt;/p&gt;

          &lt;h2
            class="m-0 max-w-[11ch] text-[clamp(1.7rem,4vw,2rem)] font-semibold leading-[1.08] tracking-tight text-slate-900 text-balance"&gt;
            {{ selectedRestaurant.name }}
          &lt;/h2&gt;

          &lt;div class="grid justify-items-center gap-3"&gt;
            &lt;p class="m-0 text-[1rem] leading-6 text-slate-700"&gt;
              {{ selectedRestaurant.cuisine }}
            &lt;/p&gt;

            &lt;span
              class="inline-flex min-h-10 items-center justify-center rounded-full border border-slate-300/40 bg-white/90 px-5 text-sm font-bold text-slate-900 shadow-[0_6px_18px_rgba(15,23,42,0.06)]"&gt;
              {{ selectedRestaurant.mode === '</span>dine-in<span class="token string">' ? '</span>Dine-in<span class="token string">' : '</span>Takeout<span class="token string">' }}
            &lt;/span&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/section&gt;
      } @else {
      &lt;div
        class="rounded-[1.75rem] border border-dashed border-slate-400/60 bg-slate-50/75 px-5 py-6 text-center text-[0.95rem] leading-6 text-slate-500"&gt;
        @if (filteredRestaurants.length === 0) {
        No restaurants found for this option.
        } @else {
        Click the button to pick a restaurant.
        }
      &lt;/div&gt;
      }

      &lt;p class="text-center text-[0.82rem] font-semibold text-slate-500"&gt;
        {{ filteredRestaurants.length }}
        restaurant{{ filteredRestaurants.length === 1 ? '</span><span class="token string">' : '</span>s' <span class="token punctuation">}</span><span class="token punctuation">}</span> available
      <span class="token operator">&lt;</span>/p<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>/div<span class="token operator">&gt;</span>
  <span class="token operator">&lt;</span>/section<span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>/main<span class="token operator">&gt;</span>
</code></pre><p>Here&rsquo;s what&rsquo;s going on above:</p><ul><li>When we run <code>pickRestaurant()</code>, the app displays the filtered items as a signal.</li><li>The <code>kendo-dropdownlist</code> component uses <code>data</code> field for options with <code>label</code> and <code>value</code> matching the drop down.</li><li>We just add <code>kendoButton</code> to our <code>button</code> component to get the look we want. We can use Tailwind with the look!</li></ul><p>And it&rsquo;s that simple!</p><p><strong>Repo:</strong> <a target="_blank" href="https://github.com/jdgamble555/angular-dinner-picker">GitHub</a><br /><strong>Demo:</strong> <a target="_blank" href="https://angular-dinner-picker.vercel.app/">Vercel Serverless</a></p><p>You can try all of this yourself with the Kendo UI for Angular trial, free for 30 days.</p><p><a href="https://www.telerik.com/try/kendo-angular-ui" target="_blank" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/10827/17424591.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:4696baea-52cd-4e5f-b14b-8051d7ccbea9</id>
    <title type="text">Build, Automate and Observe: What You Can Do with Telerik, Kendo UI and AI Engineering This August</title>
    <summary type="text">See what you can do with this August’s updates across Progress Telerik, Kendo UI, Telerik Reporting, Fiddler and AI Observability.</summary>
    <published>2026-08-19T17:28:37Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Iva Borisova </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17423167/build-automate-observe-august"/>
    <content type="text"><![CDATA[<p><span class="featured">See what you can do with this August&rsquo;s updates across Progress Telerik, Kendo UI, Telerik Reporting, Fiddler and AI Observability.</span></p><p>AI stopped being &ldquo;the thing that writes your code&rdquo; a while ago. At this point, it&rsquo;s showing up at nearly every stage of the job: helping you build the app, automate the parts nobody wants to do by hand and keep an eye on how AI-powered features actually behave once real users touch them.</p><p>This month&rsquo;s updates across Progress Telerik, Kendo UI, Telerik Reporting, Fiddler and Progress AI Observability are a good snapshot of that shift. Here&rsquo;s what&rsquo;s new and, more importantly, what it actually lets you do.</p><p>But before that &hellip; We&rsquo;re walking through everything below on <strong>Progress AI Monthly: Telerik, Kendo UI &amp; More livestream on August 28, 9 a.m. ET.</strong> No sign-up, just show up.</p><p><a target="_blank" href="https://www.youtube.com/watch?v=yAdEAoDZTAc">YouTube</a> &middot; <a target="_blank" href="https://www.linkedin.com/events/7495480741330386945/">LinkedIn</a> &middot; <a target="_blank" href="https://www.twitch.tv/codeitlive">Twitch</a></p><h2 id="build">Build</h2><h3 id="more-ai-fewer-tokens">More AI, Fewer Tokens</h3><p>Before getting to what&rsquo;s new, a word on what&rsquo;s gotten leaner: token consumption is down by up to 45% across our .NET products, with better output quality on top of that, so the same prompt now costs you less and gets you more. Alongside that, the Agentic UI Generator for Telerik UI for Blazor, KendoReact and Kendo UI for Angular is now available as an agent-native skill instead of an externally configured tool, which makes it noticeably easier to deploy in air-gapped and regulated environments.</p><p><a target="_blank" href="https://www.telerik.com/mcp-servers">See the MCP servers page.</a></p><h3 id="build-ui-with-a-prompt">Build UI with a Prompt</h3><p>The Agentic UI Generator now supports Telerik UI for ASP.NET Core. Describe what you need in plain language, and it generates real, framework-aware code that follows the patterns you&rsquo;d already be using: grids, forms, navigation, layout, the works. That means less time assembling scaffolding and more time on the parts of the app that actually need your judgment.</p><p>A few things people are already using it for: business applications, admin portals, customer-facing dashboards, analytics views and data-driven workflows.</p><p><a target="_blank" href="https://www.telerik.com/aspnet-core-ui/documentation/ai-core/agentic-ui-generator/overview">See how the ASP.NET Core UI Generator works.</a></p><h3 id="build-reports-with-natural-language">Build Reports with Natural Language</h3><p>The Telerik Reporting AI Report Generator in Web Report Designer has already been generating charts and gauges from a plain description of what you want to see. Now that same idea extends to structured report content: tables, lists, crosstabs, summary rows, headers and footers, and grouped structures.</p><p>Describe the table you need. For instance, grouped by region, with subtotals. And the Generator builds it, then hands you a preview to check before anything actually lands in the report.</p><p><a target="_blank" href="https://demos.telerik.com/reporting/ai-report-generator?mode=ai-table-generator">Explore AI-Generated Tables, Lists and Crosstabs.</a></p><h3 id="build-faster-with-new-components">Build Faster with New Components</h3><p>Not every update this month is AI-flavored. Some of it is just useful, new UI:</p><ul><li>Telerik UI for Blazor: <a target="_blank" href="https://demos.telerik.com/blazor-ui/expansionpanel/overview">Expansion Panel</a></li><li>KendoReact: <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/inputs/otpinput">OTP Input</a></li><li>Kendo UI for Vue: <a target="_blank" href="https://www.telerik.com/kendo-vue-ui/components/barcodes">Barcode</a>, <a target="_blank" href="https://www.telerik.com/kendo-vue-ui/components/pdfviewer">PDF Viewer</a></li><li>Telerik UI for .NET MAUI: <a target="_blank" href="https://www.telerik.com/maui-ui/documentation/controls/charts/overview">Charts (Preview)</a>, <a target="_blank" href="https://www.telerik.com/maui-ui/documentation/controls/circularslider/overview">Circular Slider</a></li><li>Telerik UI for WPF: <a target="_blank" href="https://www.telerik.com/products/wpf/documentation/controls/radbuttons/features/smart-paste-button">Smart Paste Button</a>, <a target="_blank" href="https://www.telerik.com/products/wpf/documentation/controls/radinlineaiassistant/overview">Inline AI Assistant</a></li><li>Telerik UI for WinForms: <a target="_blank" href="https://www.telerik.com/products/winforms/documentation/controls/smartpastebutton/overview">Smart Paste Button</a></li></ul><h3 id="build-ai-ready-experiences-with-the-llm-kit">Build AI-ready Experiences with the LLM Kit</h3><p>The LLM Kit isn&rsquo;t just something you bolt on after the fact to keep tabs on your agents. It&rsquo;s a set of components for building the AI-powered experience itself. Chat-style interfaces, multi-step agent workflows, tool-call displays, approval steps, citations, checkpoints: the pieces you&rsquo;d otherwise have to design and build from scratch are already there, ready to wire into your app.</p><p>It gives you a real head start on shipping AI features that feel considered rather than bolted on, and, as a side benefit, your users and your team can actually see what the AI is doing along the way.</p><p>It&rsquo;s available now for <a target="_blank" href="https://demos.telerik.com/blazor-ui/llmkit/overview">Telerik UI for Blazor</a>, <a target="_blank" href="https://demos.telerik.com/aspnet-core/llm-kit">UI for ASP.NET Core</a>, <a target="_blank" href="https://demos.telerik.com/aspnet-mvc/llm-kit">UI for MVC</a>, <a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/conversational-ui/llm-kit">Kendo UI for Angular</a>, <a target="_blank" href="https://demos.telerik.com/kendo-ui/llm-kit/index">UI for jQuery</a> and <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/conversationalui/llm-kit">KendoReact</a>.</p><h2 id="automate">Automate</h2><h3 id="automate-application-upgrades">Automate Application Upgrades</h3><p>Nobody enjoys upgrading a codebase, and it&rsquo;s rarely the highest-value use of anyone&rsquo;s afternoon. AI-assisted migration is now available for Telerik UI for ASP.NET Core, UI for MVC, UI for WPF, KendoReact and Kendo UI for jQuery through the Upgrade Assistant (part of the MCP engine). It handles the changes it already knows how to make on its own and walks you through the ones that need a human decision, so version bumps stop eating whole sprints.</p><h3 id="automate-reporting-operations-with-ai-agents">Automate Reporting Operations with AI Agents</h3><p>Telerik Report Server now ships with a native MCP Server, so assistants like Copilot, Claude, Cursor and Codex can work directly inside your reporting environment instead of going through a custom integration you&rsquo;d have to build and maintain. Once you&rsquo;ve set up a Personal Access Token, agents can:</p><ul><li>Generate reports</li><li>Manage data sources</li><li>Schedule reports</li><li>Handle general reporting-environment admin</li></ul><p><a target="_blank" href="https://www.telerik.com/report-server/documentation/dotnet-docs/mcp-server">See Report Server MCP docs.</a></p><h3 id="automate-debugging-with-fiddler-mcp">Automate Debugging with Fiddler MCP</h3><p>Debugging gets a lot more useful when your AI assistant can see what actually happened over the wire, not just what the code says should happen. Fiddler MCP connects your coding assistant to real traffic captured in Fiddler Everywhere, actual requests, responses, headers, status codes, so it&rsquo;s reasoning from evidence instead of guessing. Set it up with one click inside Fiddler Everywhere, then go from &ldquo;something&rsquo;s broken&rdquo; to a fix without leaving your IDE.</p><p><a target="_blank" href="https://www.telerik.com/fiddler/fiddler-everywhere/mcp">Explore Fiddler Everywhere MCP.</a></p><h3 id="automate-licensing-for-cicd">Automate Licensing for CI/CD</h3><p>Automation isn&rsquo;t only about what your agents do. It&rsquo;s also about what shouldn&rsquo;t need a human to manage. Deployment Keys give you a dedicated way to license automated builds and deployments: each key ties to a registered application and the exact Telerik or Kendo products it uses, so tracking, monitoring and reporting on product usage across your CI/CD environments gets a lot less manual.</p><h2 id="observe">Observe</h2><p>Building faster and automating more only gets you so far if you can&rsquo;t tell what your AI systems are actually doing once they&rsquo;re live. That&rsquo;s the part teams tend to underinvest in until something goes wrong.</p><h3 id="learn-why-ai-observability-matters">Learn Why AI Observability Matters</h3><p>If you want to go deeper on this, join Jeff Fritz and Progress Product Manager Lyubomir Atanasov on <strong>August 26</strong> for <strong>&ldquo;AI Observability: When AI Goes Off the Rails.&rdquo;</strong> They&rsquo;ll walk through some genuinely memorable public AI failures, then run a live Progress AI Observability demo covering agent tracing, drift and hallucination detection, and performance and cost monitoring.</p><p><a target="_blank" href="https://www.telerik.com/webinars/progress-telerik/when-ai-goes-off-the-rails">Register for the webinar.</a></p><h3 id="help-shape-the-future-of-agentic-development">Help Shape the Future of Agentic Development</h3><p>This is also the thinking behind the Progress Agent Harness Early Access Program: structured, auditable AI workflows, with early adopters getting a direct line to the roadmap. As agents take on more, being able to manage them predictably matters just as much as what they can build.</p><p><a target="_blank" href="https://www.telerik.com/agent-harness-early-access">Join the Agent Harness EAP.</a></p><h2 id="what’s-new-and-release-history">What&rsquo;s New and Release History</h2><p>To see everything that&rsquo; is new in August 2026 release edition, visit the <a target="_blank" href="https://www.telerik.com/support/whats-new">What&rsquo;s New in Telerik and Kendo UI page</a>. For a deeper dive into each product, follow the links below.</p><table><style>table, th, td {
  border: 1px;
  border-color: #bdbdba;
  border-style: dotted;
  border-collapse: collapse;
  margin-right: auto;
  padding:0in 5.4pt 0in 5.4pt;
  text-align :left;
}
</style>
<thead><tr><th><strong>Product</strong></th><th><strong>What&rsquo;s New</strong></th><th><strong>Release History</strong></th></tr></thead><tbody><tr><td>Kendo UI for Angular</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/kendo-angular-ui/2026-q3">What&rsquo;s New in Kendo UI for Angular</a></td><td><a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/changelogs/kendo-angular-ui#v25.1.0-develop.4">Kendo UI for Angular Release History</a></td></tr><tr><td>KendoReact</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/kendo-react-ui/2026-q3">What&rsquo;s New in KendoReact</a></td><td><a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/changelogs/ui-for-react#v16.1.0-develop.2">KendoReact Release History</a></td></tr><tr><td>Kendo UI for Vue</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/kendo-vue-ui/2026-q3">What&rsquo;s New in Kendo UI for Vue</a></td><td><a target="_blank" href="https://www.telerik.com/kendo-vue-ui/components/changelogs/ui-for-vue#v9.2.0">Kendo UI for Vue Release History</a></td></tr><tr><td>Kendo UI for jQuery</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/kendo-jquery-ui/2026-q3">What&rsquo;s New in Kendo UI for jQuery</a></td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/kendo-ui/release-history/kendo-ui-for-jquery-2026-3-811-(2026-q3)">Kendo UI for jQuery Release History</a></td></tr><tr><td>Telerik UI for Blazor</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/blazor-ui/2026-q3">What&rsquo;s New in Telerik UI for Blazor</a></td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/blazor-ui/release-history/telerik-ui-for-blazor-15-0-0-(2026-q3)">Telerik UI for Blazor Release History</a></td></tr><tr><td>Telerik UI for ASP.NET Core</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/aspnet-core-ui/2026-q3">What&rsquo;s New in Telerik UI for ASP.NET Core</a></td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/aspnet-core-ui/release-history/telerik-ui-for-asp-net-core-2026-3-811-(2026-q3)">Telerik UI for ASP.NET Core Release History</a></td></tr><tr><td>Telerik UI for ASP.NET MVC</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/aspnet-mvc/2026-q3">What&rsquo;s New in Telerik UI for ASP.NET MVC</a></td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/aspnet-mvc/release-history/telerik-ui-for-asp-net-mvc-2026-3-811-(2026-q3)">Telerik UI for ASP.NET MVC Release History</a></td></tr><tr><td>Telerik UI for .NET MAUI</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/maui-ui/2026-q3">What&rsquo;s New in Telerik UI for .NET MAUI</a></td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/maui-ui/release-history/telerik-ui-for-net-maui-15-0-0-(2026-q3)">Telerik UI for .NET MAUI Release History</a></td></tr><tr><td>Telerik UI for WPF</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/wpf/2026-q3">What&rsquo;s New in Telerik UI for WPF</a></td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/wpf/release-history/telerik-ui-for-wpf-2026-3-812-(2026-q3)">Telerik UI for WPF Release History</a></td></tr><tr><td>Telerik UI for WinForms</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/winforms/2026-q3">What&rsquo;s New in Telerik UI for WinForms</a></td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/winforms/release-history/telerik-ui-for-winforms-2026-3-812-(2026-q3)">Telerik UI for WinForms Release History</a></td></tr><tr><td>Telerik Reporting</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/reporting/2026-q3">What&rsquo;s New in Telerik Reporting</a></td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/reporting/release-history/progress-telerik-reporting-2026-q3-(20-2-26-812)">Telerik Reporting Release History</a></td></tr><tr><td>Telerik Report Server</td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/report-server/2026-q3">What&rsquo;s New in Telerik Report Server</a></td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/report-server/release-history/progress-telerik-report-server-2026-q3-(12-2-26-812)">Telerik Report Server Release History</a></td></tr><tr><td>Telerik Document Processing</td><td><a target="_blank" href="https://www.telerik.com/document-processing-libraries">What&rsquo;s New in Telerik DPL</a></td><td><a target="_blank" href="https://www.telerik.com/support/whats-new/telerik-document-processing/release-history/progress-telerik-document-processing-2026-3-805">Telerik DPL Release History</a></td></tr></tbody></table><img src="https://feeds.telerik.com/link/10827/17423167.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:d487dffa-56e8-493a-b7b3-47bd62e986a2</id>
    <title type="text">Telerik UI for Blazor vs. Syncfusion Blazor: Which Is Better for Long-Term Application Development?</title>
    <summary type="text">Both Telerik UI for Blazor and Syncfusion Blazor can be used to build complex business applications. See how the two stack up across several evaluation areas.</summary>
    <published>2026-08-18T17:08:35Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Maya Mateva </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17422402/telerik-ui-blazor-vs-syncfusion-blazor-which-better-long-term-application-development"/>
    <content type="text"><![CDATA[<p><span class="featured">Both Telerik UI for Blazor and Syncfusion Blazor can be used to build complex business applications. See how the two stack up across several evaluation areas.</span></p><p>Blazor has become a serious platform for building modern web applications with .NET. As the framework has matured, commercial UI component libraries have become an important part of the Blazor development experience.</p><p>Instead of building every Grid, Chart, Scheduler, Form, Editor, and navigation component from scratch, teams can rely on mature component suites to accelerate development, improve UI quality and reduce long-term maintenance effort. Two of the most common options for Blazor development are <strong>Progress Telerik UI for Blazor</strong> and <strong>Syncfusion Blazor</strong>.</p><p>Both are mature. Both provide large component collections, commercial support, documentation, examples and tooling for .NET developers. Both can be used to build complex business applications.</p><p>The decision is rarely about whether one product is objectively &ldquo;better.&rdquo; A more useful question is: &ldquo;Which library better matches the way your team builds, maintains, upgrades and scales Blazor applications over time?&rdquo;</p><p>At a high level, <strong>Syncfusion Blazor</strong> is attractive for teams that want a very broad component catalog, access to many specialized controls and a Community License for qualifying users. <strong><a target="_blank" href="https://www.telerik.com/blazor-ui">Telerik UI for Blazor</a></strong> is a strong fit for teams that value a cohesive developer experience, predictable APIs, strong theming workflows, product polish, support reputation and long-term maintainability.</p><p>This comparison looks at both products through practical evaluation criteria: onboarding, component coverage, grid capabilities, theming, accessibility, performance, support, AI readiness and long-term project fit.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/telerik-blazor-dashboard.png?sfvrsn=4c114d9e_2" alt="Blazor Dashboard app" /></p><h2 id="quick-decision-summary">Quick Decision Summary</h2><p>Choose Telerik UI for Blazor if&hellip;</p><ul><li>You are building a long-lived application that will be maintained for several years.</li><li>Multiple developers or product teams will contribute to the same UI codebase.</li><li>You value predictable APIs and standardized implementation patterns.</li><li>You need strong theming, design-token management or designer-developer collaboration.</li><li>You care about upgrade experience, support quality and long-term maintainability.</li><li>You want a component suite that is easy for both developers and AI coding assistants to reason about.</li></ul><h2 id="key-differences-at-a-glance">Key Differences at a Glance</h2><table><style>table,
 th,
    td {
      border: 1px;
      border-color: #bdbdba;
      border-style: dotted;
      border-collapse: collapse;
      margin-right: auto;
      padding: 0in 5.4pt 0in 5.4pt;
      text-align: left;
    }
  </style>
 <thead><tr><th><strong>Evaluation Area</strong></th><th><strong>Telerik UI for Blazor</strong></th><th><strong>Syncfusion Blazor</strong></th></tr></thead><tbody><tr><td>Primary Strength</td><td>Cohesive developer experience, theming, maintainability</td><td>Broad component catalog and ecosystem coverage</td></tr><tr><td>Best Fit</td><td>Long-lived apps, shared UI platforms, design-system driven teams</td><td>Teams prioritizing maximum component availability</td></tr><tr><td>Component Count</td><td>120+ Blazor components</td><td>145+ Blazor components through the Community License page</td></tr><tr><td>Community License</td><td>No equivalent free Community License for qualifying small companies</td><td>Available for qualifying companies and individuals under stated revenue, developer and employee limits</td></tr><tr><td>Theming</td><td>ThemeBuilder with visual customization, CSS/SASS variables, Figma integration, project sharing, version control and user access rights</td><td>Flexible styling and theme customization options</td></tr><tr><td>Accessibility</td><td>Provides a WCAG 2.2 compliance table and Accessibility Conformance Report through VPAT</td><td>Documents WAI-ARIA, WCAG 2.2, Section 508, screen reader, keyboard, color contrast and RTL accessibility support</td></tr><tr><td>Grid</td><td>Strong focus on polished data scenarios, predictable usage patterns and accessibility documentation</td><td>Broad Grid feature set and detailed accessibility documentation</td></tr><tr><td>AI Readiness</td><td>Public documentation references AI/WebMCP-related work in Telerik Blazor docs</td><td>Syncfusion has published Blazor skills and license-related resources for AI-assisted component workflows</td></tr><tr><td>Overall Positioning</td><td>Strong fit when coherence, team productivity and UI governance matter most</td><td>Strong fit when breadth and licensing flexibility matter most</td></tr></tbody></table><br /><h2 id="what-most-teams-eventually-optimize-for">What Most Teams Eventually Optimize For</h2><p>When teams first compare component libraries, they usually start with feature lists.</p><p>That makes sense. Component count, available controls, Grid features, Scheduler options, chart types, templates, exporting and documentation examples are easy to compare.</p><p>But once a Blazor application moves into production, different questions become more important:</p><ul><li>How easy is the application to maintain?</li><li>Can new developers understand the UI code quickly?</li><li>Are component APIs predictable?</li><li>How painful are upgrades?</li><li>Can we apply the same branding across multiple applications?</li><li>Can designers and developers work from the same design system?</li><li>How reliable is support when something breaks?</li><li>Can AI coding assistants generate correct and maintainable UI code?</li></ul><p>This is where the distinction between <strong>breadth</strong> and <strong>cohesion</strong> matters.</p><p>Syncfusion has a strong case when a team wants access to a broad collection of components, related document libraries and specialized controls. Telerik has a very strong case when a team wants a more unified development model, mature theming workflows and a polished component experience across the core UI scenarios that most business applications depend on.</p><p>Neither priority is wrong. The better choice depends on what your team values most.</p><h2 id="time-to-first-value">Time to First Value</h2><p>Feature comparisons are useful, but the first developer experience is usually more practical:</p><ul><li>How do I install the package?</li><li>How do I configure licensing?</li><li>How do I set up NuGet access?</li><li>How do I create the first Blazor project?</li><li>How quickly can I render a real component?</li><li>How many manual steps are required before I can start evaluating the library?</li></ul><p>This is where <strong>time to first value</strong> becomes important.</p><h3 id="time-to-first-value-telerik-ui-for-blazor">Time to First Value: Telerik UI for Blazor</h3><p>Telerik puts a strong emphasis on guided onboarding. The product experience includes getting-started documentation, templates, licensing guidance, CLI-based workflows and integration with Telerik tooling.</p><p>Telerik focuses heavily on onboarding and guided setup, and this includes:</p><ul><li>Structured getting-started documentation</li><li>Starter project guidance</li><li>Project templates and configuration assistance</li><li>Consistent setup patterns across components</li></ul><p>The Telerik CLI can help reduce setup friction by assisting with project creation, package configuration, licensing-related setup, templates and related tooling.</p><p>This matters because early evaluation is not only about installing a NuGet package. It is about reducing the number of small setup decisions that slow teams down before they can assess the actual components.</p><p>The setup becomes as simple as:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/telerik-cli.png?sfvrsn=d8047b3e_2" width="400" alt="dotnet tool install -g Telerik.CLI  Telerik setup blazor  dotnet new Telerik-blazor -o MyApplication" /></p><p>For larger teams, this kind of guided setup can be useful because new developers do not need to rediscover the same configuration steps independently.</p><h3 id="time-to-first-value-syncfusion-blazor">Time to First Value: Syncfusion Blazor</h3><p>Syncfusion also has a strong onboarding story. Its ecosystem includes product documentation, sample applications and a large collection of examples. The Syncfusion Blazor samples repository describes demos for Blazor Server and Blazor WebAssembly apps, including project files targeting modern .NET versions.</p><p>That breadth of examples can be valuable when a developer is looking for a specific scenario or specialized component behavior.</p><h3 id="time-to-first-value-takeaway">Time-to-First-Value Takeaway</h3><p>For a small proof of concept, both products can get developers started quickly.</p><p>The advantage of Telerik UI is more about guided setup and a cohesive path from installation to production-style usage. Syncfusion&rsquo;s advantage is the large ecosystem of samples.</p><h2 id="component-coverage">Component Coverage</h2><p>Component count is one of the most visible comparison points.</p><p>Syncfusion Blazor is known for broad component coverage. Its Community License page describes access to <strong>145+ Blazor components</strong> for eligible users and organizations. Syncfusion&rsquo;s broader product line also includes UI components, document SDKs and standalone UI SDKs under the Community License description.</p><p>That breadth is a legitimate advantage. Teams that need niche controls, document processing capabilities, PDF-related libraries, Office document support or specialized UI scenarios may find Syncfusion difficult to overlook.</p><p>Telerik UI for Blazor takes a somewhat different approach. It provides a large suite of core Blazor components, but its strongest value proposition is not simply the number of controls. Telerik UI is strongest when the application depends heavily on polished core components such as Grid, Charts, Scheduler, Forms, Inputs, Editors, Layout, Navigation, Upload and theming.</p><p>For many business applications, both libraries cover the essential requirements. The practical difference is often not &ldquo;Does a component exist?&rdquo; but:</p><ul><li>How predictable is it to configure?</li><li>Does it follow familiar patterns from other components in the same suite?</li><li>Does documentation match real development scenarios?</li><li>Is the component easy to theme and maintain?</li><li>Does it fit into a shared UI platform?</li></ul><h3 id="component-coverage-takeaway">Component Coverage Takeaway</h3><p>Syncfusion has the advantage in overall breadth and specialized coverage.</p><p>Telerik has the advantage when teams want a cohesive suite focused on core business application scenarios, polished UI behavior and maintainable implementation patterns.</p><h2 id="the-grid-the-component-that-often-decides-the-evaluation">The Grid: The Component That Often Decides the Evaluation</h2><p>If one component influences UI library decisions more than any other, it is usually the grid.</p><p>Business applications spend a large amount of time displaying, filtering, grouping, sorting, editing, exporting and analyzing data. For many teams, the grid is not just one component. It is the center of the application.</p><p>Both Telerik UI for Blazor and Syncfusion Blazor provide capable Grid components with support for common business scenarios such as:</p><ul><li>Sorting</li><li>Filtering</li><li>Grouping</li><li>Paging</li><li>Virtualization</li><li>Inline editing</li><li>Batch editing</li><li>Templates</li><li>Aggregates</li><li>Exporting</li><li>Hierarchical data</li><li>Large dataset scenarios</li></ul><p>Both vendors can support serious data-heavy applications. In practice, grid performance depends not only on the component itself but also on data access strategy, rendering mode, server communication, virtualization configuration and application architecture.</p><h3 id="telerik-ui-for-blazor-grid">Telerik UI for Blazor Grid</h3><p>The Telerik UI for <a target="_blank" href="https://demos.telerik.com/blazor-ui/grid/overview">Blazor Grid</a> is strongest when teams care about a polished and predictable implementation model.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/telerik-blazor-grid.png?sfvrsn=3e25ad7e_2" alt="Telerik Blazor grid for shipment tracker" /></p><p>The main advantages are not only feature availability, but how the Grid fits into the broader development experience:</p><ul><li>Predictable configuration patterns</li><li>Strong template support</li><li>Familiar event and data-binding approaches</li><li>Alignment with the rest of the Telerik component suite</li><li>Accessibility-specific documentation</li><li>A mature experience for common business data scenarios</li></ul><p>Telerik Grid accessibility is <strong>WCAG 2.2 AA and Section 508 compliant</strong>, follows WAI-ARIA best practices for keyboard navigation and is tested with popular screen readers. That is a concrete point worth considering for teams building applications with formal accessibility requirements.</p><h3 id="syncfusion-blazor-grid">Syncfusion Blazor Grid</h3><p>The Syncfusion Grid is also highly capable and has a broad feature surface. It is particularly attractive when teams need extensive configuration options or specialized behaviors.</p><p>Syncfusion&rsquo;s Grid supports accessibility standards including ADA, Section 508, WCAG 2.2 and ARIA roles, with details on screen reader support, RTL support, color contrast, mobile device support, keyboard navigation and Axe-core validation.</p><h3 id="grid-takeaway">Grid Takeaway</h3><p>Both Grids are mature and capable.</p><p>Syncfusion&rsquo;s Grid stands out for broad functionality and configurability. The Telerik Grid stands out for a polished developer experience, accessibility documentation and a usage model that tends to fit well into long-lived applications with multiple contributors.</p><p>If your evaluation is based on a specific Grid feature, test that feature in both products. If your evaluation is based on long-term maintainability, onboarding, accessibility and shared implementation patterns, Telerik deserves stronger consideration.</p><h2 id="scheduler-charts-and-dashboards">Scheduler, Charts and Dashboards</h2><p>After the Grid, Scheduler and Charts are often among the most important components in business applications.</p><h3 id="scheduler">Scheduler</h3><p>Both Telerik and Syncfusion support common scheduling scenarios such as:</p><ul><li>Day, week and month views</li><li>Recurring events</li><li>Resource grouping</li><li>Drag-and-drop editing</li><li>Custom templates</li><li>Business calendar scenarios</li></ul><p>Syncfusion is attractive when the team wants broad scenario coverage and many examples.</p><p>Telerik is attractive when the <a target="_blank" href="https://demos.telerik.com/blazor-ui/scheduler/overview">Blazor Scheduler</a> needs to feel like part of the same UI platform as the Grid, Forms, Inputs, Dialogs, Charts and navigation components.</p><h3 id="charts-and-dashboards">Charts and Dashboards</h3><p>Both libraries provide charting capabilities suitable for dashboards, reporting UIs, KPI tracking, analytics screens and business intelligence-style interfaces.</p><p>The distinction is less about whether common chart types exist and more about the surrounding workflow:</p><ul><li>How easily can charts be themed?</li><li>Can they follow the same visual system as the rest of the app?</li><li>Can multiple teams reuse the same design language?</li><li>Can dashboard screens stay visually aligned over time?</li></ul><p>This is where the Telerik theming story becomes especially relevant.</p><h2 id="theming-and-design-system-governance">Theming and Design-System Governance</h2><p>Theming is often underestimated during evaluations.</p><p>At the start of a project, teams usually focus on functionality. Later, especially in long-lived applications, theming becomes a product and architecture concern.</p><p>Questions start to appear:</p><ul><li>Can we align components with our brand?</li><li>Can designers and developers collaborate without translating everything manually?</li><li>Can we reuse themes across multiple applications?</li><li>Can we manage design tokens centrally?</li><li>Can we support light and dark modes?</li><li>Can we avoid one-off CSS overrides that become hard to maintain?</li><li>Can we update the visual language without rewriting large parts of the UI?</li></ul><p>This is one of Progress Telerik UI&rsquo;s clearest differentiators.</p><h3 id="progress-telerik-themebuilder">Progress Telerik ThemeBuilder</h3><p><a href="https://www.telerik.com/themebuilder" target="_blank">Progress Telerik ThemeBuilder</a> is a SaaS tool that enables teams to create custom themes and preview how they affect component appearance. It can generate a CSS file that can be used in a Blazor app instead of a built-in theme.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/progress-themebuilder-blazor.png?sfvrsn=d5ab735f_2" alt="Progress ThemeBuilder showing button styles" /></p><p>ThemeBuilder also supports capabilities that matter in design-system driven organizations, including:</p><ul><li>Atomic customization</li><li>CSS/SASS variables</li><li>Custom HTML support</li><li>Figma integration</li><li>Theme modes and built-in themes</li><li>Custom fonts and iconography</li><li>Project sharing</li><li>Version control</li><li>Collaboration and user access rights</li></ul><p>This is more than visual customization. It helps teams create a shared styling workflow between design and engineering.</p><p>For organizations with multiple product teams, this can reduce duplicated CSS work, prevent visual drift between applications and make brand updates easier to manage.</p><h3 id="syncfusion-customization">Syncfusion Customization</h3><p>Syncfusion also provides styling and customization options. For many teams, those capabilities will be enough.</p><p>However, if centralized design-system governance is a major requirement, Progress Telerik ThemeBuilder gives it a particularly strong position.</p><h3 id="theming-takeaway">Theming Takeaway</h3><p>Both libraries can be customized.</p><p>Telerik has a clearer advantage when teams need design tokens, Figma-related workflows, centralized branding, visual governance and reusable themes across multiple applications.</p><h2 id="blazor-app-accessibility">Blazor App Accessibility</h2><p>Accessibility should be part of any serious UI library evaluation.</p><p>Teams building applications for public sector, healthcare, finance, education, government or large internal platforms often need to consider:</p><ul><li>WCAG</li><li>Section 508</li><li>Keyboard navigation</li><li>Screen reader support</li><li>WAI-ARIA roles and attributes</li><li>Color contrast</li><li>Accessible interaction alternatives</li><li>Accessibility conformance documentation</li></ul><h3 id="telerik-accessibility">Telerik Accessibility</h3><p><a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/accessibility/compliance#compliance-table">Telerik UI for Blazor provides an accessibility compliance table</a> that lists WCAG 2.2 compliance levels for individual components. Progress Telerik also provides an Accessibility Conformance Report through VPAT.</p><p>For the Grid specifically, Telerik documents WCAG 2.2 AA and Section 508 compliance, WAI-ARIA best practices, keyboard navigation, and screen reader testing.</p><h3 id="syncfusion-accessibility">Syncfusion Accessibility</h3><p>Syncfusion documents accessibility support for Blazor components, including WAI-ARIA, WCAG 2.2, Section 508, keyboard navigation, screen reader support, RTL support and color contrast considerations.</p><p>Syncfusion&rsquo;s DataGrid accessibility documentation also provides a detailed view of supported standards and limitations for complex interactions.</p><h3 id="accessibility-takeaway">Accessibility Takeaway</h3><p>Both vendors take accessibility seriously and provide documentation around standards and component behavior.</p><p>The Telerik component-level compliance table and documented Grid compliance are helpful for teams that need formal accessibility evidence. Syncfusion also provides broad accessibility documentation, including detailed DataGrid accessibility coverage.</p><p>For accessibility-sensitive applications, the best approach is to test the exact components and configurations your application will use.</p><h2 id="support-documentation-and-learning-resources">Support, Documentation and Learning Resources</h2><p>Support quality is hard to capture in a comparison table, but it often becomes critical after adoption.</p><p>During a trial, teams should evaluate:</p><ul><li>How quickly developers find answers in documentation</li><li>Whether examples match real scenarios</li><li>How clear upgrade notes are</li><li>How support handles complex issues</li><li>Whether feedback and bug reports are visible</li><li>Whether the vendor provides practical guidance beyond simple demos</li></ul><h3 id="telerik-ui-for-blazor-support">Telerik UI for Blazor Support</h3><p>Progress Telerik has a long-standing reputation in the ecosystem for polished components, detailed documentation, active forums, feedback channels and strong support. That reputation is one of the reasons Telerik UI is often favored by teams building long-lived applications.</p><p>This should still be validated during evaluation. The best support test is not reading a marketing page. It is submitting realistic trial questions and measuring how useful the answers are.</p><h3 id="syncfusion-blazor-support">Syncfusion Blazor Support</h3><p>Syncfusion also provides commercial support, documentation, samples and a large knowledge base. Its broad ecosystem can be helpful when developers are searching for examples across many controls and scenarios.</p><p>Syncfusion&rsquo;s large sample repository is especially useful when teams want to inspect working examples and compare usage across components.</p><h3 id="support-takeaway">Support Takeaway</h3><p>Both vendors provide serious support and learning resources.</p><p>Telerik UI is often especially attractive for teams that prioritize product polish, support confidence and long-term UI quality. Syncfusion is strong for teams that value broad documentation coverage, many samples and a large ecosystem.</p><h2 id="product-quality-stability-and-upgrade-experience">Product Quality, Stability and Upgrade Experience</h2><p>Product quality is more than whether a component works in a demo.</p><p>For long-lived applications, quality usually means:</p><ul><li>Stable APIs</li><li>Clear documentation</li><li>Predictable behavior</li><li>Accessibility guidance</li><li>Reliable release notes</li><li>Manageable upgrades</li><li>Good support for modern .NET versions</li><li>Transparent handling of bug fixes and breaking changes</li><li>Similar concepts across related components</li></ul><p>This is where the Telerik UI value proposition becomes stronger. Its advantage is not only that the components are polished, but that the suite tends to feel like a coherent product rather than a collection of unrelated controls.</p><p>That can reduce cognitive load for teams. Developers who learn common Telerik concepts in one component often find similar ideas elsewhere in the suite.</p><p>Syncfusion&rsquo;s broader catalog is valuable, but naturally creates a larger surface area to learn, test and govern. That tradeoff may be completely acceptable if the team needs the additional breadth.</p><h3 id="upgrade-experience-takeaway">Upgrade Experience Takeaway</h3><p>For short-lived apps, upgrade experience may not matter much.</p><p>For applications expected to live for several years, upgrade predictability and maintainable implementation patterns become important. Telerik UI is particularly compelling for teams that prioritize those concerns.</p><h2 id="ai-readiness-and-agentic-development">AI Readiness and Agentic Development</h2><p>A few years ago, component library evaluations focused primarily on features, performance and support. Today, many teams are adding another criterion to their evaluation:</p><p><strong>How well does the platform integrate with AI-assisted development workflows?</strong></p><p>The discussion is no longer limited to AI-powered components. Developers increasingly expect AI tools that can help generate interfaces, configure components, validate code, enforce design-system rules and accelerate application development.</p><h3 id="progress-telerik-ai-approach">Progress Telerik AI Approach</h3><p>Progress Telerik has invested in AI-assisted development across several areas of the product ecosystem, combining UI components, AI-powered tooling and agentic workflows into a more complete development experience.</p><p>The foundation of this effort is the <a href="https://www.telerik.com/mcp-servers#ai-coding-assistant" target="_blank">Telerik AI Coding Assistant</a>, which integrates directly into developer workflows through IDE-based experiences, GitHub Copilot extensions, and <a href="https://www.telerik.com/mcp-servers" target="_blank">MCP-based tooling</a>. The assistant can help generate Telerik UI for Blazor code, configure components, scaffold common scenarios, generate sample data and provide implementation guidance using Telerik-specific knowledge rather than generic web examples.</p><p>Beyond code generation, Progress Telerik has introduced an <a href="https://www.telerik.com/mcp-servers#agentic-ui-generator" target="_blank">Agentic UI Generator</a> that uses a collection of specialized assistants working together through an MCP-based architecture. Rather than generating isolated snippets, it can help create complete pages, dashboards, layouts and component configurations while following Telerik component patterns and design-system guidance.</p><p>The platform includes specialized assistants for:</p><ul><li>Page and UI generation</li><li>Component selection and configuration</li><li>Layout creation</li><li>Styling and theming</li><li>Icons and visual assets</li><li>Accessibility guidance</li><li>Validation and quality checks</li><li>Project onboarding and setup workflows</li></ul><p>These capabilities are orchestrated through the Agentic UI Generator, allowing developers to work at a higher level of abstraction when creating Blazor applications.</p><h4 id="telerik-ai-plugins-mcp-and-development-tools">Telerik AI Plugins, MCP and Development Tools</h4><p>One notable aspect of the Telerik AI strategy is the focus on developer tools rather than only AI-enabled components.</p><p>The Telerik MCP-based tooling includes plugin capabilities for:</p><p>![Telerik MCP-based tooling includes UI Generator/Orchestrator, Getting Started Assistant, Component Assistant, Icon Assistant, Layout Assistant, Styling Assistant, Accessibility Assistant, Validator Assistant](/sfimages/default-source/blogs/2026/2026-08/telerik-mcp- plugins)</p><p>This allows AI tools to work with Telerik-specific knowledge instead of relying solely on generic LLM training data, which can help reduce incorrect component usage and configuration errors.</p><p>In addition, Telerik provides AI-enhanced capabilities within its component ecosystem, including AI-powered chat experiences, semantic search scenarios, AI-assisted data operations, AI integrations within Grid and Editor components, WebMCP support, and integration scenarios involving technologies such as <a target="_blank" href="https://demos.telerik.com/blazor-ui/a2ui/ai-a2ui">A2UI</a>, AG-UI and Microsoft Agent Framework.</p><h3 id="syncfusion-ai-position">Syncfusion AI Position</h3><p>Syncfusion is also actively investing in AI-based developer experiences and provides AI-related features, assistants and tooling across its ecosystem. Like Progress Telerik, Syncfusion is exploring AI-assisted development workflows and has published AI-related resources and component skills for developers working with its platform.</p><p>One of Syncfusion&rsquo;s broader advantages remains the size of its overall ecosystem, which includes a large collection of UI components, document processing libraries and developer tools that can participate in AI-assisted workflows.</p><h3 id="ai-readiness-takeaway">AI Readiness Takeaway</h3><p>Which platform has the stronger AI story? Today, both Progress Telerik and Syncfusion are investing in AI.</p><p>The difference is less about whether AI capabilities exist and more about where the investment is focused.</p><p>Syncfusion&rsquo;s strength comes from the breadth of its ecosystem and the large number of controls, libraries, and scenarios available to developers.</p><p>Telerik UI&rsquo;s strength comes from providing an increasingly connected AI development experience that spans:</p><ul><li>AI Coding Assistant</li><li>Agentic UI Generator</li><li>MCP-based development tooling</li><li>Specialized AI assistants</li><li>Validation workflows</li><li>Accessibility guidance</li><li>Design-system alignment</li><li>AI-enhanced components and features</li></ul><p>For teams looking beyond AI-powered controls and toward AI-assisted UI development, Telerik currently offers one of the more comprehensive AI stories available in the Blazor ecosystem. The combination of UI generation, component-aware assistants, validation tooling, theming guidance and MCP integration makes the AI capabilities feel like part of the overall developer workflow rather than a collection of isolated features.</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">Progress Telerik Agentic UI Generator vs. Syncfusion Agentic UI Builder</h4></div><div class="col-8"><p class="u-fs16 u-mb0"><a target="_blank" href="https://www.telerik.com/blogs/progress-telerik-agentic-ui-generator-vs-syncfusion-agentic-ui-builder">See how the AI-based UI creation tools</a> from devtools powerhouses Progress and Syncfusion stack up when they go head to head.</p></div></div><hr class="u-mb3" /></aside><h2 id="where-telerik-has-a-clear-advantage">Where Telerik Has a Clear Advantage</h2><p>Telerik is strongest when the team cares less about maximizing the catalog and more about building a maintainable UI platform.</p><p>Telerik is particularly compelling when:</p><ul><li>The application will live for several years.</li><li>Multiple developers or product teams will work in the same codebase.</li><li>The Grid, Forms, Charts, Scheduler and layout components need to feel cohesive.</li><li>Design-system governance matters.</li><li>The team wants strong theming workflows through ThemeBuilder.</li><li>Accessibility evidence is important.</li><li>Support confidence is a major factor.</li><li>AI-assisted development is becoming part of the engineering workflow.</li></ul><p>The clearest Telerik differentiator is ThemeBuilder. Its support for visual customization, variables, Figma integration, theme modes, custom fonts, project sharing, version control and collaboration makes it especially useful for teams that need a structured design-to-development workflow.</p><h2 id="pros-and-cons">Pros and Cons</h2><p><strong>Telerik UI for Blazor</strong></p><table><style>table,
 th,
    td {
      border: 1px;
      border-color: #bdbdba;
      border-style: dotted;
      border-collapse: collapse;
      margin-right: auto;
      padding: 0in 5.4pt 0in 5.4pt;
      text-align: left;
    }
  </style>
 <thead><tr><th><strong>Pros</strong></th><th><strong>Cons</strong></th></tr></thead><tbody><tr><td>Cohesive component suite</td><td>Commercial licensing required</td></tr><tr><td>Strong Grid experience</td><td>Smaller component catalog than Syncfusion (Telerik &ndash; 120+, Syncfusion &ndash; 145+)</td></tr><tr><td>Predictable APIs and implementation patterns</td><td>Fewer niche controls in some specialized areas</td></tr><tr><td>Strong ThemeBuilder and design-system workflow</td><td>Some advanced scenarios may require custom implementation</td></tr><tr><td>Accessibility documentation and compliance resources</td><td>Not the best fit if raw component count is the top priority</td></tr><tr><td>Strong fit for long-lived applications</td><td>&nbsp;</td></tr><tr><td>Good alignment with AI-assisted development trends</td><td>&nbsp;</td></tr><tr><td>Strong support reputation in the .NET ecosystem</td><td>&nbsp;</td></tr></tbody></table><br /><p><strong>Syncfusion Blazor</strong></p><table><style>table,
 th,
    td {
      border: 1px;
      border-color: #bdbdba;
      border-style: dotted;
      border-collapse: collapse;
      margin-right: auto;
      padding: 0in 5.4pt 0in 5.4pt;
      text-align: left;
    }
  </style>
 <thead><tr><th><strong>Pros</strong></th><th><strong>Cons</strong></th></tr></thead><tbody><tr><td>Broad component catalog</td><td>Larger API surface can increase complexity</td></tr><tr><td>Community License for qualifying users</td><td>More governance may be needed to standardize usage</td></tr><tr><td>Document and file-format related ecosystem</td><td>Component experience may vary more across the suite</td></tr><tr><td>Many samples and examples</td><td>Teams should carefully test upgrades and scenario-specific behavior</td></tr><tr><td>Strong specialized-control coverage</td><td>&nbsp;</td></tr></tbody></table><br /><h2 id="decision-framework">Decision Framework</h2><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/telerik-vs-syncfusion-blazor.png?sfvrsn=ee697bc7_2" alt="Telerik vs Syncfusion for Blazor decision tree" /></p><p><strong>Telerik UI for Blazor is likely the better fit if&hellip;</strong></p><ol><li>The application will be maintained for several years.</li><li>Multiple developers or teams will contribute to the UI.</li><li>You need a shared design system.</li><li>The Grid is central to your application.</li><li>Accessibility documentation is important.</li><li>You want a polished and cohesive development experience.</li><li>Support confidence is part of the buying decision.</li><li>You expect AI-assisted development to become more important.</li><li>You value maintainability more than raw component count.</li></ol><p><strong>Syncfusion Blazor is likely the better fit if&hellip;</strong></p><ol><li>You need the large component catalog.</li><li>You need niche components.</li><li>The Community License applies to you.</li><li>Your team is comfortable managing a larger API surface.</li><li>Your evaluation is primarily driven by breadth and feature availability.</li></ol><h2 id="final-verdict">Final Verdict</h2><p>If your primary goal is maximizing the number of available controls, accessing specialized components, benefiting from a Community License, <strong>Syncfusion Blazor is difficult to overlook</strong>.</p><p>If your priority is building a long-lived Blazor application with a cohesive developer experience, strong theming, accessibility documentation, support confidence, predictable implementation patterns and maintainable UI architecture, <strong>Telerik UI for Blazor offers a very compelling value proposition</strong>.</p><p>The choice comes down to what your team wants to optimize for. Feature breadth matters during evaluation. Maintainability matters in production.</p><p>For teams building applications that need to scale across developers, products, themes, accessibility requirements, and future AI-assisted workflows, <strong>Telerik UI for Blazor stands out as the stronger long-term choice</strong>&mdash;not because Syncfusion is weak, but because Telerik UI for Blazor&rsquo;s strengths align especially well with the needs of structured, design-system driven, maintainable Blazor development.</p><p><a target="_blank" href="https://www.telerik.com/try/ui-for-blazor" class="Btn">Try Telerik UI for Blazor</a></p><img src="https://feeds.telerik.com/link/10827/17422402.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:147ede5a-1142-489e-9a4b-a4a105b2b5ed</id>
    <title type="text">Useful JavaScript Additions in ECMAScript 2026</title>
    <summary type="text">The useful part of the ECMAScript 2026 standard for JavaScript developers is not a dramatic change to the language. It is that several common use cases now have direct names.</summary>
    <published>2026-08-17T16:34:58Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Peter Mbanugo </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17421667/useful-javascript-additions-ecmascript-2026"/>
    <content type="text"><![CDATA[<p><span class="featured">The useful part of the ECMAScript 2026 standard for JavaScript developers is not a dramatic change to the language. It is that several common use cases now have direct names.</span></p><p>JavaScript gets a new language standard every year. Some editions introduce syntax that changes how programs are written or how they perform. The <a target="_blank" href="https://ecma-international.org/publications-and-standards/standards/ecma-262/">ECMAScript 2026</a> (ES17) version brings a handful of useful features which we will look at. These features add focused APIs for tasks that JavaScript developers already handle with small helpers, repeated checks or easy-to-miss workarounds.</p><p>In this article, I will focus on additions that can remove code, make intent clearer or prevent subtle errors.</p><blockquote><p><strong>Runtime support:</strong> These APIs did not arrive in every runtime at the same time. Check the <strong>Browser compatibility</strong> table on each linked MDN page for the browsers and runtimes you support. Use a tested fallback when an API is unavailable.</p></blockquote><p><img sf-image-responsive="true" src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/useful-javascript-additions-in-ecmascript-2026.png?sfvrsn=c4d86b5a_2" height="941" style="max-width:100%;height:auto;" title="Useful JavaScript Additions in ECMAScript 2026" width="1672" alt="Useful JavaScript Additions in ECMAScript 2026" sf-size="1499143" /><br /><span style="font-size:11px;">Image generated with AI</span></p><h2 id="build-a-map-as-values-arrive">Build a Map as Values Arrive</h2><p>In <a target="_blank" href="https://pmbanugo.me/blog/array-grouping-in-javascript">my article about array grouping</a>, I showed a simple way to group related things using <code>Object.groupBy()</code> and <code>Map.groupBy()</code>. Both methods begin with a collection that already exists, but what if you want to do the same for streaming data?</p><p>For example, this code groups employees from an asynchronous stream:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> employeesByDepartment <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Map</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">for</span> <span class="token keyword">await</span> <span class="token punctuation">(</span><span class="token keyword">const</span> employee <span class="token keyword">of</span> employeeStream<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>employeesByDepartment<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>employee<span class="token punctuation">.</span>department<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    employeesByDepartment<span class="token punctuation">.</span><span class="token keyword">set</span><span class="token punctuation">(</span>employee<span class="token punctuation">.</span>department<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>

  employeesByDepartment
    <span class="token punctuation">.</span><span class="token keyword">get</span><span class="token punctuation">(</span>employee<span class="token punctuation">.</span>department<span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>employee<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>There is nothing complicated here, but the <code>has()</code>, <code>set()</code> and <code>get()</code> sequence is boilerplate code around the operation we actually care about&mdash;adding a new employee to a department.</p><p>ECMAScript 2026 adds <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/getOrInsert"><code>Map.prototype.getOrInsert()</code></a> and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/getOrInsertComputed"><code>Map.prototype.getOrInsertComputed()</code></a>. These methods return the value corresponding to the specified key. If not present, it inserts a new entry with the key and a given default value, and returns the inserted value.</p><p>You should use <code>getOrInsertComputed()</code> whenever the default should be created lazily, particularly when creating it is expensive, has side effects or allocates a mutable object such as an array. With the computed version, the code from before becomes:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> employeesByDepartment <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Map</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">for</span> <span class="token keyword">await</span> <span class="token punctuation">(</span><span class="token keyword">const</span> employee <span class="token keyword">of</span> employeeStream<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  employeesByDepartment
    <span class="token punctuation">.</span><span class="token function">getOrInsertComputed</span><span class="token punctuation">(</span>employee<span class="token punctuation">.</span>department<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</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">push</span><span class="token punctuation">(</span>employee<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>The result is simple and compact.</p><h2 id="collect-an-asynchronous-iterable-into-an-array">Collect an Asynchronous Iterable into an Array</h2><p>An asynchronous generator is a useful way to hide pagination. Its caller can consume one sequence without knowing where one response page ends and the next begins. For example:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">async</span> <span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">fetchAllIssues</span><span class="token punctuation">(</span>url<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">while</span> <span class="token punctuation">(</span>url<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span>url<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>response<span class="token punctuation">.</span>ok<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`Request failed: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>response<span class="token punctuation">.</span>status<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 punctuation">}</span>

    <span class="token keyword">const</span> page <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">yield</span><span class="token operator">*</span> page<span class="token punctuation">.</span>items<span class="token punctuation">;</span>
    url <span class="token operator">=</span> page<span class="token punctuation">.</span>next<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>To collect every result into an array, you would normally write a loop:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> issues <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

<span class="token keyword">for</span> <span class="token keyword">await</span> <span class="token punctuation">(</span><span class="token keyword">const</span> issue <span class="token keyword">of</span> <span class="token function">fetchAllIssues</span><span class="token punctuation">(</span><span class="token string">"/api/issues"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  issues<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>issue<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>The <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fromAsync"><code>Array.fromAsync()</code> method</a> can be used to perform that collection directly:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> issues <span class="token operator">=</span> <span class="token keyword">await</span> Array<span class="token punctuation">.</span><span class="token function">fromAsync</span><span class="token punctuation">(</span>
  <span class="token function">fetchAllIssues</span><span class="token punctuation">(</span><span class="token string">"/api/issues"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Isn&rsquo;t that shorter and simpler?</p><p>Despite its name, the method also accepts synchronous iterables and array-like objects. It awaits values from those sources one at a time.</p><p>Array.fromAsync() also accepts a mapping function, and the runtime waits for the mapped result before reading the next value. For example, the same code can be adjusted to return only the issue title using the map function:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> issueTitles <span class="token operator">=</span> <span class="token keyword">await</span> Array<span class="token punctuation">.</span><span class="token function">fromAsync</span><span class="token punctuation">(</span>
  <span class="token function">fetchAllIssues</span><span class="token punctuation">(</span><span class="token string">"/api/issues"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">(</span>issue<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> issue<span class="token punctuation">.</span>title<span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Given that example, it might be misleading to think that <code>Array.fromAsync()</code> runs independent operations concurrently. For example:</p><pre class=" language-js"><code class="prism  language-js"><span class="token comment">// The requests start one after another.</span>
<span class="token keyword">const</span> sequential <span class="token operator">=</span> <span class="token keyword">await</span> Array<span class="token punctuation">.</span><span class="token function">fromAsync</span><span class="token punctuation">(</span>urls<span class="token punctuation">,</span> fetchJson<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// All requests are initiated before any of them is awaited.</span>
<span class="token keyword">const</span> concurrent <span class="token operator">=</span> <span class="token keyword">await</span> Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span>urls<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>fetchJson<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>You should use <code>Promise.all()</code> when independent operations can run at the same time. And reach for <code>Array.fromAsync()</code> when the source or mapping step may be asynchronous, and you want lazy, ordered consumption. Also remember what the method returns&mdash;an array containing every result. Keep the <code>for await...of</code> loop when you want to process a large stream incrementally and avoid collecting a stream that may never end.</p><h2 id="combine-iterables-into-one-lazy-sequence">Combine Iterables into One Lazy Sequence</h2><p>Sometimes several iterable sources should behave like one continuous sequence. Suppose an application checks its built-in routes first, then registered routes by plugins and finally a catch-all route. You could spread everything into a new array, but that would eagerly consume each iterable and allocate another collection.</p><p>A generator can keep the sequence lazy:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">function</span><span class="token operator">*</span> <span class="token function">allRoutes</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">yield</span><span class="token operator">*</span> builtInRoutes<span class="token punctuation">;</span>
  <span class="token keyword">yield</span><span class="token operator">*</span> pluginRoutes<span class="token punctuation">;</span>
  <span class="token keyword">yield</span> fallbackRoute<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator/concat"><code>Iterator.concat()</code></a>, on the other hand, expresses the same operation without the custom generator:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> allRoutes <span class="token operator">=</span> Iterator<span class="token punctuation">.</span><span class="token function">concat</span><span class="token punctuation">(</span>
  builtInRoutes<span class="token punctuation">,</span>
  pluginRoutes<span class="token punctuation">,</span>
  <span class="token punctuation">[</span>fallbackRoute<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The result yields the built-in routes first, followed by the plugin routes and then the fallback. Values are pulled only as the consumer advances the iterator; <code>Iterator.concat()</code> does not collect them into a new array first. Each argument must be an iterable object, which is why the example wraps <code>fallbackRoute</code> in an array.</p><p>The <code>Iterator.concat()</code>method works with synchronous iterables only. It does not combine asynchronous iterables.</p><h2 id="preserve-large-integers-in-json">Preserve Large Integers in JSON</h2><p>Chat platforms often hand out snowflake IDs as part of a message or other kinds of data. These are commonly 64-bit integers and can exceed <code>Number.MAX_SAFE_INTEGER</code>, so parsing them as JSON numbers can silently lose precision. Consider this response:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> payload <span class="token operator">=</span> <span class="token template-string"><span class="token string">`{
  "messageId": 1183028002140618753,
  "channel": "general"
}`</span></span><span class="token punctuation">;</span>

<span class="token keyword">const</span> event <span class="token operator">=</span> JSON<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>payload<span class="token punctuation">)</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>messageId<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 1183028002140618800</span>
</code></pre><p>The logged value isn&rsquo;t the value in the JSON text.</p><p>ECMAScript 2026 gives the <code>JSON.parse()</code> reviver function a third argument, named <code>context</code>. When the value is an unmodified primitive from the parser, <code>context.source</code> contains the original JSON text. We can use that to parse and convert the text to the proper type, in this case <code>BigInt</code>.</p><p>Here&rsquo;s the sample from earlier, rewritten using the reviver function:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> event <span class="token operator">=</span> JSON<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>
  payload<span class="token punctuation">,</span>
  <span class="token punctuation">(</span>key<span class="token punctuation">,</span> value<span class="token punctuation">,</span> context<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>key <span class="token operator">===</span> <span class="token string">"messageId"</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">return</span> <span class="token function">BigInt</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span>source<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">return</span> value<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>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>messageId<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 1183028002140618753n</span>
</code></pre><p>By the time the reviver receives <code>value</code>, the <code>Number</code> has already lost precision. However, <code>context.source</code> lets the code ignore that damaged value and build a <code>BigInt</code> from the original digits instead.</p><p>Serialization has the opposite problem: <code>JSON.stringify()</code> throws when it reaches a <code>BigInt</code>, unless you handle the value. The new <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/rawJSON"><code>JSON.rawJSON()</code></a> method lets a replacer function for <code>JSON.stringify</code> provide valid JSON text for a primitive value. The <code>JSON.rawJSON()</code> method creates a &ldquo;raw JSON&rdquo; object containing JSON text.</p><p>Here&rsquo;s an example using <code>JSON.stringify()</code> and <code>JSON.rawJSON()</code> together:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> json <span class="token operator">=</span> JSON<span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>
  event<span class="token punctuation">,</span>
  <span class="token punctuation">(</span>key<span class="token punctuation">,</span> value<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
    <span class="token keyword">typeof</span> value <span class="token operator">===</span> <span class="token string">"bigint"</span>
      <span class="token operator">?</span> JSON<span class="token punctuation">.</span><span class="token function">rawJSON</span><span class="token punctuation">(</span>value<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
      <span class="token punctuation">:</span> value<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">log</span><span class="token punctuation">(</span>json<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// {"messageId":1183028002140618753,"channel":"general"}</span>
</code></pre><p>Used together, the two APIs let this program recover the original digits as a <code>BigInt</code> and serialize those digits back into JSON without rounding them.</p><p>Using those APIs doesn&rsquo;t mean you should turn every integer into a <code>BigInt</code>. A count, price and database identifier may all appear as JSON numbers, but they do not necessarily belong in the same underlying JavaScript type. That said, it is important to remember that the parser exposes <code>context.source</code> only for unmodified primitive values, and <code>JSON.rawJSON()</code> accepts only valid JSON text representing a primitive value.</p><h2 id="convert-bytes-to-and-from-base64-or-hex">Convert Bytes to and from Base64 or Hex</h2><p>Binary data has often taken an unnecessary detour through strings. Converting to and from bytes wasn&rsquo;t a natural interface in the language. I think Bun was the first JS runtime I used that had a built-in API for converting bytes to various data types. Fortunately, the 2026 ECMAScript standard release brings the following functions for converting bytes:</p><ul><li>Uint8Array.prototype.toBase64</li><li>Uint8Array.prototype.toHex</li><li>Uint8Array.prototype.setFromHex and its static form Uint8Array.fromHex</li><li>Uint8Array.prototype.setFromBase64 and its static form Uint8Array.fromBase64</li></ul><p>How are they useful, you may ask?</p><p>Imagine you want to create a URL-safe token. The code might look like this:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> bytes <span class="token operator">=</span> crypto<span class="token punctuation">.</span><span class="token function">getRandomValues</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Uint8Array</span><span class="token punctuation">(</span><span class="token number">32</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> token <span class="token operator">=</span> <span class="token function">btoa</span><span class="token punctuation">(</span>String<span class="token punctuation">.</span><span class="token function">fromCharCode</span><span class="token punctuation">(</span><span class="token operator">...</span>bytes<span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">replaceAll</span><span class="token punctuation">(</span><span class="token string">"+"</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">replaceAll</span><span class="token punctuation">(</span><span class="token string">"/"</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">replace</span><span class="token punctuation">(</span><span class="token regex">/=+$/</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The program starts with bytes, turns them into a temporary string, encodes that string and then adjusts the alphabet and padding. It works, but none of those intermediate steps express the real task: encode these bytes as base64url.</p><p>We can use the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/toBase64"><code>Uint8Array.prototype.toBase64()</code></a> method to do the conversion instead:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> bytes <span class="token operator">=</span> crypto<span class="token punctuation">.</span><span class="token function">getRandomValues</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Uint8Array</span><span class="token punctuation">(</span><span class="token number">32</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> token <span class="token operator">=</span> bytes<span class="token punctuation">.</span><span class="token function">toBase64</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  alphabet<span class="token punctuation">:</span> <span class="token string">"base64url"</span><span class="token punctuation">,</span>
  omitPadding<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>
</code></pre><p>The reverse conversion is also as simple as:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> decoded <span class="token operator">=</span> Uint8Array<span class="token punctuation">.</span><span class="token function">fromBase64</span><span class="token punctuation">(</span>token<span class="token punctuation">,</span> <span class="token punctuation">{</span>
  alphabet<span class="token punctuation">:</span> <span class="token string">"base64url"</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 <code>setFromBase64()</code> and <code>setFromHex()</code> methods write into an existing array and return an object with <code>read</code> and <code>written</code> counts. Unlike <code>fromBase64()</code> and <code>fromHex()</code>, they are useful when you need to control memory allocation, decode into a preallocated buffer or track how much input fits. See the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/fromBase64">docs for <code>Uint8Array.fromBase64()</code></a> to learn about the available input options and runtime support.</p><p>These methods do not replace <code>TextEncoder</code> or <code>TextDecoder</code>. Use those APIs to convert between text and bytes; use the new <code>Uint8Array</code> methods to convert between bytes and base64 or hexadecimal representations.</p><h2 id="recognize-error-objects-across-realms">Recognize Error Objects Across Realms</h2><p>The <code>instanceof Error</code> looks like the obvious way to check if an object is an <code>Error</code>. It stops being reliable when the value comes from another JavaScript realm, e.g., an iframe or the Node.js <code>vm</code> context. Each realm has its own <code>Error</code> constructor, so a genuine error from another realm can fail an <code>instanceof</code> check.</p><p>Try this in the browser console:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> iframe <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">"iframe"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>iframe<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> otherError <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">iframe<span class="token punctuation">.</span>contentWindow<span class="token punctuation">.</span>Error</span><span class="token punctuation">(</span><span class="token string">"Failure"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>otherError <span class="token keyword">instanceof</span> <span class="token class-name">Error</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// false</span>
</code></pre><p>This may not surprise many experienced JavaScript programmers who have been deceived by some JavaScript quirkiness.</p><p>The solution is to use the new <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/isError"><code>Error.isError()</code></a>. <code>Error.isError()</code> performs a built-in check by testing for the internal <code>[[ErrorData]]</code> slot instead of relying on the current realm&rsquo;s prototype chain. This makes it analogous to <code>Array.isArray()</code> as a reliable cross-realm check.</p><p>If you append <code>console.log(Error.isError(otherError))</code> to the previous code snippet you ran in your browser console, you should see the correct result.</p><p>The method is also useful in a <code>catch</code> block because JavaScript allows any value to be thrown:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">try</span> <span class="token punctuation">{</span>
  <span class="token keyword">await</span> <span class="token function">runPlugin</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">catch</span> <span class="token punctuation">(</span><span class="token class-name">value</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> error <span class="token operator">=</span> Error<span class="token punctuation">.</span><span class="token function">isError</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span>
    <span class="token operator">?</span> value
    <span class="token punctuation">:</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token function">String</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> cause<span class="token punctuation">:</span> value <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token function">reportError</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>You should use <code>Error.isError()</code> when you need to know whether a value is a real Error object. It deliberately does not treat a plain object with <code>name</code> and <code>message</code> properties as one.</p><h2 id="sum-floating-point-values-more-accurately">Sum Floating-point Values More Accurately</h2><p>A straightforward <code>reduce()</code> can lose information while adding floating-point values, and the failure mode is sneakier than you&rsquo;d expect, because the result doesn&rsquo;t always look obviously wrong. Consider a motion-sensor library that applies a large per-device calibration offset, adds a small reading, then removes the offset again:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> readings <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">1e16</span><span class="token punctuation">,</span> <span class="token number">3.5</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">1e16</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> total <span class="token operator">=</span> readings<span class="token punctuation">.</span><span class="token function">reduce</span><span class="token punctuation">(</span>
  <span class="token punctuation">(</span>sum<span class="token punctuation">,</span> value<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> sum <span class="token operator">+</span> value<span class="token punctuation">,</span>
  <span class="token number">0</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">log</span><span class="token punctuation">(</span>total<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 4</span>
</code></pre><p>The correct answer is <code>3.5</code>&mdash;that&rsquo;s the actual reading once the offset cancels out. Near <code>1e16</code>, adjacent representable numbers are two units apart. The exact value <code>1e16 + 3.5</code> therefore rounds to <code>1e16 + 4</code>, and subtracting the offset leaves 4 instead of 3.5. The result isn&rsquo;t a crash but a plausible-looking wrong number, which is what makes this type of bug easy to miss in code review.</p><p>That&rsquo;s where <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sumPrecise"><code>Math.sumPrecise()</code></a> comes in. It uses a more accurate summation algorithm, so switching from <code>.reduce()</code> to <code>Math.sumPrecise()</code> gets you the correct answer.</p><p>Here&rsquo;s an accurate way to rewrite it:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> total <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">sumPrecise</span><span class="token punctuation">(</span>readings<span class="token punctuation">)</span><span class="token punctuation">;</span>

console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>total<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 3.5</span>
</code></pre><p>The <code>Math.sumPrecise()</code> method accepts an iterable of numbers. It does not coerce strings or <code>BigInt</code> values into numbers. An empty iterable, or one containing only <code>-0</code>, returns <code>-0</code>. The name deserves one warning though, because <code>Precise</code> does not mean decimal arithmetic precision. This <em>familiar</em> result does not change:</p><pre class=" language-js"><code class="prism  language-js">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">sumPrecise</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">0.1</span><span class="token punctuation">,</span> <span class="token number">0.2</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">// 0.30000000000000004</span>
</code></pre><p>Both inputs are already binary floating-point approximations. <code>Math.sumPrecise()</code> reduces the additional error introduced while summing them; it does not change how JavaScript represents numbers. That makes it useful for numerical aggregation, but not a complete solution for money. Use an appropriate decimal type or an integer representation for financial values.</p><h2 id="thats-a-wrap">That&rsquo;s a Wrap</h2><p>The useful part of the ECMAScript 2026 standard is not a dramatic change to the language. It is that several common use cases now have direct names: get a Map value or create it, preserve the original digits from JSON, encode bytes without pretending they are text, collect an asynchronous sequence, recognize a real Error, sum numbers with less loss and join iterables lazily. None of these APIs will transform an application on its own. They can, however, replace code that is easy to repeat (boilerplate code), easy to get slightly wrong or harder to understand than the operation it performs.</p><p>That is why JavaScript has become nicer to use!</p><p>Make sure to check runtime support before using these additions in production. The standard defines the language, but every runtime follows its own release schedule. You can get the source code for some of the examples on <a target="_blank" href="https://github.com/pmbanugo/es2026-samples-and-verifier">GitHub</a>.</p><h2 id="further-reading">Further Reading</h2><ul><li><a target="_blank" href="https://ecma-international.org/publications-and-standards/standards/ecma-262/">ECMAScript 2026 language specification</a></li><li><a target="_blank" href="https://github.com/tc39/proposals/blob/cae61138d3872cb9748effe04b729b7152f71369/finished-proposals.md">TC39 finished proposals at the ECMAScript 2026 cutoff</a></li></ul><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">AI Can&rsquo;t Solve It All: What Frontend Devs Still Hate Working On</h4></div><div class="col-8"><p class="u-fs16 u-mb0">What still causes the most friction when building modern web applications? <a target="_blank" href="https://www.telerik.com/blogs/ai-cant-solve-all-what-120-frontend-developers-say-they-still-hate-working">120+ developers at JSNation and React Summit weigh in.</a></p></div></div></aside><img src="https://feeds.telerik.com/link/10827/17421667.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:68d0b963-eb80-4d92-a001-5ca19733c57f</id>
    <title type="text">Telerik UI for Blazor TaskBoard for Enterprise Workflows</title>
    <summary type="text">See how the Blazor TaskBoard can equip your app with a handy Kanban-style visualized workflow.</summary>
    <published>2026-08-12T20:22:31Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Dimo Dimov </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17416596/telerik-ui-blazor-taskboard-enterprise-workflows"/>
    <content type="text"><![CDATA[<p><span class="featured">See how the Blazor TaskBoard can equip your app with a handy Kanban-style visualized workflow.</span></p><p>In the modern fast-paced era, teams need more than a simple to-do list. They need structured, visual workflows that reflect real project states, support multiple roles and integrate smoothly into existing processes. The Progress <a target="_blank" href="https://www.telerik.com/blazor-ui/taskboard">Telerik UI for Blazor TaskBoard</a>, also known as a Kanban Board, is purpose-built for exactly that.</p><p>In this post, we will explore the key TaskBoard features and walk through a practical two-role enterprise scenario: an administrator who configures the board and a user who works with the tasks.</p><h2 id="what-is-the-telerik-ui-for-blazor-taskboard">What Is the Telerik UI for Blazor TaskBoard?</h2><p>The <code>TelerikTaskBoard</code> component displays task and progress information as <strong>Cards</strong> organized in <strong>Columns</strong>. The component supports:</p><ul><li>Flexible data binding with convention-based or custom property names</li><li>Drag-and-drop for Cards and Columns</li><li>Built-in and custom toolbar tools</li><li>CRUD operations for Cards and Columns</li><li>A rich event model</li><li>Card priorities with color coding</li><li>Column-level restrictions about the maximum allowed number of Cards</li><li>Customizable templates for Cards and Column headers</li></ul><h3 id="data-binding">Data Binding</h3><p>The TaskBoard recognizes a set of default model property names out of the box, which means you can bind your data without any extra configuration as long as your classes follow the naming convention. For simplicity, the examples in this blog post will follow the default conventions.</p><p>The <code>Status</code> property is the link between Cards and Columns: a Card is rendered inside the Column with a matching <code>Status</code> value. If your domain model uses different property names, you can map them via parameters such as <code>CardStatusField</code>, <code>ColumnStatusField</code>, etc.</p><h4 id="card-model">Card Model</h4><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">TaskBoardCard</span> 
<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">public</span> <span class="token keyword">int</span>    Index       <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">string</span> Title       <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">string</span> Description <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">string</span> Status      <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">string</span> Priority    <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><h4 id="column-model">Column Model</h4><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">TaskBoardColumn</span> 
<span class="token punctuation">{</span> 
    <span class="token keyword">public</span> <span class="token keyword">int</span>                     Index    <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">string</span>                  Status   <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">string</span>                  Title    <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">string</span>                  Width    <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><span class="token operator">?</span>                    WipLimit <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">bool</span>                    Enabled  <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> TaskBoardColumnButtons<span class="token operator">?</span> Buttons  <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><h3 id="drag-and-drop">Drag and Drop</h3><p>Drag-and-drop is the signature UX feature of any Kanban board, and the TaskBoard delivers it with minimal setup.</p><h3 id="dragging-cards">Dragging Cards</h3><p>Card-dragging is enabled by default (<code>CardDraggable="true"</code>). When a user drops a Card into a different column or a different position within the same column, the <code>OnCardMove</code> event fires. Update the Card&rsquo;s <code>Index</code> and <code>Status</code> in the handler:</p><pre class=" language-razor"><code class="prism  language-razor">&lt;TelerikTaskBoard OnCardMove="@OnTaskBoardCardMove" /&gt; 
 
@code { 
    private void OnTaskBoardCardMove(TaskBoardCardMoveEventArgs&lt;TaskBoardCard&gt; args) 
    { 
        args.Item.Index  = args.NewIndex; 
        args.Item.Status = args.NewStatus; 
    } 
} 
</code></pre><h3 id="reordering-columns">Reordering Columns</h3><p>Column reordering is disabled by default (<code>ColumnReorderable="false"</code>). When enabled, the <code>OnColumnReorder</code> event gives you the old and new index so you can update the original data source or cancel the operation:</p><pre class=" language-razor"><code class="prism  language-razor">&lt;TelerikTaskBoard ColumnReorderable="true" 
                  OnColumnReorder="@OnColumnReorder" /&gt; 
 
@code { 
    private void OnColumnReorder(TaskBoardColumnReorderEventArgs&lt;TaskBoardColumn&gt; args) 
    { 
        // Use args.NewIndex to update the original Column data source 
        // or 
        // args.IsCancelled = true; // Revert the reordering 
    } 
} 
</code></pre><h3 id="editing-cards-and-columns">Editing Cards and Columns</h3><p>The TaskBoard supports built-in create, edit and delete operations for both Cards and Columns.</p><h3 id="card-crud-events">Card CRUD Events</h3><p>Each operation requires a corresponding event handler that updates your data collection:</p><pre class=" language-razor"><code class="prism  language-razor">&lt;TelerikTaskBoard CardData="@TaskBoardCards" 
                  OnCardCreate="@OnCreate" 
                  OnCardUpdate="@OnUpdate" 
                  OnCardDelete="@OnDelete" /&gt; 
 
@code { 
    private List&lt;TaskBoardCard&gt; TaskBoardCards { get; set; } = new List&lt;TaskBoardCard&gt;(); 
 
    private void OnCreate(TaskBoardCardCreateEventArgs&lt;TaskBoardCard&gt; args) 
    { 
        args.Item.Id = 12345; 
 
        TaskBoardCards.Add(args.Item); 
    } 
 
    private void OnUpdate(TaskBoardCardUpdateEventArgs&lt;TaskBoardCard&gt; args) 
    { 
        args.OriginalItem.Title       = args.Item.Title; 
        args.OriginalItem.Description = args.Item.Description; 
        args.OriginalItem.Priority    = args.Item.Priority; 
    } 
 
    private void OnDelete(TaskBoardCardDeleteEventArgs&lt;TaskBoardCard&gt; args) 
    { 
        TaskBoardCards.Remove(args.Item); 
    } 
} 
</code></pre><h3 id="column-crud-events">Column CRUD Events</h3><p>Column operations follow the same pattern. When a Column is deleted, the app decides what to do with orphaned Cards&mdash;keep them, delete them or move them to another column:</p><pre class=" language-razor"><code class="prism  language-razor">&lt;TelerikTaskBoard ColumnData="@TaskBoardColumns" 
                  OnColumnCreate="@OnTaskBoardColumnCreate" 
                  OnColumnDelete="@OnTaskBoardColumnDelete" 
                  OnColumnUpdate="@OnTaskBoardColumnUpdate" /&gt; 
 
@code { 
    private List&lt;TaskBoardColumn&gt; TaskBoardColumns { get; set; } = new List&lt;TaskBoardColumn&gt;(); 
 
    private void OnTaskBoardColumnCreate(TaskBoardColumnCreateEventArgs&lt;TaskBoardColumn&gt; args) 
    { 
        args.Item.Status = "new-unique-status"; 
        TaskBoardColumns.Add(args.Item); 
    } 
 
    private void OnTaskBoardColumnDelete(TaskBoardColumnDeleteEventArgs&lt;TaskBoardColumn&gt; args) 
    { 
        TaskBoardColumns.Remove(args.Item); 
    } 
 
    private void OnTaskBoardColumnUpdate(TaskBoardColumnUpdateEventArgs&lt;TaskBoardColumn&gt; args) 
    { 
        args.OriginalItem.Title = args.Item.Title; 
        args.OriginalItem.Status = args.Item.Status; 
        args.OriginalItem.Width = args.Item.Width; 
        args.OriginalItem.WipLimit = args.Item.WipLimit; 
    } 
} 
</code></pre><h3 id="toolbar">ToolBar</h3><p>The <code>TaskBoardToolBar</code> sits above the Columns and provides both built-in and custom tools. With regard to editing, the ToolBar provides a built-in <code>&lt;TaskBoardToolBarAddColumnTool /&gt;</code> component that renders a Button that fires the <code>OnColumnCreate</code> event.</p><pre class=" language-razor"><code class="prism  language-razor">&lt;TelerikTaskBoard&gt; 
    &lt;TaskBoardToolBar&gt; 
        &lt;TaskBoardToolBarAddColumnTool Icon="@SvgIcon.Plus" /&gt; 
    &lt;/TaskBoardToolBar&gt; 
&lt;/TelerikTaskBoard&gt; 
</code></pre><h2 id="two-role-taskboard-use-case">Two-Role TaskBoard Use Case</h2><p>A common pattern separates the board configuration from day-to-day usage. The following samples demonstrate this pattern with two TaskBoard instances that share the same column settings.</p><h3 id="administrator-role-configuring-the-board-columns">Administrator Role: Configuring the Board Columns</h3><p>The administrator starts from an empty <code>TelerikTaskBoard</code> to create and manage the workflow stages (e.g., Backlog, In Progress, Under Review, Done).</p><p>A <code>TelerikGrid</code> with inline editing and row drag-and-drop lets the administrator define the available priority levels and assign a color to each. A <code>TelerikColorPalette</code> editor uses color values from the Telerik CSS theme.</p><pre class=" language-razor"><code class="prism  language-razor">@using System.Text.Json 
 
@inject IJSRuntime JS 
 
&lt;h1&gt;TaskBoard Management&lt;/h1&gt; 
 
&lt;h2&gt;TaskBoard Columns&lt;/h2&gt; 
 
&lt;TelerikTaskBoard CardData="@TaskBoardCards" 
                  ColumnData="@TaskBoardColumns" 
                  ColumnReorderable="true" 
                  Height="300px" 
                  OnColumnCreate="@OnTaskBoardColumnCreate" 
                  OnColumnDelete="@OnTaskBoardColumnDelete" 
                  OnColumnReorder="@OnTaskBoardColumnReorder" 
                  OnColumnUpdate="@OnTaskBoardColumnUpdate" 
                  TColumn="@TaskBoardColumn" 
                  TItem="@TaskBoardCard"&gt; 
    &lt;TaskBoardSettings&gt; 
        &lt;TaskBoardColumnSettings Buttons="@(TaskBoardColumnButtons.EditColumn | TaskBoardColumnButtons.DeleteColumn)" 
                                 Width="300px" /&gt; 
    &lt;/TaskBoardSettings&gt; 
    &lt;TaskBoardToolBar&gt; 
        &lt;TaskBoardToolBarAddColumnTool Icon="@SvgIcon.Plus" /&gt; 
    &lt;/TaskBoardToolBar&gt; 
&lt;/TelerikTaskBoard&gt; 
 
&lt;h2&gt;Task Priorities&lt;/h2&gt; 
 
&lt;TelerikGrid Data="@TaskBoardPriorities" 
             ConfirmDelete="true" 
             EditMode="@GridEditMode.Inline" 
             OnUpdate="@OnGridUpdate" 
             OnCreate="@OnGridCreate" 
             OnDelete="@OnGridDelete" 
             RowDraggable="true" 
             OnRowDrop="@OnGridRowDrop" 
             TItem="@TaskBoardCardPriority"&gt; 
    &lt;GridToolBarTemplate&gt; 
        &lt;GridCommandButton Command="Add"&gt;Add Item&lt;/GridCommandButton&gt; 
    &lt;/GridToolBarTemplate&gt; 
    &lt;GridColumns&gt; 
        &lt;GridColumn Field="@nameof(TaskBoardCardPriority.Text)" /&gt; 
        &lt;GridColumn Field="@nameof(TaskBoardCardPriority.Color)"&gt; 
            &lt;Template&gt; 
                @{ TaskBoardCardPriority priority = (TaskBoardCardPriority)context; } 
                &lt;div class="priority-color"&gt; 
                    &lt;strong style="background-color: @(priority.Color);"&gt;&lt;/strong&gt; 
                    &lt;span&gt;@GetColorKeyword(priority.Color)&lt;/span&gt; 
                &lt;/div&gt; 
            &lt;/Template&gt; 
            &lt;EditorTemplate&gt; 
                @{ TaskBoardCardPriority priority = (TaskBoardCardPriority)context; } 
                &lt;TelerikColorPalette @bind-Value="@priority.Color" 
                                     Colors="@PriorityColors" 
                                     Columns="6" 
                                     Size="@ThemeConstants.ColorPalette.Size.Large" /&gt; 
            &lt;/EditorTemplate&gt; 
        &lt;/GridColumn&gt; 
        &lt;GridCommandColumn Title="Commands"&gt; 
            &lt;GridCommandButton Command="Edit"&gt;Edit&lt;/GridCommandButton&gt; 
            &lt;GridCommandButton Command="Save" ShowInEdit="true"&gt;Save&lt;/GridCommandButton&gt; 
            &lt;GridCommandButton Command="Cancel" ShowInEdit="true"&gt;Cancel&lt;/GridCommandButton&gt; 
            &lt;GridCommandButton Command="Delete"&gt;Delete&lt;/GridCommandButton&gt; 
        &lt;/GridCommandColumn&gt; 
    &lt;/GridColumns&gt; 
&lt;/TelerikGrid&gt; 
 
&lt;TelerikButton OnClick="@OnConfirmTaskBoardClick"&gt;Confirm TaskBoard Configuration&lt;/TelerikButton&gt; 
 
&lt;style&gt; 
    .priority-color { 
        display: flex; 
        align-items: center; 
    } 
 
    .priority-color &gt; strong { 
        display: inline-block; 
        width: 1.2em; 
        height: 1.2em; 
        margin-right: .5em; 
    } 
&lt;/style&gt; 
 
@code { 
    private List&lt;TaskBoardCard&gt; TaskBoardCards { get; set; } = new List&lt;TaskBoardCard&gt;(); 
    private List&lt;TaskBoardColumn&gt; TaskBoardColumns { get; set; } = new List&lt;TaskBoardColumn&gt;(); 
    private List&lt;TaskBoardCardPriority&gt; TaskBoardPriorities { get; set; } = new List&lt;TaskBoardCardPriority&gt;() 
        { 
            new TaskBoardCardPriority() { Text = "Low", Value = "low", Color = "var(--kendo-color-success)" }, 
            new TaskBoardCardPriority() { Text = "Normal", Value = "normal", Color = "var(--kendo-color-info)" }, 
            new TaskBoardCardPriority() { Text = "High", Value = "high", Color = "var(--kendo-color-warning)"}, 
            new TaskBoardCardPriority() { Text = "Critical", Value = "critical", Color = "var(--kendo-color-error)" } 
        }; 
 
    private void OnTaskBoardColumnCreate(TaskBoardColumnCreateEventArgs&lt;TaskBoardColumn&gt; args) 
    { 
            args.Item.Status = $"status-{++LastId}"; 
            TaskBoardColumns.Add(args.Item); 
    } 
 
    private void OnTaskBoardColumnDelete(TaskBoardColumnDeleteEventArgs&lt;TaskBoardColumn&gt; args) 
    { 
        TaskBoardColumns.Remove(args.Item); 
 
        TaskBoardCards.RemoveAll(c =&gt; c.Status == args.Item.Status); 
    } 
 
    private void OnTaskBoardColumnReorder(TaskBoardColumnReorderEventArgs&lt;TaskBoardColumn&gt; args) 
    { 
 
    } 
 
    private void OnTaskBoardColumnUpdate(TaskBoardColumnUpdateEventArgs&lt;TaskBoardColumn&gt; args) 
    { 
        args.OriginalItem.Title = args.Item.Title; 
        args.OriginalItem.Status = args.Item.Status; 
        args.OriginalItem.Width = args.Item.Width; 
        args.OriginalItem.WipLimit = args.Item.WipLimit; 
    } 
 
    private async Task OnConfirmTaskBoardClick() 
    { 
        await JS.InvokeVoidAsync("localStorage.setItem", 
                new object\\\[] { "taskboard-columns", JsonSerializer.Serialize(TaskBoardColumns) }); 
 
        await JS.InvokeVoidAsync("localStorage.setItem", 
                new object\\\[] { "taskboard-priorities", JsonSerializer.Serialize(TaskBoardPriorities) }); 
    } 
 
    private readonly string\\\[] PriorityColors = new string\\\[] 
    { 
        "var(--kendo-color-info)", 
        "var(--kendo-color-primary)", 
        "var(--kendo-color-secondary)", 
        "var(--kendo-color-tertiary)", 
        "var(--kendo-color-inverse)", 
        "var(--kendo-color-success)", 
        "var(--kendo-color-warning)", 
        "var(--kendo-color-error)", 
        "var(--kendo-color-series-b)", 
        "var(--kendo-color-series-c)", 
        "var(--kendo-color-series-d)", 
        "var(--kendo-color-series-f)" 
    }; 
 
    private string GetColorKeyword(string themeColorName) 
    { 
        return themeColorName.Replace("var(--kendo-color-", "").Replace(")", ""); 
    } 
 
    private void OnGridDelete(GridCommandEventArgs args) 
    { 
        TaskBoardCardPriority deletedItem = (TaskBoardCardPriority)args.Items.First(); 
 
        TaskBoardPriorities.Remove(deletedItem); 
    } 
 
    private void OnGridCreate(GridCommandEventArgs args) 
    { 
        TaskBoardCardPriority createdItem = (TaskBoardCardPriority)args.Items.First(); 
 
        TaskBoardPriorities.Insert(0, createdItem); 
    } 
 
    private void OnGridRowDrop(GridRowDropEventArgs&lt;TaskBoardCardPriority&gt; args) 
    { 
        TaskBoardPriorities.Remove(args.Item); 
 
        int destinationItemIndex = TaskBoardPriorities.IndexOf(args.DestinationItem); 
        if (args.DropPosition == GridRowDropPosition.After) 
        { 
            destinationItemIndex++; 
        } 
 
        TaskBoardPriorities.Insert(destinationItemIndex, args.Item); 
    } 
 
    private void OnGridUpdate(GridCommandEventArgs args) 
    { 
        TaskBoardCardPriority updatedItem = (TaskBoardCardPriority)args.Items.First(); 
        int originalItemIndex = TaskBoardPriorities.FindIndex(i =&gt; i.Value == updatedItem.Value); 
 
        if (originalItemIndex != -1) 
        { 
            TaskBoardPriorities\\\[originalItemIndex] = updatedItem; 
        } 
    } 
 
    private int LastId { get; set; } 
 
    public class TaskBoardCard 
    { 
        public string Description { get; set; } = string.Empty; 
        public int Id { get; set; } 
        public int Index { get; set; } 
        public string Priority { get; set; } = string.Empty; 
        public string Status { get; set; } = string.Empty; 
        public string Title { get; set; } = string.Empty; 
    } 
 
    public class TaskBoardColumn 
    { 
        public TaskBoardColumnButtons? Buttons { get; set; } 
        public bool Enabled { get; set; } = true; 
        public int Index { get; set; } 
        public string Status { get; set; } = string.Empty; 
        public string Title { get; set; } = string.Empty; 
        public string Width { get; set; } = string.Empty; 
        public int? WipLimit { get; set; } 
    } 
} 
</code></pre><h3 id="user-role-working-with-tasks">User Role: Working with Tasks</h3><p>The second example demonstrates the user&rsquo;s workspace. It loads the column and priority definitions saved by the administrator and then presents a fully operational <code>TelerikTaskBoard</code> where cards can be created, edited, moved and deleted.</p><p>Notice that the column management buttons are absent, and the user can only manage cards. The <code>TaskBoardSearchBox</code> tool lets them quickly filter cards by title or description. The task priorities automatically color-code the left border of each card.</p><pre class=" language-razor"><code class="prism  language-razor">@using System.Text.Json 
@using Telerik.Blazor.Components.TaskBoard 
 
@inject IJSRuntime JS 
 
&lt;PageTitle&gt;Counter&lt;/PageTitle&gt; 
 
&lt;TelerikTaskBoard CardData="@TaskBoardCards" 
                  ColumnData="@TaskBoardColumns" 
                  ColumnReorderable="true" 
                  Height="600px" 
                  Priorities="@TaskBoardPriorities" 
                  OnCardCreate="@OnTaskBoardCardCreate" 
                  OnCardDelete="@OnTaskBoardCardDelete" 
                  OnCardMove="@OnTaskBoardCardMove" 
                  OnCardUpdate="@OnTaskBoardCardUpdate" 
                  TColumn="@TaskBoardColumn" 
                  TItem="@TaskBoardCard"&gt; 
    &lt;TaskBoardSettings&gt; 
        &lt;TaskBoardColumnSettings Buttons="@(TaskBoardColumnButtons.AddCard)" 
                                 Width="300px" /&gt; 
    &lt;/TaskBoardSettings&gt; 
    &lt;TaskBoardToolBar&gt; 
        &lt;TaskBoardSearchBox /&gt; 
    &lt;/TaskBoardToolBar&gt; 
&lt;/TelerikTaskBoard&gt; 
 
@code { 
    private List&lt;TaskBoardCard&gt; TaskBoardCards { get; set; } = new List&lt;TaskBoardCard&gt;(); 
    private List&lt;TaskBoardColumn&gt; TaskBoardColumns { get; set; } = new List&lt;TaskBoardColumn&gt;(); 
    private List&lt;TaskBoardCardPriority&gt; TaskBoardPriorities { get; set; } = new List&lt;TaskBoardCardPriority&gt;(); 
 
    private void OnTaskBoardCardCreate(TaskBoardCardCreateEventArgs&lt;TaskBoardCard&gt; args) 
    { 
        args.Item.Id = ++LastId; 
 
        // Optionally, add the Card to the bottom of the column 
        //int maxIndexInColumn = TaskBoardCards.Where(c =&gt; c.Status == args.Item.Status).Select(c =&gt; c.Index).DefaultIfEmpty(0).Max(); 
        //args.Item.Index = ++maxIndexInColumn; 
 
        TaskBoardCards.Add(args.Item); 
    } 
 
    private void OnTaskBoardCardDelete(TaskBoardCardDeleteEventArgs&lt;TaskBoardCard&gt; args) 
    { 
        TaskBoardCards.Remove(args.Item); 
    } 
 
    private void OnTaskBoardCardMove(TaskBoardCardMoveEventArgs&lt;TaskBoardCard&gt; args) 
    { 
        args.Item.Index = args.NewIndex; 
        args.Item.Status = args.NewStatus; 
    } 
 
    private void OnTaskBoardCardUpdate(TaskBoardCardUpdateEventArgs&lt;TaskBoardCard&gt; args) 
    { 
        args.OriginalItem.Description = args.Item.Description; 
        args.OriginalItem.Index = args.Item.Index; 
        args.OriginalItem.Priority = args.Item.Priority; 
        args.OriginalItem.Title = args.Item.Title; 
        args.OriginalItem.Status = args.Item.Status; 
    } 
 
    protected override async Task OnAfterRenderAsync(bool firstRender) 
    { 
        if (firstRender) 
        { 
            //TaskBoardColumns = await LocalStorage.GetItem&lt;List&lt;TaskBoardColumn&gt;&gt;("taskboard-columns") ?? new List&lt;TaskBoardColumn&gt;(); 
            //TaskBoardPriorities = await LocalStorage.GetItem&lt;List&lt;TaskBoardCardPriority&gt;&gt;("taskboard-priorities") ?? new List&lt;TaskBoardCardPriority&gt;(); 
 
            string serializedColumns = await JS.InvokeAsync&lt;string&gt;("localStorage.getItem", "taskboard-columns"); 
            TaskBoardColumns = JsonSerializer.Deserialize&lt;List&lt;TaskBoardColumn&gt;&gt;(serializedColumns) ?? new List&lt;TaskBoardColumn&gt;(); 
 
            string serializedPriorities = await JS.InvokeAsync&lt;string&gt;("localStorage.getItem", "taskboard-priorities"); 
            TaskBoardPriorities = JsonSerializer.Deserialize&lt;List&lt;TaskBoardCardPriority&gt;&gt;(serializedPriorities) ?? new List&lt;TaskBoardCardPriority&gt;(); 
 
            StateHasChanged(); 
        } 
    } 
 
    private int LastId { get; set; } 
 
    public class TaskBoardCard 
    { 
        public string Description { get; set; } = string.Empty; 
        public int Id { get; set; } 
        public int Index { get; set; } 
        public string Priority { get; set; } = string.Empty; 
        public string Status { get; set; } = string.Empty; 
        public string Title { get; set; } = string.Empty; 
    } 
 
    public class TaskBoardColumn 
    { 
        public TaskBoardColumnButtons? Buttons { get; set; } 
        public bool Enabled { get; set; } = true; 
        public int Index { get; set; } 
        public string Status { get; set; } = string.Empty; 
        public string Title { get; set; } = string.Empty; 
        public string Width { get; set; } = string.Empty; 
        public int? WipLimit { get; set; } 
 
        public TaskBoardColumn Clone() 
        { 
            return new TaskBoardColumn() 
            { 
                Status = this.Status, 
                Title = this.Title, 
                Width = this.Width, 
                WipLimit = this.WipLimit 
            }; 
        } 
    } 
} 
</code></pre><h2 id="key-takeaways">Key Takeaways</h2><p>The Telerik UI for Blazor TaskBoard fits naturally into enterprise workflows with its data-driven and event-driven architecture. Here are the design principles the examples above demonstrate:</p><ul><li><strong>Separation of concerns</strong> &ndash; Board structure (columns, priorities) lives apart from the task data, enabling role-based access and centralized configuration.</li><li><strong>Convention-based models</strong> &ndash; Default property names (<code>Status</code>, <code>Index</code>, <code>Title</code>, etc.) eliminate boilerplate configuration while still allowing full customization via field-mapping parameters.</li><li><strong>Event-sourced mutations</strong> &ndash; Every user action fires a cancellable event. Your handlers decide what gets persisted and how, making integration with any backend straightforward.</li><li><strong>Progressive disclosure</strong> &ndash; <code>TaskBoardCardSettings</code> and <code>TaskBoardColumnSettings</code> let you show exactly the right buttons to the right roles.</li><li><strong>Theme-aware colors</strong> &ndash; Using CSS theme variables (<code>var(--kendo-color-error)</code>) for priority colors enables visual consistency across Telerik themes with zero extra work.</li></ul><h3 id="resources">Resources</h3><ul><li><a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/components/taskboard/overview">TaskBoard Documentation</a></li><li><a target="_blank" href="https://demos.telerik.com/blazor-ui/taskboard/overview">TaskBoard Live Demos</a></li></ul><h2 id="ready-to-experiment-with-the-taskboard-control">Ready to Experiment with the TaskBoard Control?</h2><p>TaskBoard and 120 other Telerik UI for Blazor components are all available for a free 30-day trial:</p><p><a target="_blank" href="https://www.telerik.com/try/ui-for-blazor" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/10827/17416596.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:bd100e25-ddba-4ff3-b841-0f92766ab6d4</id>
    <title type="text">Understanding Race Conditions in ASP.NET Core</title>
    <summary type="text">Explore the concept of race conditions in ASP.NET Core, understand how to identify them in practice and learn strategies to protect applications against this type of issue.</summary>
    <published>2026-08-11T18:04:18Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17415006/understanding-race-conditions-aspnet-core"/>
    <content type="text"><![CDATA[<p><span class="featured">Explore the concept of race conditions in ASP.NET Core, understand how to identify them in practice and learn strategies to protect applications against this type of issue.</span></p><p>Two requests arrive at your application at the exact same moment. Both read the same data, both attempt to update it, and everything appears to work correctly. Yet one update silently overwrites the other. This is a classic race condition. In this post, you&rsquo;ll see how these problems arise and how to prevent them using common concurrency control techniques.</p><p>Working with systems that execute multiple operations in milliseconds is not a distant reality. On the contrary, in medium- and large-scale applications, this is a common scenario.</p><p>But along with this level of concurrency comes a problem that is often overlooked: race conditions. When not handled properly, they can lead to inconsistencies and even silently corrupt data.</p><p>In this post, we will explore the concept of race conditions, understand how to identify them in practice, and discuss strategies to protect our applications against this type of issue.</p><h2 id="what-is-a-race-condition">What Is a Race Condition?</h2><p>According to Microsoft documentation on <a target="_blank" href="https://learn.microsoft.com/en-us/troubleshoot/developer/visualstudio/visual-basic/language-compilers/race-conditions-deadlocks">race conditions and deadlocks</a>, a race condition occurs when two threads access and modify a shared resource at the same time. The final result depends on the order in which these operations are executed.</p><p>The problem is that this order is not guaranteed; it can vary with each execution. This leads to unpredictable behavior, such as inconsistent data, lost updates or invalid states.</p><p>Imagine two requests trying to update an account balance at the same time. Both read the same initial value, perform separate calculations and save the result. Depending on which one saves last, one update may overwrite the other, even if both were performed correctly in isolation.</p><h2 id="when-do-race-conditions-occur">When Do Race Conditions Occur?</h2><p>Race conditions don&rsquo;t appear &ldquo;out of nowhere.&rdquo; They usually arise from some very common code and architectural patterns. Below, we&rsquo;ll look at two of the best-known: Read-Modify-Write and Check-Then-Act.</p><h3 id="read-modify-write">Read-Modify-Write</h3><p>The idea here is simple: you read a value, make some modification to it and then write it back. The problem starts when two or more executions do this at the same time. Consider the image below:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/read-modify-write-problem.png?sfvrsn=c7122620_2" title="read modify write problem" alt="Read modify write problem" /></p><p>Note that both readings happen simultaneously, returning the value of 50. However, Thread A adds 20 to the initial value, while Thread B adds 10. The problem occurs when Thread A updates the initial value (50) to 70, while Thread B updates it 1 second later with the value of 60, completely disregarding the value previously added by Thread A.</p><p>The result is an incorrectly calculated value, something that would certainly cause losses in a production environment.</p><h3 id="check-then-act">Check-Then-Act</h3><p>The Check-Then-Act pattern is often even more subtle than the previous pattern. The problem with this pattern is not in updating a value, but in making a decision based on a state that may change before the action takes place. Consider the image below:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/check-then-act-problem.png?sfvrsn=bb7cec48_2" title="check then act problem" alt="Check Then Act problem" /></p><p>In this case we have two threads that check the stock quantity of a product. In both queries, the quantity is 1, and even though there is a check, both purchases were executed based on an invalid state. After all, when Thread A executed the purchase, there was no longer any quantity available for Thread B. If this problem had occurred in a real environment, the client of Thread B would have been left without the product.</p><h2 id="avoiding-race-conditions">Avoiding Race Conditions</h2><p>Now that we&rsquo;ve learned how to identify a race condition, let&rsquo;s understand how to prevent it from happening. There are different strategies to protect ASP.NET Core applications against concurrency issues, and choosing the most appropriate approach depends on the scenario.</p><p>The main point is to understand that any concurrent operation involving shared state needs to be handled with care.</p><h2 id="understanding-the-concept-of-thread-safety">Understanding the Concept of Thread Safety</h2><p>Thread safety is the ability of code or a resource to function correctly even when accessed simultaneously by multiple threads.</p><p>We can say that thread-safe code means that data is not corrupted, the state remains valid, and the behavior does not depend on the order of execution of the threads.</p><h3 id="using-locks">1. Using Locks</h3><p>A lock is a mechanism used to make an operation thread-safe. It allows only one thread to execute a given piece of code at a time.</p><p>Consider the code below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> System<span class="token punctuation">;</span>
<span class="token keyword">using</span> System<span class="token punctuation">.</span>Threading<span class="token punctuation">;</span>
<span class="token keyword">using</span> System<span class="token punctuation">.</span>Threading<span class="token punctuation">.</span>Tasks<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ProductService</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">object</span> _lock <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 comment">// Simulating a stock quantity</span>
    <span class="token keyword">private</span> <span class="token keyword">int</span> _stock <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Purchase</span><span class="token punctuation">(</span><span class="token keyword">string</span> customer<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">"{customer} is waiting to enter the critical section..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">lock</span> <span class="token punctuation">(</span>_lock<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">"{customer} entered the critical section."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">if</span> <span class="token punctuation">(</span>_stock <span class="token operator">&lt;=</span> <span class="token number">0</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">"{customer} could not complete the purchase. Product out of stock."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">return</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">"{customer} is processing the purchase..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token comment">// Simulating a slow operation</span>
            Thread<span class="token punctuation">.</span><span class="token function">Sleep</span><span class="token punctuation">(</span><span class="token number">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            _stock<span class="token operator">--</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">"{customer} completed the purchase."</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">"Remaining stock: {_stock}"</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">"{customer} left the critical section."</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> <span class="token keyword">class</span> <span class="token class-name">Program</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task <span class="token function">Main</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> service <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ProductService</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> task1 <span class="token operator">=</span> Task<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> <span class="token operator">=</span><span class="token operator">&gt;</span> service<span class="token punctuation">.</span><span class="token function">Purchase</span><span class="token punctuation">(</span><span class="token string">"Customer A"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> task2 <span class="token operator">=</span> Task<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> <span class="token operator">=</span><span class="token operator">&gt;</span> service<span class="token punctuation">.</span><span class="token function">Purchase</span><span class="token punctuation">(</span><span class="token string">"Customer B"</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">WhenAll</span><span class="token punctuation">(</span>task1<span class="token punctuation">,</span> task2<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>You can <a target="_blank" href="https://dotnetfiddle.net/XLYifS">run this code in Fiddle</a> and get the following result:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/lock-result.png?sfvrsn=f599ef0b_2" title="lock result" alt="Lock result" /></p><p>The idea here is to simulate a scenario where only one unit of stock is available for two customers attempting to make a purchase simultaneously. Note that we declare a static object <code>_lock</code>, which acts as a guardian for the critical section of the code, so only one thread at a time can execute the logic block that checks and decrements the stock.</p><p>When the request is triggered, two distinct tasks are initiated in parallel for customers A and B. If we didn&rsquo;t use the locking structure, both could read the stock value as available at the same time, resulting in a duplicate sale of an item that only exists once, which constitutes a race condition.</p><p>However, the use of the <code>lock</code> instruction forces a waiting queue. While the first customer processes their purchase and the system waits (<code>Thread.Sleep</code>), the second customer remains held at the entrance of the critical section.</p><p>Only after the first customer&rsquo;s transaction is completed and the stock is updated to zero is the lock released for the next customer. Upon entering the critical section, the second client performs a logical security check, realizes that the stock has been depleted by the previous processing and ends the purchase attempt without causing data inconsistencies.</p><p>The end result is a predictable execution flow, where we guarantee data integrity in a critical scenario under concurrent demand.</p><h3 id="using-semaphoreslim">2. Using SemaphoreSlim</h3><p>As we saw above, a lock blocks the current thread until the critical region is released. This may not always be a good strategy, as in some cases it can reduce the scalability of the application. In these cases, SemaphoreSlim may be a more suitable alternative.</p><p>In ASP.NET Core, SemaphoreSlim is a built-in class used to limit the number of threads that can access a resource or a section of code simultaneously. When set to 1, it works similarly to a lock, allowing only one execution at a time. Consider the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> System<span class="token punctuation">;</span>
<span class="token keyword">using</span> System<span class="token punctuation">.</span>Threading<span class="token punctuation">;</span>
<span class="token keyword">using</span> System<span class="token punctuation">.</span>Threading<span class="token punctuation">.</span>Tasks<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Program</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">async</span> Task <span class="token function">Main</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> service <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ProductService</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> task1 <span class="token operator">=</span> Task<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> <span class="token operator">=</span><span class="token operator">&gt;</span> service<span class="token punctuation">.</span><span class="token function">PurchaseAsync</span><span class="token punctuation">(</span><span class="token string">"Customer A"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">var</span> task2 <span class="token operator">=</span> Task<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> <span class="token operator">=</span><span class="token operator">&gt;</span> service<span class="token punctuation">.</span><span class="token function">PurchaseAsync</span><span class="token punctuation">(</span><span class="token string">"Customer B"</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">WhenAll</span><span class="token punctuation">(</span>task1<span class="token punctuation">,</span> task2<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">"Finished."</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> <span class="token keyword">class</span> <span class="token class-name">ProductService</span>
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> SemaphoreSlim _semaphore <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">int</span> _stock <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">PurchaseAsync</span><span class="token punctuation">(</span><span class="token keyword">string</span> customer<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">"{customer} is waiting to enter the critical section..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> _semaphore<span class="token punctuation">.</span><span class="token function">WaitAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">try</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">"{customer} entered the critical section."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token keyword">if</span> <span class="token punctuation">(</span>_stock <span class="token operator">&lt;=</span> <span class="token number">0</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">"{customer} could not complete the purchase. Product out of stock."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">return</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">"{customer} is processing the purchase..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            <span class="token comment">// Simulates an asynchronous operation</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">3000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

            _stock<span class="token operator">--</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">"{customer} completed the purchase."</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">"Remaining stock: {_stock}"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
        <span class="token keyword">finally</span>
        <span class="token punctuation">{</span>
            _semaphore<span class="token punctuation">.</span><span class="token function">Release</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">"{customer} left the critical section."</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>You can <a target="_blank" href="https://dotnetfiddle.net/R6CNsN">run this code in Fiddle</a> and get the following result:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/semaphore-result.png?sfvrsn=c64f1819_2" title="semaphore result" alt="Semaphore result" /></p><p>In this code, we create an instance of SemaphoreSlim, passing an initial and maximum value of 1 as a parameter. Thus, when two threads attempt to execute <code>PurchaseAsync</code> simultaneously, the first thread enters SemaphoreSlim while the second waits.<br />After the first operation is completed, the semaphore is released and the second thread can finally continue.</p><p>As in the example with lock, this prevents two operations from altering the stock at the same time. Tthe difference here is that the threads are asynchronous, and we also have the possibility of configuring an initial and maximum value for the number of simultaneous threads.</p><h3 id="using-thread-safe-collections">3. Using Thread-Safe Collections</h3><p>Another scenario prone to race condition problems is using collections shared between multiple threads. Structures such as <code>List&lt;T&gt;</code>, <code>Dictionary&lt;TKey, TValue&gt;</code> and <code>HashSet&lt;T&gt;</code> were not designed for concurrent access. This means that collections of these types allow multiple threads to read and modify their data, generating inconsistent data and unpredictable behavior.</p><p>Consider the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp">   <span class="token keyword">private</span> <span class="token keyword">readonly</span> Dictionary<span class="token operator">&lt;</span>Guid<span class="token punctuation">,</span> Product<span class="token operator">&gt;</span> _products <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> <span class="token keyword">void</span> <span class="token function">AddProduct</span><span class="token punctuation">(</span>Product product<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _products<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>product<span class="token punctuation">.</span>Id<span class="token punctuation">,</span> product<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre><p>If multiple requests attempt to add or update items at the same time, the application may throw exceptions or even corrupt the internal state of the collection. The problem occurs because <code>Dictionary&lt;TKey, TValue&gt;</code> is not prepared for synchronization.</p><p>For concurrent scenarios, .NET provides thread-safe collections through the namespace <code>System.Collections.Concurrent</code>, one of the most commonly used being <code>ConcurrentDictionary&lt;TKey, TValue&gt;</code>. Thus, we can use <code>ConcurrentDictionary</code> with safe concurrent access between multiple threads:</p><pre class=" language-csharp"><code class="prism  language-csharp">   <span class="token keyword">private</span> <span class="token keyword">readonly</span> ConcurrentDictionary<span class="token operator">&lt;</span>Guid<span class="token punctuation">,</span> Product<span class="token operator">&gt;</span> _concurrentProducts <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> <span class="token keyword">void</span> <span class="token function">AddConcurrentProduct</span><span class="token punctuation">(</span>Product product<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _concurrentProducts<span class="token punctuation">.</span><span class="token function">TryAdd</span><span class="token punctuation">(</span>product<span class="token punctuation">.</span>Id<span class="token punctuation">,</span> product<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> Product<span class="token operator">?</span> <span class="token function">GetProduct</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _concurrentProducts<span class="token punctuation">.</span><span class="token function">TryGetValue</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> <span class="token keyword">out</span> <span class="token keyword">var</span> product<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> product<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
</code></pre><p>Now multiple threads can read <code>_concurrentProducts</code> at the same time, and concurrent operations are handled internally.</p><h3 id="using-optimistic-concurrency">4. Using Optimistic Concurrency</h3><p>Optimistic concurrency is another option for avoiding race conditions, especially in modern applications.</p><p>Unlike previously seen approaches such as locking or SemaphoreSlim, it does not attempt to prevent simultaneous accesses. Instead, it assumes that conflicts are rare and only detected when they occur.</p><p>Imagine that multiple operations can read the same data simultaneously. The first update happens normally, but subsequent updates fail if the data has been changed in the middle of the process. This prevents silent overwrites and provides consistency.</p><p>Consider the image below:<br /><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/optimistic-concurrency-example.png?sfvrsn=24e00cb9_2" title="optimistic concurrency example" alt="Optimistic Concurrency example" /></p><p>Note that both threads read the value at the same time (stock = 10, version 1), but Thread A was faster and updated the value first (stock = 9, version 2). When Thread B tries to update the value, an exception is generated because version 2 already exists. This verifies the consistency of the object&rsquo;s initial state; in this case, the stock quantity does not receive an invalid state.</p><h4 id="implementing-optimistic-concurrency-with-ef-core">Implementing Optimistic Concurrency with EF Core</h4><p>Entity Framework Core has a mechanism for using Optimistic Concurrency. To implement it, in an entity class we define a Version column as follows:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> System<span class="token punctuation">.</span>ComponentModel<span class="token punctuation">.</span>DataAnnotations<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> PracticingRaceConditions<span class="token punctuation">.</span>Models<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Product</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> Guid Id <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">internal</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> Name <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">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token keyword">int</span> Stock <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>Timestamp<span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> Version <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>This Version column informs EF Core that it will be used by the Optimistic Concurrency mechanism, and when two threads attempt to update the same record with the same version, an exception will be thrown.</p><p>To simulate the error, we can do the following:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">SimulatingPurchaseAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> options <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DbContextOptionsBuilder</span><span class="token operator">&lt;</span>ProductDbContext<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span><span class="token function">UseSqlite</span><span class="token punctuation">(</span><span class="token string">"Data Source=productsDb"</span><span class="token punctuation">)</span>
        <span class="token punctuation">.</span>Options<span class="token punctuation">;</span>

    <span class="token comment">// Request A</span>
    <span class="token keyword">using</span> <span class="token keyword">var</span> contextA <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ProductDbContext</span><span class="token punctuation">(</span>options<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">// Request B</span>
    <span class="token keyword">using</span> <span class="token keyword">var</span> contextB <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ProductDbContext</span><span class="token punctuation">(</span>options<span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> productA <span class="token operator">=</span> <span class="token keyword">await</span> contextB<span class="token punctuation">.</span>Products<span class="token punctuation">.</span><span class="token function">FirstOrDefaultAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">var</span> productB <span class="token operator">=</span> <span class="token keyword">await</span> contextB<span class="token punctuation">.</span>Products<span class="token punctuation">.</span><span class="token function">FirstOrDefaultAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    productA<span class="token punctuation">.</span>Stock<span class="token operator">--</span><span class="token punctuation">;</span>
    productB<span class="token punctuation">.</span>Stock<span class="token operator">--</span><span class="token punctuation">;</span>

    <span class="token comment">// Request A saves first</span>
    <span class="token keyword">await</span> contextA<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token keyword">try</span>
    <span class="token punctuation">{</span>
        <span class="token comment">// Request B attempts to save using an outdated version</span>
        <span class="token keyword">await</span> contextB<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</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">catch</span> <span class="token punctuation">(</span><span class="token class-name">DbUpdateConcurrencyException</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">"Concurrency conflict detected!"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>If we execute the <code>SimulatingPurchaseAsync()</code> method, we will get the following output in the console:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-08/concurrency-conflict-error.png?sfvrsn=bc8f8963_2" title="concurrency conflict error" alt="Concurrency conflict error" /></p><p>Note that when executing the <code>SimulatingPurchaseAsync()</code> method, we simulate two stock changes at the same time. When saving the result of Thread A, execution occurs normally because a version 2 of the product did not yet exist. But when trying to save the result of Thread B, a <code>DbUpdateConcurrencyException</code> exception is thrown, because EF Core detected that a version 2 was again trying to update the record.</p><p>In this way, we can use the EF Core&rsquo;s Optimistic Concurrency mechanism to prevent the race condition problem.</p><h2 id="conclusion">Conclusion</h2><p>The race condition problem occurs when two threads access and modify the same resource at the same time, which can result in an invalid state depending on the order in which the operations are performed.</p><p>In this post, we&rsquo;ve seen common examples where race conditions can occur, and learned how protect our code from these problems through approaches like Locks, SemaphoreSlim, Thread-Safe Collections and Optimistic Concurrency with EF Core. I hope this post has helped you understand what race conditions are and how to protect your applications from errors resulting from this type of problem.</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">Best Practices for Exceptions in ASP.NET Core</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Exceptions are a common approach to dealing with unexpected situations. But are they truly necessary? Let&rsquo;s see some <a target="_blank" href="https://www.telerik.com/blogs/best-practices-exceptions-aspnet-core"> best practices for using exceptions in ASP.NET Core</a>.</p></div></div></aside><img src="https://feeds.telerik.com/link/10827/17415006.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:320185f1-cb3f-4b3a-86b3-d6b5a64a70f7</id>
    <title type="text">Creating a Production-Ready CRUD Application in ASP.NET Core</title>
    <summary type="text">Are you a beginner looking to create a portfolio to impress recruiters? Or perhaps an experienced developer looking to build better basic apps ready to evolve? Learn how to create a CRUD application in ASP.NET Core that goes beyond the basics and has everything you need to stand out in the real world.</summary>
    <published>2026-08-05T13:24:07Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17403822/creating-production-ready-crud-application-aspnet-core"/>
    <content type="text"><![CDATA[<p><span class="featured">Are you a beginner looking to create a portfolio to impress recruiters? Or perhaps an experienced developer looking to build better basic apps ready to evolve? Learn how to create a CRUD application in ASP.NET Core that goes beyond the basics and has everything you need to stand out in the real world.</span></p><p>In this post, you&rsquo;ll learn how to structure an ASP.NET Core project following CRUD best practices, even when the premise is simple. The idea here is to build something you can actually use as a portfolio.</p><p>In addition, we&rsquo;ll focus on decisions that experienced developers make daily: how to avoid unnecessary coupling, how to better model the domain, and how to prepare your application to grow without becoming chaotic.</p><p>By the end of the post, you&rsquo;ll have a solid foundation on how to create a CRUD application ready to evolve into something bigger, maybe with authentication, messaging, caching, or even an event-driven architecture.</p><h2 id="-the-problem-with-generic-cruds"> The Problem with Generic CRUDs</h2><p>You&rsquo;ve probably faced a requirement like this at some point: Create a simple, straightforward CRUD that only performs the basic operations of Create, Read, Update and Delete, with entities representing tables, and everything working in a few minutes. And that&rsquo;s not wrong. The problem starts when this same model, designed for prototyping, is taken to production without being prepared for future evolution.</p><p>One of the clearest signs of a basic CRUD is the use of anemic entities, classes that only have properties, without any behavior:</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">User</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">string</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">public</span> <span class="token keyword">string</span> Name <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">string</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>
</code></pre><p>Here, the entity does not protect its own state. Therefore, it can be created with an empty name, it can have an invalid email, it can be changed from anywhere. In other words, there is no business rule, just a simple data structure.</p><p>Another classic symptom of a prematurely scaled CRUD is having rules and validations scattered throughout the code. In basic CRUDs, when rules appear, they are scattered a bit in the Controller class, a bit in the Service class, and in the worst cases they don&rsquo;t even exist.</p><p>Consider the example below:</p><pre class=" language-csharp"><code class="prism  language-csharp">   <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">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>user<span class="token punctuation">.</span>Name<span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">BadRequest</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>This solves the immediate problem, but creates a bigger one: the rule doesn&rsquo;t belong to the domain, it&rsquo;s loose in the system. Therefore, as the system grows, validations can be duplicated, lose consistency and become harder to find everywhere because they are scattered.</p><p>The biggest problem here is that you&rsquo;ve only modeled the data, not the system. Basic CRUDs focus on basic operations: creating, reading, updating and deleting data. But real systems are about behavior: A user can register, they can deactivate their account, they can change their email based on internal validations, they can have a preferred name, they can change their address &hellip;</p><p>When you only model data, the system loses meaning and becomes just a data handling tool, which can easily be replaced by anything else, like a simple Excel spreadsheet for example.</p><p>The opposite of this are applications that, despite using only basic operations, are prepared for new features that will likely be needed. The image below illustrates the main points of both approaches:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/generic-crud-vs-complete-crud.png?sfvrsn=e82eb177_2" title="generic crud vs complete crud" alt="Generic CRUD VS Complete CRUD" /></p><h2 id="-creating-a-crud-ready-for-production"> Creating a CRUD Ready for Production</h2><p>Now that we&rsquo;ve reviewed some examples to avoid, let&rsquo;s see how to create a complete CRUD application, organizing each part of the code according to best practices and preparing the project for evolution. You can access the complete project code in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/campus-hub-crud-base">Campus Hub source code</a>.</p><h2 id="architecture-and-basic-structure">Architecture and Basic Structure</h2><p>The application will be a CRUD to manage university courses, which can be registered, updated and deactivated. We will use the principles of Clean Architecture combined with tactical Domain-Driven Design (DDD) to create the basic structure of the project. Thus, the application will have the following project organization:</p><p><strong>src/</strong></p><ul><li><strong>Presentation</strong> -&gt; <code>CampusHub.Api</code></li><li><strong>Application</strong> -&gt; <code>CampusHub.Application</code></li><li><strong>Domain</strong> -&gt; <code>CampusHub.Domain</code></li><li><strong>Infrastructure</strong> -&gt; <code>CampusHub.Infrastructure</code></li></ul><p>Let&rsquo;s run the .NET commands to create the projects. In a terminal, execute the command below:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new sln -n CampusHub
</code></pre><p>This will create a new solution (CampusHub.sln). Then, to create the projects within the src directory, run the following command:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new webapi -n CampusHub.Api -o src/CampusHub.Api
dotnet new classlib -n CampusHub.Application -o src/CampusHub.Application
dotnet new classlib -n CampusHub.Domain -o src/CampusHub.Domain
dotnet new classlib -n CampusHub.Infrastructure -o src/CampusHub.Infrastructure
</code></pre><p>Now, to add the projects to the solution, run the following command:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet sln add src/CampusHub.Api/CampusHub.Api.csproj
dotnet sln add src/CampusHub.Application/CampusHub.Application.csproj
dotnet sln add src/CampusHub.Domain/CampusHub.Domain.csproj
dotnet sln add src/CampusHub.Infrastructure/CampusHub.Infrastructure.csproj
</code></pre><p>Finally, run the following commands to add the dependencies between the projects:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet add src/CampusHub.Application reference src/CampusHub.Domain
dotnet add src/CampusHub.Infrastructure reference src/CampusHub.Domain
dotnet add src/CampusHub.Infrastructure reference src/CampusHub.Application
dotnet add src/CampusHub.Api reference src/CampusHub.Application
dotnet add src/CampusHub.Api reference src/CampusHub.Infrastructure
</code></pre><p>Then, in the CampusHub.Infrastructure.cs add the following NuGet packages:</p><pre class=" language-csharp"><code class="prism  language-csharp">
  <span class="token operator">&lt;</span>ItemGroup<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Microsoft.EntityFrameworkCore"</span> Version<span class="token operator">=</span><span class="token string">"8.0.0"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Microsoft.EntityFrameworkCore.Relational"</span> Version<span class="token operator">=</span><span class="token string">"8.0.0"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Microsoft.EntityFrameworkCore.Tools"</span> Version<span class="token operator">=</span><span class="token string">"8.0.0"</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>PrivateAssets<span class="token operator">&gt;</span>all<span class="token operator">&lt;</span><span class="token operator">/</span>PrivateAssets<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>IncludeAssets<span class="token operator">&gt;</span>runtime<span class="token punctuation">;</span> build<span class="token punctuation">;</span> native<span class="token punctuation">;</span> contentfiles<span class="token punctuation">;</span> analyzers<span class="token punctuation">;</span> buildtransitive<span class="token operator">&lt;</span><span class="token operator">/</span>IncludeAssets<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>PackageReference<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Pomelo.EntityFrameworkCore.MySql"</span> Version<span class="token operator">=</span><span class="token string">"8.0.0"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>PackageReference Include<span class="token operator">=</span><span class="token string">"Microsoft.EntityFrameworkCore.Design"</span> Version<span class="token operator">=</span><span class="token string">"8.0.0"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
  <span class="token operator">&lt;</span><span class="token operator">/</span>ItemGroup<span class="token operator">&gt;</span>
</code></pre><h3 id="domain-layer">Domain Layer</h3><p>In the domain layer, we will place the project&rsquo;s entities and enums. Therefore, we will have a class to represent the Course entity and an enum to represent the course statuses. So, within the <code>CampusHub.Domain</code> project, create a folder called <code>Entities</code> and add the following class inside it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Enums<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Course</span>
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">const</span> <span class="token keyword">int</span> MinNameLength <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">const</span> <span class="token keyword">int</span> MaxNameLength <span class="token operator">=</span> <span class="token number">200</span><span class="token punctuation">;</span>
    <span class="token keyword">private</span> <span class="token keyword">const</span> <span class="token keyword">int</span> MinWorkload <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span>

    <span class="token keyword">public</span> Guid Id <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> Code <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 keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Name <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 keyword">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> WorkloadHours <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">int</span> MaxStudents <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> CourseStatus Status <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 comment">// EF Core requirement</span>
    <span class="token keyword">private</span> <span class="token function">Course</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 function">Course</span><span class="token punctuation">(</span><span class="token keyword">string</span> code<span class="token punctuation">,</span> <span class="token keyword">string</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> workloadHours<span class="token punctuation">,</span> <span class="token keyword">int</span> maxStudents<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        Id <span class="token operator">=</span> Guid<span class="token punctuation">.</span><span class="token function">NewGuid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">SetCode</span><span class="token punctuation">(</span>code<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">SetName</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">SetWorkload</span><span class="token punctuation">(</span>workloadHours<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">SetMaxStudents</span><span class="token punctuation">(</span>maxStudents<span class="token punctuation">)</span><span class="token punctuation">;</span>

        Status <span class="token operator">=</span> CourseStatus<span class="token punctuation">.</span>Draft<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">static</span> Course <span class="token function">Create</span><span class="token punctuation">(</span><span class="token keyword">string</span> code<span class="token punctuation">,</span> <span class="token keyword">string</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> workloadHours<span class="token punctuation">,</span> <span class="token keyword">int</span> maxStudents<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Course</span><span class="token punctuation">(</span>code<span class="token punctuation">,</span> name<span class="token punctuation">,</span> workloadHours<span class="token punctuation">,</span> maxStudents<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token comment">// Behavior methods</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">UpdateDetails</span><span class="token punctuation">(</span><span class="token keyword">string</span> name<span class="token punctuation">,</span> <span class="token keyword">int</span> workloadHours<span class="token punctuation">,</span> <span class="token keyword">int</span> maxStudents<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token function">EnsureNotArchived</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token function">SetName</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">SetWorkload</span><span class="token punctuation">(</span>workloadHours<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">SetMaxStudents</span><span class="token punctuation">(</span>maxStudents<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">ChangeCapacity</span><span class="token punctuation">(</span><span class="token keyword">int</span> maxStudents<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token function">EnsureNotArchived</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token function">SetMaxStudents</span><span class="token punctuation">(</span>maxStudents<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">Activate</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>Status <span class="token operator">==</span> CourseStatus<span class="token punctuation">.</span>Active<span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"Course is already active."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Status <span class="token operator">=</span> CourseStatus<span class="token punctuation">.</span>Active<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">Archive</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>Status <span class="token operator">==</span> CourseStatus<span class="token punctuation">.</span>Archived<span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"Course is already archived."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Status <span class="token operator">=</span> CourseStatus<span class="token punctuation">.</span>Archived<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>


    <span class="token comment">// Private validation logic</span>
    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">SetCode</span><span class="token punctuation">(</span><span class="token keyword">string</span> code<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">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>code<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentException</span><span class="token punctuation">(</span><span class="token string">"Course code cannot be empty."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Code <span class="token operator">=</span> code<span class="token punctuation">.</span><span class="token function">Trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToUpper</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">SetName</span><span class="token punctuation">(</span><span class="token keyword">string</span> name<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">string</span><span class="token punctuation">.</span><span class="token function">IsNullOrWhiteSpace</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentException</span><span class="token punctuation">(</span><span class="token string">"Course name cannot be empty."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">if</span> <span class="token punctuation">(</span>name<span class="token punctuation">.</span>Length <span class="token operator">&lt;</span> MinNameLength <span class="token operator">||</span> name<span class="token punctuation">.</span>Length <span class="token operator">&gt;</span> MaxNameLength<span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentException</span><span class="token punctuation">(</span>$<span class="token string">"Course name must be between {MinNameLength} and {MaxNameLength} characters."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Name <span class="token operator">=</span> name<span class="token punctuation">.</span><span class="token function">Trim</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">SetWorkload</span><span class="token punctuation">(</span><span class="token keyword">int</span> workloadHours<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>workloadHours <span class="token operator">&lt;</span> MinWorkload<span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentException</span><span class="token punctuation">(</span><span class="token string">"Workload must be greater than zero."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        WorkloadHours <span class="token operator">=</span> workloadHours<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">SetMaxStudents</span><span class="token punctuation">(</span><span class="token keyword">int</span> maxStudents<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>maxStudents <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">ArgumentException</span><span class="token punctuation">(</span><span class="token string">"Max students must be greater than zero."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        MaxStudents <span class="token operator">=</span> maxStudents<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">EnsureNotArchived</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>Status <span class="token operator">==</span> CourseStatus<span class="token punctuation">.</span>Archived<span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"Archived courses cannot be modified."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Note that this entity has an excellent structure. It is not anemic, meaning it has behaviors such as the <code>Activate()</code>, <code>Archive()</code> and <code>UpdateDetails()</code> methods.</p><p>Furthermore, you can never create an invalid Course because the verification methods protect against creating an invalid state. For example, an archived course cannot be modified, and an active course cannot be reactivated.</p><p>Now, create a new folder called <code>Enums</code> and add the following enum to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Enums<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">enum</span> CourseStatus
<span class="token punctuation">{</span>
    Draft <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">,</span>
    Active <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span>
    Archived <span class="token operator">=</span> <span class="token number">2</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="application-layer">Application Layer</h3><p>The application layer is where we will configure the service classes with CRUD methods and Data Transfer Objects (DTOs). So, within the CampusHub.Application project, create a new folder called <code>DTOs</code> and create the following records inside it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">CourseResponseDto</span><span class="token punctuation">(</span>
    Guid Id<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Code<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Name<span class="token punctuation">,</span>
    <span class="token keyword">int</span> WorkloadHours<span class="token punctuation">,</span>
    <span class="token keyword">int</span> MaxStudents<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Status
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">CreateCourseDto</span><span class="token punctuation">(</span>
    <span class="token keyword">string</span> Code<span class="token punctuation">,</span>
    <span class="token keyword">string</span> Name<span class="token punctuation">,</span>
    <span class="token keyword">int</span> WorkloadHours<span class="token punctuation">,</span>
    <span class="token keyword">int</span> MaxStudents
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">UpdateCourseDto</span><span class="token punctuation">(</span>
    <span class="token keyword">string</span> Name<span class="token punctuation">,</span>
    <span class="token keyword">int</span> WorkloadHours<span class="token punctuation">,</span>
    <span class="token keyword">int</span> MaxStudents
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Then, create a new folder called <code>Interfaces</code> and add the following interfaces to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">ICourseRepository</span>
<span class="token punctuation">{</span>
    Task <span class="token function">AddAsync</span><span class="token punctuation">(</span>Course course<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task<span class="token operator">&lt;</span>Course<span class="token operator">?</span><span class="token operator">&gt;</span> <span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>Course<span class="token operator">&gt;</span><span class="token operator">&gt;</span> <span class="token function">GetAllAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task <span class="token function">Update</span><span class="token punctuation">(</span>Course course<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task<span class="token operator">&lt;</span><span class="token keyword">bool</span><span class="token operator">&gt;</span> <span class="token function">ExistsByCodeAsync</span><span class="token punctuation">(</span><span class="token keyword">string</span> code<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">ICourseService</span>
<span class="token punctuation">{</span>
    Task<span class="token operator">&lt;</span>Guid<span class="token operator">&gt;</span> <span class="token function">CreateAsync</span><span class="token punctuation">(</span>CreateCourseDto dto<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task <span class="token function">UpdateAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">,</span> UpdateCourseDto dto<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task <span class="token function">ActivateAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task <span class="token function">ArchiveAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task<span class="token operator">&lt;</span>CourseResponseDto<span class="token operator">?</span><span class="token operator">&gt;</span> <span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span><span class="token punctuation">;</span>
    Task<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>CourseResponseDto<span class="token operator">&gt;</span><span class="token operator">&gt;</span> <span class="token function">GetAllAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Finally, create a new folder called <code>Services</code> and add the class below to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Services<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CourseService</span> <span class="token punctuation">:</span> ICourseService
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> ICourseRepository _repository<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">CourseService</span><span class="token punctuation">(</span>ICourseRepository repository<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _repository <span class="token operator">=</span> repository<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>Guid<span class="token operator">&gt;</span> <span class="token function">CreateAsync</span><span class="token punctuation">(</span>CreateCourseDto dto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> exists <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">ExistsByCodeAsync</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Code<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>exists<span class="token punctuation">)</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"Course code already exists."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">var</span> course <span class="token operator">=</span> Course<span class="token punctuation">.</span><span class="token function">Create</span><span class="token punctuation">(</span>
            dto<span class="token punctuation">.</span>Code<span class="token punctuation">,</span>
            dto<span class="token punctuation">.</span>Name<span class="token punctuation">,</span>
            dto<span class="token punctuation">.</span>WorkloadHours<span class="token punctuation">,</span>
            dto<span class="token punctuation">.</span>MaxStudents
        <span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">AddAsync</span><span class="token punctuation">(</span>course<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> course<span class="token punctuation">.</span>Id<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">UpdateAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">,</span> UpdateCourseDto dto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> course <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>
            <span class="token operator">?</span><span class="token operator">?</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"Course not found."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        course<span class="token punctuation">.</span><span class="token function">UpdateDetails</span><span class="token punctuation">(</span>dto<span class="token punctuation">.</span>Name<span class="token punctuation">,</span> dto<span class="token punctuation">.</span>WorkloadHours<span class="token punctuation">,</span> dto<span class="token punctuation">.</span>MaxStudents<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">Update</span><span class="token punctuation">(</span>course<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">async</span> Task <span class="token function">ActivateAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> course <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>
            <span class="token operator">?</span><span class="token operator">?</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"Course not found."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        course<span class="token punctuation">.</span><span class="token function">Activate</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">Update</span><span class="token punctuation">(</span>course<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">async</span> Task <span class="token function">ArchiveAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> course <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span>
            <span class="token operator">?</span><span class="token operator">?</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">InvalidOperationException</span><span class="token punctuation">(</span><span class="token string">"Course not found."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        course<span class="token punctuation">.</span><span class="token function">Archive</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">Update</span><span class="token punctuation">(</span>course<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">async</span> Task<span class="token operator">&lt;</span>CourseResponseDto<span class="token operator">?</span><span class="token operator">&gt;</span> <span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> course <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">GetByIdAsync</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>course <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">MapToResponse</span><span class="token punctuation">(</span>course<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">async</span> Task<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>CourseResponseDto<span class="token operator">&gt;</span><span class="token operator">&gt;</span> <span class="token function">GetAllAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> courses <span class="token operator">=</span> <span class="token keyword">await</span> _repository<span class="token punctuation">.</span><span class="token function">GetAllAsync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> courses<span class="token punctuation">.</span><span class="token function">Select</span><span class="token punctuation">(</span>MapToResponse<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 punctuation">}</span>

    <span class="token keyword">private</span> <span class="token keyword">static</span> CourseResponseDto <span class="token function">MapToResponse</span><span class="token punctuation">(</span>Course course<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">CourseResponseDto</span><span class="token punctuation">(</span>
            course<span class="token punctuation">.</span>Id<span class="token punctuation">,</span>
            course<span class="token punctuation">.</span>Code<span class="token punctuation">,</span>
            course<span class="token punctuation">.</span>Name<span class="token punctuation">,</span>
            course<span class="token punctuation">.</span>WorkloadHours<span class="token punctuation">,</span>
            course<span class="token punctuation">.</span>MaxStudents<span class="token punctuation">,</span>
            course<span class="token punctuation">.</span>Status<span class="token punctuation">.</span><span class="token function">ToString</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>Here we have all the methods we need to execute the CRUD functions. Note that to modify the state of the objects we use the methods created in the domain class.</p><h3 id="infrastructure-layer">Infrastructure Layer</h3><p>Now let&rsquo;s create the infrastructure layer, used for communication with external parts of the application such as external APIs and databases. Within the CampusHub.Infrastructure project, create a new folder called <code>Data</code> and add the following classes to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>src<span class="token punctuation">.</span>CampusHub<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">AppDbContext</span> <span class="token punctuation">:</span> DbContext
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> DbSet<span class="token operator">&lt;</span>Course<span class="token operator">&gt;</span> Courses <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token generic-method function">Set<span class="token punctuation">&lt;</span>Course<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">public</span> <span class="token function">AppDbContext</span><span class="token punctuation">(</span>DbContextOptions<span class="token operator">&lt;</span>AppDbContext<span class="token operator">&gt;</span> options<span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token keyword">base</span><span class="token punctuation">(</span>options<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">void</span> <span class="token function">OnModelCreating</span><span class="token punctuation">(</span>ModelBuilder modelBuilder<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        modelBuilder<span class="token punctuation">.</span><span class="token function">ApplyConfigurationsFromAssembly</span><span class="token punctuation">(</span><span class="token keyword">typeof</span><span class="token punctuation">(</span>AppDbContext<span class="token punctuation">)</span><span class="token punctuation">.</span>Assembly<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">base</span><span class="token punctuation">.</span><span class="token function">OnModelCreating</span><span class="token punctuation">(</span>modelBuilder<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">.</span>Metadata<span class="token punctuation">.</span>Builders<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>src<span class="token punctuation">.</span>CampusHub<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CourseConfiguration</span> <span class="token punctuation">:</span> IEntityTypeConfiguration<span class="token operator">&lt;</span>Course<span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">Configure</span><span class="token punctuation">(</span>EntityTypeBuilder<span class="token operator">&lt;</span>Course<span class="token operator">&gt;</span> builder<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        builder<span class="token punctuation">.</span><span class="token function">ToTable</span><span class="token punctuation">(</span><span class="token string">"Courses"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        builder<span class="token punctuation">.</span><span class="token function">HasKey</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Id<span class="token punctuation">)</span><span class="token punctuation">;</span>

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Code<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">IsRequired</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">HasMaxLength</span><span class="token punctuation">(</span><span class="token number">20</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        builder<span class="token punctuation">.</span><span class="token function">HasIndex</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Code<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">IsUnique</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Name<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">IsRequired</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">HasMaxLength</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>WorkloadHours<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">IsRequired</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>MaxStudents<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">IsRequired</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Status<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">IsRequired</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token generic-method function">HasConversion<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 punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Enum -&gt; int</span>

        builder<span class="token punctuation">.</span><span class="token function">Property</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Id<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">ValueGeneratedNever</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>Here we made the necessary configurations for the EF Core implementation, defining database details such as the table name, maximum number of characters for the Name property and others.</p><p>Next, create a new folder called <code>Repositories</code> and add the following class to it:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Domain<span class="token punctuation">.</span>Entities<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>src<span class="token punctuation">.</span>CampusHub<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Repositories<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CourseRepository</span> <span class="token punctuation">:</span> ICourseRepository
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> AppDbContext _context<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">CourseRepository</span><span class="token punctuation">(</span>AppDbContext context<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _context <span class="token operator">=</span> context<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">public</span> <span class="token keyword">async</span> Task <span class="token function">AddAsync</span><span class="token punctuation">(</span>Course course<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> _context<span class="token punctuation">.</span>Courses<span class="token punctuation">.</span><span class="token function">AddAsync</span><span class="token punctuation">(</span>course<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> _context<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</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> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>Course<span class="token operator">?</span><span class="token operator">&gt;</span> <span class="token function">GetByIdAsync</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">return</span> <span class="token keyword">await</span> _context<span class="token punctuation">.</span>Courses<span class="token punctuation">.</span><span class="token function">FirstOrDefaultAsync</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Id <span class="token operator">==</span> id<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">async</span> Task<span class="token operator">&lt;</span>List<span class="token operator">&lt;</span>Course<span class="token operator">&gt;</span><span class="token operator">&gt;</span> <span class="token function">GetAllAsync</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">await</span> _context<span class="token punctuation">.</span>Courses<span class="token punctuation">.</span><span class="token function">ToListAsync</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> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span><span class="token keyword">bool</span><span class="token operator">&gt;</span> <span class="token function">ExistsByCodeAsync</span><span class="token punctuation">(</span><span class="token keyword">string</span> code<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> normalizedCode <span class="token operator">=</span> code<span class="token punctuation">.</span><span class="token function">Trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">ToUpper</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">await</span> _context<span class="token punctuation">.</span>Courses
            <span class="token punctuation">.</span><span class="token function">AnyAsync</span><span class="token punctuation">(</span>c <span class="token operator">=</span><span class="token operator">&gt;</span> c<span class="token punctuation">.</span>Code <span class="token operator">==</span> normalizedCode<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">async</span> Task <span class="token function">Update</span><span class="token punctuation">(</span>Course course<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _context<span class="token punctuation">.</span>Courses<span class="token punctuation">.</span><span class="token function">Update</span><span class="token punctuation">(</span>course<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> _context<span class="token punctuation">.</span><span class="token function">SaveChangesAsync</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 repository class, we create the implementation of the CRUD methods that execute the operations on the database.</p><p>The last class in the infrastructure layer will be used to configure the dependency injection and define the connection string. So, inside the project add the class below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Repositories<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>src<span class="token punctuation">.</span>CampusHub<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">.</span>Data<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>Extensions<span class="token punctuation">.</span>Configuration<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>Extensions<span class="token punctuation">.</span>DependencyInjection<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">DependencyInjection</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> IServiceCollection <span class="token function">AddInfrastructure</span><span class="token punctuation">(</span><span class="token keyword">this</span> IServiceCollection services<span class="token punctuation">,</span> IConfiguration configuration<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        services<span class="token punctuation">.</span><span class="token generic-method function">AddDbContext<span class="token punctuation">&lt;</span>AppDbContext<span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span>options <span class="token operator">=</span><span class="token operator">&gt;</span>
        options<span class="token punctuation">.</span><span class="token function">UseMySql</span><span class="token punctuation">(</span>
            configuration<span class="token punctuation">.</span><span class="token function">GetConnectionString</span><span class="token punctuation">(</span><span class="token string">"DefaultConnection"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
            ServerVersion<span class="token punctuation">.</span><span class="token function">AutoDetect</span><span class="token punctuation">(</span>configuration<span class="token punctuation">.</span><span class="token function">GetConnectionString</span><span class="token punctuation">(</span><span class="token string">"DefaultConnection"</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>

        services<span class="token punctuation">.</span><span class="token generic-method function">AddScoped<span class="token punctuation">&lt;</span>ICourseRepository<span class="token punctuation">,</span> CourseRepository<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">return</span> services<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="api-layer">API Layer</h3><p>The API is the final layer of the application, here we will define the controller classes and other settings such as the database connection string. Inside the Controllers folder, add the following controller:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>DTOs<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>
<span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>AspNetCore<span class="token punctuation">.</span>Mvc<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> CampusHub<span class="token punctuation">.</span>Api<span class="token punctuation">.</span>Controllers<span class="token punctuation">;</span>

<span class="token punctuation">[</span>ApiController<span class="token punctuation">]</span>
<span class="token punctuation">[</span><span class="token function">Route</span><span class="token punctuation">(</span><span class="token string">"api/[controller]"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CoursesController</span> <span class="token punctuation">:</span> ControllerBase
<span class="token punctuation">{</span>
    <span class="token keyword">private</span> <span class="token keyword">readonly</span> ICourseService _service<span class="token punctuation">;</span>

    <span class="token keyword">public</span> <span class="token function">CoursesController</span><span class="token punctuation">(</span>ICourseService service<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        _service <span class="token operator">=</span> service<span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token punctuation">[</span>HttpPost<span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IActionResult<span class="token operator">&gt;</span> <span class="token function">Create</span><span class="token punctuation">(</span>CreateCourseDto dto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> id <span class="token operator">=</span> <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">CreateAsync</span><span class="token punctuation">(</span>dto<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">CreatedAtAction</span><span class="token punctuation">(</span><span class="token function">nameof</span><span class="token punctuation">(</span>GetById<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token punctuation">{</span> id <span class="token punctuation">}</span><span class="token punctuation">,</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 punctuation">[</span><span class="token function">HttpGet</span><span class="token punctuation">(</span><span class="token string">"{id:guid}"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IActionResult<span class="token operator">&gt;</span> <span class="token function">GetById</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> course <span class="token operator">=</span> <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">GetByIdAsync</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>course <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
            <span class="token keyword">return</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 keyword">return</span> <span class="token function">Ok</span><span class="token punctuation">(</span>course<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token punctuation">[</span>HttpGet<span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IActionResult<span class="token operator">&gt;</span> <span class="token function">GetAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> courses <span class="token operator">=</span> <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">GetAllAsync</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 function">Ok</span><span class="token punctuation">(</span>courses<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token punctuation">[</span><span class="token function">HttpPut</span><span class="token punctuation">(</span><span class="token string">"{id:guid}"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IActionResult<span class="token operator">&gt;</span> <span class="token function">Update</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">,</span> UpdateCourseDto dto<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">UpdateAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">,</span> dto<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">NoContent</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 function">HttpPost</span><span class="token punctuation">(</span><span class="token string">"{id:guid}/activate"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IActionResult<span class="token operator">&gt;</span> <span class="token function">Activate</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">ActivateAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">NoContent</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 function">HttpPost</span><span class="token punctuation">(</span><span class="token string">"{id:guid}/archive"</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
    <span class="token keyword">public</span> <span class="token keyword">async</span> Task<span class="token operator">&lt;</span>IActionResult<span class="token operator">&gt;</span> <span class="token function">Archive</span><span class="token punctuation">(</span>Guid id<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">await</span> _service<span class="token punctuation">.</span><span class="token function">ArchiveAsync</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">return</span> <span class="token function">NoContent</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>Then, in the Program class, replace the existing code with the code below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Services<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Application<span class="token punctuation">.</span>Interfaces<span class="token punctuation">;</span>
<span class="token keyword">using</span> CampusHub<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">;</span>

<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 comment">// Services</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddControllers</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">AddEndpointsApiExplorer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Application</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>ICourseService<span class="token punctuation">,</span> CourseService<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 comment">// Infrastructure</span>

builder<span class="token punctuation">.</span>Services<span class="token punctuation">.</span><span class="token function">AddInfrastructure</span><span class="token punctuation">(</span>builder<span class="token punctuation">.</span>Configuration<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// Build</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 comment">// Middleware</span>

app<span class="token punctuation">.</span><span class="token function">UseHttpsRedirection</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">UseAuthorization</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">MapControllers</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>Finally, in the file appsettings.json, add the following configuration. (Don&rsquo;t forget to add your credentials!):</p><pre class=" language-json"><code class="prism  language-json"> <span class="token string">"ConnectionStrings"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">"DefaultConnection"</span><span class="token punctuation">:</span> <span class="token string">"server=localhost;port=3306;database=campushub;user=YOUR_USER;password=YOUR_PASSWORD;"</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">,</span>
</code></pre><h2 id="running-ef-core-commands">Running EF Core Commands</h2><p>To apply the migration commands, in the project root, open a new terminal and run the commands below.</p><p><strong>Creating the Migration Files</strong></p><pre class=" language-bash"><code class="prism  language-bash">dotnet ef migrations add InitialCreate --project src/CampusHub.Infrastructure --startup-project src/CampusHub.Api --output-dir Data/Migrations
</code></pre><p><strong>Applying the Migration Commands</strong></p><pre class=" language-bash"><code class="prism  language-bash">dotnet ef database update --project src/CampusHub.Infrastructure --startup-project src/CampusHub.Api
</code></pre><h2 id="what-does-the-final-structure-look-like">What Does the Final Structure Look Like?</h2><p>After implementing all the steps above, the project will have the following structure:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/final-project-structure.png?sfvrsn=5ab25a90_2" title="final project structure" alt="Final project structure" /></p><h2 id="-when-does-a-complete-crud-not-pay-off"> When Does a Complete CRUD Not Pay Off?</h2><p>In this post, we created a complete CRUD, with all the necessary elements for its evolution. However, this complexity can exponentially increase development time and the level of knowledge required to maintain the project.</p><p>With this in mind, in some scenarios, simplicity is preferable. For example, when the system basically stores and retrieves data without relevant business rules. In scenarios such as basic registrations, auxiliary tables or internal administrative panels, adding layers such as services, rich entities and DDD patterns tends to generate more complexity than value, making the code more difficult to maintain without a justified need.</p><p>It is also not worthwhile to invest in a sophisticated architecture in the initial phases of a product, such as MVPs or systems still in validation. In these cases, the priority is speed and adaptation to frequent changes, and a simple structure allows for faster evolution without the burden of unnecessary abstractions.</p><p>On the other hand, a more elaborate CRUD makes sense when the domain has important rules, states, complex validations or when errors can have a significant impact. In these scenarios, a more structured architecture, as demonstrated in the post, helps protect the domain, maintain consistency and facilitate the system&rsquo;s evolution over time (all parts are in their proper places).</p><p>The final decision of whether or not to use a complete and evolve-ready CRUD should therefore always take into account the level of complexity of the problem.</p><h2 id="conclusion-and-next-steps">Conclusion and Next Steps</h2><p>Knowing the best approach when creating new web applications is always a challenge, but we must keep in mind that if the project requires complex business rules, validations and errors that can have a significant impact, this is a strong indication that the application should not be a simple and generic CRUD.</p><p>In this post, we learned how to create a complete CRUD, with each layer representing a part of Clean Architecture, and using DDD development principles to implement a well-structured domain with private properties and behaviors.</p><p>But far beyond the basic structure presented in this post, a complete CRUD also involves unit tests, complex validations (FluentValidation), authentication and authorization with modern methods (JW Tokens for example), and many other elements. I hope this post serves as a starting point and helps you create CRUD applications that are not only ready to function, but also to evolve with quality over time.</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">Best Practices for Exceptions in ASP.NET Core</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Exceptions are a common approach to dealing with unexpected situations. But are they truly necessary? Let&rsquo;s see some <a target="_blank" href="https://www.telerik.com/blogs/best-practices-exceptions-aspnet-core"> best practices for using exceptions in ASP.NET Core</a>.</p></div></div></aside><img src="https://feeds.telerik.com/link/10827/17403822.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:87e533de-18c4-4413-bfa3-86b39bf91ec3</id>
    <title type="text">Simplify Hierarchical Selection in Blazor with the Telerik DropDownTree</title>
    <summary type="text">The Blazor DropDownTree allows you to show hierarchy for data like product searches or organization and department structures down to individual employees, with options for expansion and collapse and breadcrumb displays.</summary>
    <published>2026-08-04T15:48:07Z</published>
    <updated>2026-09-10T05:05:50Z</updated>
    <author>
      <name>Ivan Danchev </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17403342/simplify-hierarchical-selection-blazor-telerik-dropdowntree"/>
    <content type="text"><![CDATA[<p><span class="featured">The Blazor DropDownTree allows you to show hierarchy for data like product searches or organization and department structures down to individual employees, with options for expansion and collapse and breadcrumb displays.</span></p><p>Hierarchical data is everywhere. Product catalogs, organizational structures, file systems, geographical regions and permission models all rely on parent-child relationships to help users understand and navigate information.</p><p>The challenge appears when users need to select something from that hierarchy.</p><p>A traditional dropdown works well for flat lists, but once the number of items grows, users lose important context. A product category called &ldquo;Gaming Laptops&rdquo; means much more when users can see that it belongs under &ldquo;Electronics &rarr; Computers &rarr; Laptops.&rdquo;</p><p>A TreeView solves the navigation problem, but it is not always the right fit inside a form. Keeping a large hierarchy permanently visible can consume valuable screen space.</p><p>This is where the Progress Telerik UI for <a target="_blank" href="https://www.telerik.com/blazor-ui/dropdowntree">Blazor DropDownTree</a> comes in.</p><p>The DropDownTree combines the compact experience of a dropdown with the navigation capabilities of a <a target="_blank" href="https://www.telerik.com/blazor-ui/treeview">TreeView</a>, allowing users to browse, search and select hierarchical data without overwhelming the UI.</p><p>In this article, we will explore how to use the DropDownTree to:</p><ul><li>Display hierarchical data</li><li>Help users quickly find items</li><li>Make working with hierarchical data more intuitive</li><li>Customize the appearance of nodes</li><li>Customize the selected value display</li></ul><h2 id="why-choose-a-dropdowntree">Why Choose a DropDownTree?</h2><p>When working with hierarchical data, choosing the right component can make a significant difference in the user experience.</p><p>A standard dropdown or <a target="_blank" href="https://www.telerik.com/blazor-ui/combobox">ComboBox</a> is ideal for flat data, but it does not provide context about how items relate to each other. A TreeView preserves the hierarchy, but it is always visible and may not fit well inside forms where space is limited.</p><p>The DropDownTree combines the best of both approaches.</p><table><style>table,
 th,
        td {
            border: 1px;
            border-color: #bdbdba;
            border-style: dotted;
            border-collapse: collapse;
            margin-right: auto;
            padding: 0in 5.4pt 0in 5.4pt;
            text-align: left;
        }
    </style>
 <thead><tr><th>Component</th><th>Best For</th></tr></thead><tbody><tr><td>DropDownList</td><td>Selecting from a small flat list</td></tr><tr><td>ComboBox</td><td>Searching and selecting from large flat datasets</td></tr><tr><td>TreeView</td><td>Browsing and managing visible hierarchies</td></tr><tr><td>DropDownTree</td><td>Selecting items from hierarchical data in a compact UI</td></tr></tbody></table><br /><p>If your users need to select something from a hierarchy but don&rsquo;t need the hierarchy visible all the time, the DropDownTree is often the right choice.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/compare-dropdown-treview-dropdowntree.png?sfvrsn=a7309ec7_2" alt="Side-by-side comparison: DropDownList with flat categories, TreeView with expanded categories, DropDownTree combining dropdown + hierarchy" /></p><h2 id="how-can-you-display-hierarchical-data">How Can You Display Hierarchical Data?</h2><p>Let&rsquo;s start with a common scenario: a product catalog.</p><p>Imagine an inventory application with categories like:</p><pre><code>Electronics
&nbsp;&nbsp;Computers
&nbsp;&nbsp;&nbsp;&nbsp;Laptops
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Gaming
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Business
&nbsp;&nbsp;&nbsp;&nbsp;Phones
Furniture
&nbsp;&nbsp;Bedroom
&nbsp;&nbsp;Living Room
</code></pre><p>Displaying this structure in a flat dropdown would quickly become difficult to use. The DropDownTree allows you to bind directly to hierarchical data, including data that comes from a database through a service.</p><h2 id="the-data-model">The Data Model</h2><p>The component works with a recursive DTO where each node holds its own children:</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">HierarchicalTreeItemDto</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> TreeItemId <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">string</span> Text <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">bool</span> HasChildren <span class="token operator">=</span><span class="token operator">&gt;</span> Items<span class="token operator">?</span><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 keyword">public</span> List<span class="token operator">&lt;</span>HierarchicalTreeItemDto<span class="token operator">&gt;</span> Items <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 punctuation">}</span>
</code></pre><p><code>HasChildren</code> is computed from <code>Items.Count</code>, so it never goes out of sync with the actual data.</p><p><strong>The Component and Binding</strong></p><pre class=" language-csharp"><code class="prism  language-csharp">@inject HierarchicalTreeItemService HierarchicalTreeItemService

<span class="token operator">&lt;</span>TelerikDropDownTree Data<span class="token operator">=</span><span class="token string">"@DropDownTreeData"</span>
                     @bind<span class="token operator">-</span>Value<span class="token operator">=</span><span class="token string">"@DropDownTreeValue"</span>
                     @bind<span class="token operator">-</span>ExpandedItems<span class="token operator">=</span><span class="token string">"@DropDownTreeExpandedItems"</span>
                     ValueField<span class="token operator">=</span><span class="token string">"TreeItemId"</span>
                     Filterable<span class="token operator">=</span><span class="token string">"true"</span>
                     FilterOperator<span class="token operator">=</span><span class="token string">"@StringFilterOperator.Contains"</span>
                     FilterPlaceholder<span class="token operator">=</span><span class="token string">"Search by name"</span><span class="token operator">&gt;</span>

    <span class="token operator">&lt;</span>DropDownTreeBindings<span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>DropDownTreeBinding TextField<span class="token operator">=</span><span class="token string">"Text"</span> ItemsField<span class="token operator">=</span><span class="token string">"Items"</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>DropDownTreeBindings<span class="token operator">&gt;</span>

<span class="token operator">&lt;</span><span class="token operator">/</span>TelerikDropDownTree<span class="token operator">&gt;</span>

@code <span class="token punctuation">{</span>
    <span class="token keyword">private</span> List<span class="token operator">&lt;</span>HierarchicalTreeItemDto<span class="token operator">&gt;</span> DropDownTreeData <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> <span class="token keyword">int</span> DropDownTreeValue <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> IEnumerable<span class="token operator">&lt;</span><span class="token keyword">object</span><span class="token operator">&gt;</span> DropDownTreeExpandedItems <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 class-name">List</span><span class="token operator">&lt;</span><span class="token keyword">object</span><span class="token operator">&gt;</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">void</span> <span class="token function">OnInitialized</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        DropDownTreeData <span class="token operator">=</span> HierarchicalTreeItemService<span class="token punctuation">.</span><span class="token function">GetHierarchicalTreeItems</span><span class="token punctuation">(</span><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>
        DropDownTreeExpandedItems <span class="token operator">=</span> DropDownTreeData<span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>HasChildren<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token generic-method function">ToList<span class="token punctuation">&lt;</span><span class="token keyword">object</span><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>
</code></pre><ul><li><code>Data</code> &ndash; The root-level collection. The component recursively reads <code>ItemsField</code> to build the full tree; it never calls the service itself.</li><li><code>@bind-Value</code> &ndash; Two-way binding for the selected node&rsquo;s key. Updates <code>DropDownTreeValue</code> with the chosen <code>TreeItemId</code>.</li><li><code>@bind-ExpandedItems</code> &ndash; Controls which nodes are open. Prepopulating it with the root nodes that have children means users see the top-level structure the moment they open the dropdown.</li><li><code>DropDownTreeBinding</code> &ndash; Tells the component which property is the display label (<code>TextField</code>) and which holds the children (<code>ItemsField</code>). These match the DTO property names exactly. </li></ul><p><code>GetHierarchicalTreeItems()</code> is where your data-access logic lives. It queries the database and maps the results into the <code>HierarchicalTreeItemDto</code> hierarchy. The component receives a ready-made tree; it has no knowledge of where the data came from.</p><p>An exemplary Product category hierarchy displayed inside the DropDownTree:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/product-hierarchy-dropdowntree.png?sfvrsn=c40920c8_2" alt="Electronics – Computers – Laptops – Gaming – Razer Blade 18" /></p><blockquote><p><strong> Tip:</strong> If your application already uses hierarchical DTOs, you can often bind them directly without flattening or restructuring your data.</p></blockquote><h2 id="how-can-users-find-items-faster">How Can Users Find Items Faster?</h2><p>Large hierarchies can quickly become difficult to navigate manually. Imagine a catalog containing thousands of products organized across multiple category levels.</p><p>Instead of expanding nodes one by one, users can search directly. Filtering is enabled with a single parameter:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikDropDownTree</span> <span class="token attr-name">Data</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@CatalogData<span class="token punctuation">"</span></span>
                     <span class="token attr-name">@bind-Value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@SelectedValue<span class="token punctuation">"</span></span>
                     <span class="token attr-name">@bind-ExpandedItems</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ExpandedItems<span class="token punctuation">"</span></span>
                     <span class="token attr-name">ValueField</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(CategoryItem.Id)<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>true<span class="token punctuation">"</span></span>
                     <span class="token attr-name">FilterOperator</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@StringFilterOperator.Contains<span class="token punctuation">"</span></span>
                     <span class="token attr-name">FilterPlaceholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Search by name&hellip;<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>DropDownTreeBindings</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>DropDownTreeBinding</span> <span class="token attr-name">TextField</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(CategoryItem.Name)<span class="token punctuation">"</span></span>
                             <span class="token attr-name">ItemsField</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(CategoryItem.Children)<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>DropDownTreeBindings</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikDropDownTree</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>Setting <code>Filterable="true"</code> adds a search box at the top of the popup. <code>FilterOperator</code> controls the matching strategy. <code>Contains</code> is the most user-friendly choice. <code>FilterPlaceholder</code> sets the hint text inside the search field.</p><p>Now users can quickly locate deeply nested items. For example, searching for a specific laptop brand, e.g., &ldquo;Asus,&rdquo; can immediately reveal:</p><pre><code>&nbsp;Electronics
&nbsp;&nbsp;Computers
&nbsp;&nbsp;&nbsp;Laptops
&nbsp;&nbsp;&nbsp;&nbsp;Gaming
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Asus ROG Strix G16
</code></pre><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/search-dropdowntree.gif?sfvrsn=405a4903_2" alt="User types in Asus, and Asus ROG Strix G16 comes up, showing the breadcrumb of categories" /></p><h2 id="how-can-you-make-navigation-more-intuitive">How Can You Make Navigation More Intuitive?</h2><p>Sometimes the best experience is to show users the available structure immediately. Displaying top-level product categories expanded when the popup opens makes it easier to understand what options are available.</p><p>The DropDownTree lets you control which nodes are expanded through <code>@bind-ExpandedItems</code>. You can initialize the expanded set programmatically in <code>OnInitialized</code>:</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">// Expand all root-level categories on load</span>
    ExpandedItems <span class="token operator">=</span> CatalogData
        <span class="token punctuation">.</span><span class="token function">Where</span><span class="token punctuation">(</span>item <span class="token operator">=</span><span class="token operator">&gt;</span> item<span class="token punctuation">.</span>Children<span class="token operator">?</span><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 generic-method function">Cast<span class="token punctuation">&lt;</span><span class="token keyword">object</span><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">ToList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>You can also include buttons that let users expand or collapse all nodes at once:</p><pre class=" language-html"><code class="prism  language-html"><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>@ExpandRoots<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>
    Expand Root Categories
<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>@CollapseAll<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    Collapse All
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikButton</span><span class="token punctuation">&gt;</span></span>
private void ExpandRoots()
{
    ExpandedItems = CatalogData
        .Where(item =&gt; item.Children?.Count &gt; 0)
        .Cast<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>object</span><span class="token punctuation">&gt;</span></span>()
        .ToList();
}

private void CollapseAll()
{
    ExpandedItems = new List<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>object</span><span class="token punctuation">&gt;</span></span>();
}
</code></pre><p>This gives users immediate context while keeping the dropdown compact.</p><p>All nodes collapsed vs. expanded root nodes:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/dropdowntree-nodes-collapsed-expanded.png?sfvrsn=f3fdf335_2" alt="In the lefthand version, we see Electronics, Furniture, Sports. All indicating they are collapsed and have an accordion dropdown available. On the right, Electronics is expanded to reveal Computers and Phones, both of which have the arrow indicating they have children. Furniture shows Bedroom and Living Room, which are the final categories in that hierarchy." /></p><h2 id="how-can-you-make-hierarchies-easier-to-scan">How Can You Make Hierarchies Easier to Scan?</h2><p>Hierarchical data often contains different types of items. A department may contain employees. A folder may contain documents. A product category may contain products.</p><p>Displaying everything as plain text makes the hierarchy harder to understand.</p><p>The DropDownTree supports <code>ItemTemplate</code> inside <code>DropDownTreeBinding</code>, allowing you to fully customize how each node renders. The template receives the data item as context.</p><p>Start with an enriched data model that carries everything the template needs:</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">OrgNode</span>
<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">public</span> <span class="token keyword">string</span> Name <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">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">string</span> Role <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">string</span><span class="token punctuation">.</span>Empty<span class="token punctuation">;</span>
    <span class="token keyword">public</span> EmployeeStatus Status <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> EmployeeStatus<span class="token punctuation">.</span>Available<span class="token punctuation">;</span>
    <span class="token keyword">public</span> ISvgIcon<span class="token operator">?</span> Icon <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> List<span class="token operator">&lt;</span>OrgNode<span class="token operator">&gt;</span><span class="token operator">?</span> Children <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 keyword">public</span> <span class="token keyword">enum</span> EmployeeStatus <span class="token punctuation">{</span> Available<span class="token punctuation">,</span> Busy<span class="token punctuation">,</span> Away <span class="token punctuation">}</span>
</code></pre><p>Each department gets a contextual icon&mdash;a gear for Engineering, a dollar sign for Finance and so on. Each employee carries a job title and an availability status.</p><p>Wire up the template:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikDropDownTree</span> <span class="token attr-name">Data</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@OrgData<span class="token punctuation">"</span></span>
                     <span class="token attr-name">@bind-Value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@SelectedValue<span class="token punctuation">"</span></span>
                     <span class="token attr-name">@bind-ExpandedItems</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ExpandedItems<span class="token punctuation">"</span></span>
                     <span class="token attr-name">ValueField</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(OrgNode.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>340px<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>DropDownTreeBindings</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>DropDownTreeBinding</span> <span class="token attr-name">TextField</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(OrgNode.Name)<span class="token punctuation">"</span></span>
                             <span class="token attr-name">ItemsField</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(OrgNode.Children)<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>ItemTemplate</span><span class="token punctuation">&gt;</span></span>
                @{
                    var node = (OrgNode)context;
                    var isLeaf = !(node.Children?.Count &gt; 0);
                }
                @if (!isLeaf)
                {
                    <span class="token comment">&lt;!-- Department node: contextual icon + name --&gt;</span>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikSvgIcon</span> <span class="token attr-name">Icon</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@node.Icon<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><span class="token entity" title=" ">&amp;nbsp;</span>@node.Name<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 comment">&lt;!-- Employee node: status dot + person icon + name and role --&gt;</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>node-status node-status--@node.Status.ToString().ToLower()<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>
                    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikSvgIcon</span> <span class="token attr-name">Icon</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@SvgIcon.User<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>node-employee<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>node-employee-name<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@node.Name<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>node-employee-role<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>@node.Role<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 punctuation">&gt;</span></span>
                }
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ItemTemplate</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>DropDownTreeBinding</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>DropDownTreeBindings</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikDropDownTree</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>A few CSS rules complete the effect:</p><pre class=" language-css"><code class="prism  language-css"><span class="token selector"><span class="token class">.node-status</span> </span><span class="token punctuation">{</span>
    <span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span>
    <span class="token property">width</span><span class="token punctuation">:</span> <span class="token number">8</span>px<span class="token punctuation">;</span>
    <span class="token property">height</span><span class="token punctuation">:</span> <span class="token number">8</span>px<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">margin-right</span><span class="token punctuation">:</span> <span class="token number">5</span>px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.node-status--available</span> </span><span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token hexcode">#4caf50</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token selector"><span class="token class">.node-status--busy</span>      </span><span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token hexcode">#ff9800</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token selector"><span class="token class">.node-status--away</span>      </span><span class="token punctuation">{</span> <span class="token property">background</span><span class="token punctuation">:</span> <span class="token hexcode">#9e9e9e</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.node-employee</span> </span><span class="token punctuation">{</span>
    <span class="token property">display</span><span class="token punctuation">:</span> inline-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">line-height</span><span class="token punctuation">:</span> <span class="token number">1.2</span><span class="token punctuation">;</span>
    <span class="token property">vertical-align</span><span class="token punctuation">:</span> middle<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token selector"><span class="token class">.node-employee-name</span> </span><span class="token punctuation">{</span> <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token number">0.875</span>rem<span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token selector"><span class="token class">.node-employee-role</span> </span><span class="token punctuation">{</span> <span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token number">0.75</span>rem<span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span> <span class="token hexcode">#6c757d</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
</code></pre><p>The result: department nodes render with their domain icon, while employees show a colored availability dot, their name and their role&mdash;all inside the same compact dropdown:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/dropdowntree-department-employees.png?sfvrsn=132fa79d_2" alt="Engineering – Backend Team shows employee names, a colored availability dot and their role" /></p><h2 id="how-can-you-customize-the-selected-value-display">How Can You Customize the Selected Value Display?</h2><p>By default the DropDownTree shows the selected item&rsquo;s text in the input. With <code>ValueTemplate</code> you can replace that with any markup you like.</p><p>A useful pattern is showing the selected item together with its breadcrumb path, giving users instant context about where the selection sits in the hierarchy:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TelerikDropDownTree</span> <span class="token attr-name">Data</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@CatalogData<span class="token punctuation">"</span></span>
                     <span class="token attr-name">@bind-Value</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@SelectedValue<span class="token punctuation">"</span></span>
                     <span class="token attr-name">@bind-ExpandedItems</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@ExpandedItems<span class="token punctuation">"</span></span>
                     <span class="token attr-name">ValueField</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(CategoryItem.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>380px<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>DropDownTreeBindings</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>DropDownTreeBinding</span> <span class="token attr-name">TextField</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(CategoryItem.Name)<span class="token punctuation">"</span></span>
                             <span class="token attr-name">ItemsField</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@nameof(CategoryItem.Children)<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>DropDownTreeBindings</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ValueTemplate</span><span class="token punctuation">&gt;</span></span>
        @{
            // context is the selected data item
            var item = context as CategoryItem;
            var breadcrumb = item != null ? GetBreadcrumb(item.Id) : string.Empty;
            var isCategory = item?.Children?.Count &gt; 0;
        }
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@breadcrumb<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>TelerikSvgIcon</span> <span class="token attr-name">Icon</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>@(isCategory ? SvgIcon.Folder : SvgIcon.Star)<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
            <span class="token entity" title=" ">&amp;nbsp;</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>@item?.Name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span>
            @if (!string.IsNullOrEmpty(breadcrumb))
            {
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token style-attr language-css"><span class="token attr-name"> <span class="token attr-name">style</span></span><span class="token punctuation">="</span><span class="token attr-value"><span class="token property">font-size</span><span class="token punctuation">:</span><span class="token number">0.85</span>em<span class="token punctuation">;</span> <span class="token property">color</span><span class="token punctuation">:</span><span class="token hexcode">#6c757d</span><span class="token punctuation">;</span></span><span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                    <span class="token entity" title=" ">&amp;nbsp;</span>&mdash; @breadcrumb
                <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 punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ValueTemplate</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>TelerikDropDownTree</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>The <code>GetBreadcrumb</code> helper walks the tree and builds the ancestor path:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">private</span> <span class="token keyword">string</span> <span class="token function">GetBreadcrumb</span><span class="token punctuation">(</span><span class="token keyword">int</span> id<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">var</span> path <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">List</span><span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">BuildPath</span><span class="token punctuation">(</span>CatalogData<span class="token punctuation">,</span> id<span class="token punctuation">,</span> path<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>path<span class="token punctuation">.</span>Count <span class="token operator">&gt;</span> <span class="token number">1</span><span class="token punctuation">)</span> path<span class="token punctuation">.</span><span class="token function">RemoveAt</span><span class="token punctuation">(</span>path<span class="token punctuation">.</span>Count <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// remove leaf, keep ancestors</span>
    <span class="token keyword">return</span> <span class="token keyword">string</span><span class="token punctuation">.</span><span class="token function">Join</span><span class="token punctuation">(</span><span class="token string">" &rsaquo; "</span><span class="token punctuation">,</span> path<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 function">BuildPath</span><span class="token punctuation">(</span>List<span class="token operator">&lt;</span>CategoryItem<span class="token operator">&gt;</span> nodes<span class="token punctuation">,</span> <span class="token keyword">int</span> id<span class="token punctuation">,</span> List<span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token operator">&gt;</span> path<span class="token punctuation">)</span>
<span class="token punctuation">{</span>
    <span class="token keyword">foreach</span> <span class="token punctuation">(</span><span class="token keyword">var</span> node <span class="token keyword">in</span> nodes<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        path<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>node<span class="token punctuation">.</span>Name<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>node<span class="token punctuation">.</span>Id <span class="token operator">==</span> id<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>node<span class="token punctuation">.</span>Children <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&amp;&amp;</span> <span class="token function">BuildPath</span><span class="token punctuation">(</span>node<span class="token punctuation">.</span>Children<span class="token punctuation">,</span> id<span class="token punctuation">,</span> path<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
        path<span class="token punctuation">.</span><span class="token function">RemoveAt</span><span class="token punctuation">(</span>path<span class="token punctuation">.</span>Count <span class="token operator">-</span> <span class="token number">1</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">false</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Selecting &ldquo;Gaming&rdquo; now shows <strong>Gaming &mdash; Electronics &rsaquo; Computers &rsaquo; Laptops</strong> in the input, making the choice self-explanatory without any extra UI:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2026/2026-07/dropdowntree-breadcrumb.png?sfvrsn=156a29f7_2" alt="Gaming - Electronics - Computers – Laptops breadcrumb" /></p><h2>When Should You Use a DropDownTree?</h2><p>The DropDownTree is useful whenever users need to select items from nested data.</p><p>Common scenarios include:</p><ul><li><strong>Product catalogs</strong> &ndash; Browse categories and subcategories while preserving their relationships.</li><li><strong>Organization structures</strong> &ndash; Select employees, teams or departments.</li><li><strong>Document management</strong> &ndash; Choose folders and files from nested structures.</li><li><strong>Geographic selection</strong> &ndash; Navigate countries, regions, cities and locations.</li><li><strong>Permission management</strong> &ndash; Assign roles or resources organized into security hierarchies.</li><li><strong>Content management</strong> &ndash; Select pages, sections or navigation items.</li></ul><p>Whenever hierarchy provides useful context, the DropDownTree helps users make better selections.</p><h2 id="try-the-telerik-ui-for-blazor-dropdowntree-today">Try the Telerik UI for Blazor DropDownTree Today</h2><p>The DropDownTree brings a long-requested hierarchical selection experience to Telerik UI for Blazor applications.</p><p>With built-in filtering, flexible data binding, templates, adaptive rendering, and accessibility support, it provides everything needed to create intuitive hierarchical selection workflows.</p><p>Explore these resources to learn more:</p><ul><li><a target="_blank" href="https://blazorrepl.telerik.com/cAkVwKlf24PvTBlp46">REPL Examples</a> &ndash; Experiment with the code directly in your browser</li><li><a target="_blank" href="https://demos.telerik.com/blazor-ui/dropdowntree/overview">Live Demos</a> &ndash; See the component in action</li><li><a target="_blank" href="https://docs.telerik.com/blazor-ui/components/dropdowntree/overview">Documentation</a> &ndash; Explore configuration options and API</li></ul><p>Whether you are building an inventory system, HR portal, document manager or administrative application, the Telerik UI for Blazor DropDownTree can help users find and select hierarchical data quickly and naturally.</p><p><a target="_blank" href="https://www.telerik.com/try/ui-for-blazor" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/10827/17403342.gif" height="1" width="1"/>]]></content>
  </entry>
</feed>
