<?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:f453186c-2841-418f-99e0-df3dff1c4a4e;id=2125</id>
  <updated>2026-06-20T12:08:23Z</updated>
  <category term="Kendo UI"/>
  <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:7d2311da-a6e9-4adc-9115-599ebd10665b</id>
    <title type="text">Nuxt 4 Server Routes vs. Fetch Composables</title>
    <summary type="text">See a Nuxt app load data from the server in a Server Component and using useFetch to see how Server Components and fetch mechanisms compare.</summary>
    <published>2026-06-18T17:23:23Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Jonathan Gamble </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17363387/nuxt-4-server-routes-vs-fetch-composables"/>
    <content type="text"><![CDATA[<p><span class="featured">See a Nuxt app load data from the server in a Server Component and using useFetch to see how Server Components and fetch mechanisms compare.</span></p><p>There are currently two ways to load data from the server and hydrate it. Nuxt has Server Components, just like React, as well as fetch mechanisms API route hydration.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/image.png?sfvrsn=74a12829_2" alt="Hello World, server time, test schema, home, server component, fetch" /></p><h2 id="tldr">TL;DR</h2><p>This app will load data from the server in a Server Component and using <code>useFetch</code>. Both mechanisms work similarly from the server perspective, but one method hydrates data from the server and sets it as reactive, while the other caches data from the server. Both components can be validated with <a href="http://schema.org">schema.org</a> to verify the data is properly loaded.</p><h2 id="nuxt-setup">Nuxt Setup</h2><p>We must properly config the layout and components.</p><h3 id="configure-tailwind">Configure Tailwind</h3><p>Follow the <a href="https://tailwindcss.com/docs/installation/framework-guides/nuxt">Nuxt 4 Tailwind Guide</a> for the latest instructions.</p><h3 id="nuxt-config">Nuxt Config</h3><p>Make sure to enable component islands with deep selective client. This enables server components and client components inside of them. For more on Nuxt Server Components, see my other article <a href="https://www.telerik.com/blogs/nuxt-3-server-components-rock">Nuxt 3 Server Components Rock</a>.</p><pre class=" language-tsx"><code class="prism  language-tsx">import tailwindcss from "@tailwindcss/vite";

export default defineNuxtConfig({
  compatibilityDate: "2025-07-15",
  devtools: { enabled: true },
  css: ['./app/assets/css/main.css'],
  experimental: {
    componentIslands: {
      selectiveClient: 'deep',
    },
  },
  vite: {
    plugins: [
      tailwindcss(),
    ],
  },
});
</code></pre><h3 id="configure-appapp.vue-for-pages-and-layouts">Configure app/app.vue for Pages and Layouts</h3><pre class=" language-html"><code class="prism  language-html"><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>NuxtLayout</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>NuxtPage</span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>NuxtLayout</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>
</code></pre><h3 id="configure-the-layout-at-applayoutsdefault.vue">Configure the Layout at app/layouts/default.vue</h3><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col items-center gap-4 p-10<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>slot</span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>nav</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex gap-4<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>NuxtLink</span> <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span> <span class="token attr-name">active-class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>font-bold<span class="token punctuation">"</span></span> <span class="token attr-name">exact-active-class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>font-bold<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        Home
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>NuxtLink</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>NuxtLink</span>
        <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/server<span class="token punctuation">"</span></span>
        <span class="token attr-name">active-class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>font-bold<span class="token punctuation">"</span></span>
        <span class="token attr-name">exact-active-class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>font-bold<span class="token punctuation">"</span></span>
      <span class="token punctuation">&gt;</span></span>
        Server Component
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>NuxtLink</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>NuxtLink</span>
        <span class="token attr-name">to</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/fetch<span class="token punctuation">"</span></span>
        <span class="token attr-name">active-class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>font-bold<span class="token punctuation">"</span></span>
        <span class="token attr-name">exact-active-class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>font-bold<span class="token punctuation">"</span></span>
      <span class="token punctuation">&gt;</span></span>
        Fetch
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>NuxtLink</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>nav</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>
</code></pre><p>This allows bolded current routes.</p><h3 id="homepage-at-apppagesindex.vue">Homepage at app/pages/index.vue</h3><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>border p-4 rounded-xl<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    Welcome to the Server Components VS Fetch demo!
  <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>
</code></pre><h3 id="validate-component-at-appcomponentsvalidate.vue">Validate Component at app/components/validate.vue</h3><p>This is used to create a dynamic link to <a href="http://schema.org">schema.org</a>.</p><pre class=" language-html"><code class="prism  language-html"> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>script</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ts<span class="token punctuation">"</span></span> <span class="token attr-name">setup</span><span class="token punctuation">&gt;</span></span><span class="token script language-javascript">
<span class="token keyword">const</span> currentUrl <span class="token operator">=</span> <span class="token function">useRequestURL</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> href <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 template-string"><span class="token string">`https://validator.schema.org/#url=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token function">encodeURIComponent</span><span class="token punctuation">(</span>currentUrl<span class="token punctuation">.</span>href<span class="token punctuation">)</span><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><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">&gt;</span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>template</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>a</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>underline hover:no-underline<span class="token punctuation">"</span></span> <span class="token attr-name">:href</span><span class="token punctuation">&gt;</span></span>Test Schema<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>a</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>template</span><span class="token punctuation">&gt;</span></span>
</code></pre><blockquote><p>Notice <code>useRequestURL()</code> can get the current URL safely.</p></blockquote><h2 id="meta-data-and-schema">Meta Data and Schema</h2><p>Nuxt has a built-in composable, <code>useHead</code>, that allows you to set data directly in the header on the server.</p><pre class=" language-tsx"><code class="prism  language-tsx">useHead({
  meta: [
    {
      property: "article:modified_time",
      content: modifiedTime.data.value,
    },
    {
      name: "last-modified",
      content: modifiedTime.data.value,
    },
  ],
  script: [
    {
      type: "application/ld+json",
      textContent: () =&gt;
        JSON.stringify({
          "@context": "https://schema.org",
          "@type": "WebPage",
          dateModified: modifiedTime.data.value,
        }),
    },
  ],
});
</code></pre><p>You can create a <code>meta</code> tag with the <code>meta</code> property, and set the data that you see fit. This is imperative for SEO. Modern search engines group data using JSON-LD and <a href="http://schema.org">schema.org</a>, which requires a formatted <code>script</code> tag.</p><p>The following examples will set the modified date as the fetch date. This is technically incorrect, as you would want this static, but you can see the usefulness of this for dynamically generated pages with good SEO techniques.</p><h2 id="fetch-with-useasyncdata">Fetch with useAsyncData</h2><p>You can fetch data with <code>useFetch</code> if you have an endpoint, or <code>useAsyncData</code> if you just want to run a safe function directly on the server.</p><h3 id="basic-fetch-page-at-apppagesfetch.vue">Basic Fetch Page at app/pages/fetch.vue</h3><pre class=" language-tsx"><code class="prism  language-tsx">&lt;template&gt;Hello World! &lt;Server-Time-Client /&gt;&lt;/template&gt;
</code></pre><blockquote><p>I love not having to manually import components in the <code>components</code> directory!</p></blockquote><h3 id="here-we-create-appcomponentsserver-time-client.vue">Here We Create app/components/server-time-client.vue</h3><pre class=" language-tsx"><code class="prism  language-tsx">&lt;script setup lang="ts"&gt;
const modifiedTime = await useAsyncData("time", async () =&gt; {
  return new Date().toISOString();
});

useHead({
  meta: [
    {
      property: "article:modified_time",
      content: modifiedTime.data.value,
    },
    {
      name: "last-modified",
      content: modifiedTime.data.value,
    },
  ],
  script: [
    {
      type: "application/ld+json",
      textContent: () =&gt;
        JSON.stringify({
          "@context": "https://schema.org",
          "@type": "WebPage",
          dateModified: modifiedTime.data.value,
        }),
    },
  ],
});
&lt;/script&gt;

&lt;template&gt;
  &lt;div v-if="modifiedTime.data"&gt;Server time: {{ modifiedTime.data.value }}&lt;/div&gt;
  &lt;Validate /&gt;
&lt;/template&gt;
</code></pre><ul><li>We don&rsquo;t have to have <code>client</code> in the name, but it is good to differentiate in this example.</li><li>With <code>useAsyncData</code>, we load data on the server and hydrate it to the browser keeping it reactive. We could have equally created an endpoint at <code>server/api/time.ts</code> and called it with <code>useFetch</code>.</li><li>Your real data will probably be async and will pull from a database.</li></ul><h2 id="server-component">Server Component</h2><p>We can also load the data directly in the HTML on the server, similar to PHP. It will NOT be reactive.</p><h3 id="basic-server-page-at-apppagesserver.vue">Basic Server Page at app/pages/server.vue</h3><pre class=" language-tsx"><code class="prism  language-tsx">&lt;template&gt;Hello World! &lt;Server-Time&gt;&lt;Validate /&gt;&lt;/Server-Time&gt;&lt;/template&gt;
</code></pre><p>We are loading a client component, <code>validate</code>, inside a server component. This is reactive and depends on the current URL. It must use a <code>&lt;slot /&gt;</code> to work correctly. This is why we enabled <code>deep</code> in <code>nuxt.config.ts</code>. Again, see <a href="https://www.telerik.com/blogs/nuxt-3-server-components-rock">Nuxt 3 Server Components Rock</a>.</p><h3 id="server-time-component-at-appcomponentsserver-time.server.vue">Server Time Component at app/components/server-time.server.vue</h3><pre class=" language-tsx"><code class="prism  language-tsx">&lt;script setup lang="ts"&gt;
const modifiedTime = new Date().toISOString();

useHead({
  meta: [
    {
      property: "article:modified_time",
      content: modifiedTime,
    },
    {
      name: "last-modified",
      content: modifiedTime,
    },
  ],
  script: [
    {
      type: "application/ld+json",
      textContent: () =&gt;
        JSON.stringify({
          "@context": "https://schema.org",
          "@type": "WebPage",
          dateModified: modifiedTime,
        }),
    },
  ],
});
&lt;/script&gt;

&lt;template&gt;
  &lt;div class="flex flex-col gap-4 items-center"&gt;
    &lt;div&gt;Server time: {{ modifiedTime }}&lt;/div&gt;
    &lt;slot /&gt;
  &lt;/div&gt;
&lt;/template&gt;
</code></pre><ul><li>A Server Component <em>must</em> have the <code>.server.ts</code> extension to load correctly.</li><li>We can just put our server code directly in the header. None of the JavaScript gets loaded in the browser for this component.</li></ul><h2 id="comparison">Comparison</h2><p>So what is the difference?</p><table><style>table, th, td {
  border: 1px;
  border-color: #bdbdba;
  border-style: dotted;
  border-collapse: collapse;
  margin-right: auto;
  cellspacing="5";
  text-align :left;
}
</style>
 <thead><tr><th style="width:33.3333%;padding:5px;">Aspect</th><th style="width:33.3333%;padding:5px;">Server components</th><th style="width:33.3333%;padding:5px;"><code>useFetch</code> / <code>useAsyncData</code></th></tr></thead><tbody><tr><td style="width:33.3333%;padding:5px;">Main purpose</td><td style="width:33.3333%;padding:5px;">Render UI on server</td><td style="width:33.3333%;padding:5px;">Load reactive data</td></tr><tr><td style="width:33.3333%;padding:5px;">Output</td><td style="width:33.3333%;padding:5px;">HTML</td><td style="width:33.3333%;padding:5px;">Data refs</td></tr><tr><td style="width:33.3333%;padding:5px;">Client JS</td><td style="width:33.3333%;padding:5px;">Less / none for island</td><td style="width:33.3333%;padding:5px;">Normal hydrated component</td></tr><tr><td style="width:33.3333%;padding:5px;">Best for</td><td style="width:33.3333%;padding:5px;">Static or mostly static UI</td><td style="width:33.3333%;padding:5px;">Interactive data-driven UI</td></tr><tr><td style="width:33.3333%;padding:5px;">Data state</td><td style="width:33.3333%;padding:5px;">Props in, HTML out</td><td style="width:33.3333%;padding:5px;"><code>data</code>, <code>pending</code>, <code>error</code></td></tr><tr><td style="width:33.3333%;padding:5px;">Refresh behavior</td><td style="width:33.3333%;padding:5px;">Re-render island</td><td style="width:33.3333%;padding:5px;"><code>refresh()</code> / reactive reload</td></tr><tr><td style="width:33.3333%;padding:5px;">Good example</td><td style="width:33.3333%;padding:5px;">Server-rendered timestamp block</td><td style="width:33.3333%;padding:5px;">Posts, user, notes, lists</td></tr></tbody></table><br /><p>Generally speaking, Fetch Composables can be reactive and load JavaScript. Server Components just load the data as if it were static HTML. This is clear when you navigate between pages, the Server Component does not reload the date, while the Fetch Component does.</p><h3 id="testing-schema">Testing Schema</h3><p>You can click the <code>test schema</code> button to verify the data is loaded from the server and not client, and that it loads correctly.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/image-1.png?sfvrsn=22412d3d_2" alt="webpage 0 errors" /></p><p>If you&rsquo;re loading something quickly, use Server Components. If you need dynamic data, use Fetch Composables.</p><p>That&rsquo;s a wrap!</p><p><strong>Repo:</strong> <a href="https://github.com/jdgamble555/nuxt-server-vs-middleware">GitHub</a><br /><strong>Demo:</strong> <a href="https://nuxt-server-vs-middleware.vercel.app/">Vercel</a>
</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">Pure Surreal Database Authentication in Nuxt</h4></div><div class="col-8"><p class="u-fs16 u-mb0">See how <a target="_blank" href="https://www.telerik.com/blogs/pure-surreal-database-authentication-nuxt">Nuxt pairs up with Surreal Database</a> with a sample app enabling login, register, logout and password change options with route guards and server-safe APIs.</p></div></div></aside><img src="https://feeds.telerik.com/link/10827/17363387.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:9d8a837c-aefd-489a-a053-9009c55405d6</id>
    <title type="text">KendoReact vs. OSS: What You Can Actually Get for Free (and What You Can’t)</title>
    <summary type="text">If you’ve reached the point where free UI libraries are starting to cause friction, it may be time to consider whether they’re still the right choice for your team—and your app.</summary>
    <published>2026-06-17T20:58:55Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Kathryn Grayson Nanz </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17362910/kendoreact-vs-oss-what-actually-get-free-what-you-cant"/>
    <category term="Kendo UI"/>
    <content type="text"><![CDATA[<p>There&rsquo;s no shortage of React component libraries out there, many of them open-source or free to use. </p><p>Developers often look at a commercial component library and think, &ldquo;Why would I ever pay for something I could get for free from someone else?&rdquo; And it&rsquo;s a fair question to ask! The answer often depends primarily on a) what you&rsquo;re building and b) how much experience you (and your team) have building and maintaining frontend components. </p><p>If you&rsquo;ve reached the point where free UI libraries are starting to cause friction, it may be time to consider whether they&rsquo;re still the right choice for your team&mdash;and your app. </p><p>In this article, we&rsquo;ll break down how to evaluate open-source vs. commercial UI libraries in practical terms, including: </p><ul><li>The right choice for getting started with a new project</li><li>The cost shifts as engineering time, maintenance and integration needs scale up</li><li>Questions about predictability, support and overhead</li><li>How to balance all the pros and cons to make the choice that best suits your needs</li></ul><h2>Who&rsquo;s Building the Library? </h2><p>The number one difference between an open-source (OSS) and commercial library is, of course, who builds and maintains the components. </p><p>Open-source projects are community-built and supported, which has some distinct benefits: most notably, that means you can get involved, personally! If you have a specific dream or vision for improving an open-source project, there are incredible OSS organizers out there who would love you to work with them to make those improvements a reality. It&rsquo;s a chance to both sharpen your skills as a developer and work alongside some truly amazing people. OSS projects are wonderful for identifying a need in the development community and quickly filling that gap. </p><p>On the flip side, the rise of AI and &ldquo;vibe coding&rdquo; is a real emerging challenge in the OSS world right now. Well-intentioned organizers are struggling to sort through low-quality generated issues and code submissions. </p><p>Open-source is also (and always has been) volunteer-based. While some extremely popular OSS projects receive financial backing, the majority are reliant on volunteer contributions. That can make it a challenge for projects to sustain development over long periods of time. </p><p><a href="https://www.telerik.com/kendo-react-ui/components/introduction#what-is-kendoreact">Commercial component libraries</a>, on the other hand, are built by teams of developers who are financially compensated for their time and efforts. This has a few distinct benefits. For one, it means that developers are more likely to stay on the team for long periods of time&mdash;and that institutional knowledge helps create a more stable product. It also means that the library can be the primary focus of the developers on the team, rather than something they contribute to in their spare time on evenings or weekends. </p><h2>How Is the Library Maintained? </h2><p>A library is more than just its first version. As the fields of web development and software engineering evolve, the tools we use have to evolve with it. Even if a tool is perfect when it&rsquo;s created, will it still be useful in six months? What about in a year? Five years? Many React developers still feel the pain of Create React App&rsquo;s slow decline&mdash;and that wasn&rsquo;t even a tiny, niche project! </p><p>This is one of those times when what you&rsquo;re building weighs into the decision-making process heavily. If you&rsquo;re working on a side project of your own, then this matters far less. The same is true if you&rsquo;re prototyping a new idea, testing a new technology or similar. Those are great opportunities to reach for open-source options! </p><p>However, if you&rsquo;re migrating a legacy application, adding new features to an app with a significant userbase or building enterprise software, the longevity and dependability of your tooling matters&mdash;a lot! </p><p>It&rsquo;s also about more than just whether or not the library is still being updated; it&rsquo;s also about <a href="https://www.telerik.com/support/whats-new/kendo-react-ui/roadmap">how often those updates happen</a>, and whether or not there&rsquo;s a regular cadence. If you know that a library will get quarterly updates, you can start planning ahead on your own development cycles with that in mind. It offers the ability to be proactive&mdash;instead of reactive&mdash;when it comes to planning around potentially breaking changes in version upgrades. </p><h2>What Support Is Available? </h2><p>As mentioned earlier, one of the biggest perks of open-source software is the community that surrounds it. Popular projects often have vibrant, engaged groups of developers to help answer questions or write documentation. They also tend to have many resources (like tutorial videos or blogs) written by users, so you can often make a quick Google search and find someone who&rsquo;s tackling a similar problem to the one you&rsquo;re dealing with. </p><p>However, much like maintenance&mdash;can you be sure that the community will be there for the long haul? If you&rsquo;re still using this tool in five years, will there also still be an active chat you can ask questions to &hellip; or will another tool have become more popular in the meantime? </p><p>Additionally, there&rsquo;s also the question of <a href="https://www.telerik.com/kendo-react-ui/support">professional support.</a> Of course, it&rsquo;s handy to be able to Google things and find walk-throughs and tutorials, but what if you need to ask a specific implementation question? Is there someone available to get on a call with you or walk through your individual use case to troubleshoot the issue? If you post on a support forum, are you comfortable with just hoping someone with enough experience will be able to answer your question&mdash;or are you working on a project critical enough that you need to know a dedicated support professional is available if (or perhaps, <em>when</em>) things go wrong? </p><h2>Does It Meet the Required Standards? </h2><p>This is another one that depends a lot on what you&rsquo;re building&mdash;and who you&rsquo;re building it for. If you&rsquo;re building software that needs to meet certain accessibility or security requirements, you may have a harder time verifying that compliance with open-source solutions. </p><p>For example, do you need something that&rsquo;s <a href="https://www.telerik.com/kendo-react-ui/components/security/overview">certified ISO 27001 or SOC 2 compliant?</a> Do you need be able to provide the ACR to show that a given tool <a href="https://www.telerik.com/kendo-react-ui/components/accessibility">meets Section 508 standards</a>? Are you confident that the tools you&rsquo;re using will be updated to meet WCAG 3.0 standards, when that releases? </p><p>While this may not be a concern for every piece of software, when it matters&mdash;it really matters! </p><h2>What Tool Integrations Are Available? </h2><p>Good systems designers know that choosing a tool is about more than the capability of the tool itself. It&rsquo;s about how that tool works with all the other tools in your system&mdash;and the people who have to use it! </p><p>For example, let&rsquo;s talk about design: if you have primarily backend or full stack developers and no designers, it may make sense to choose a component library that comes with <a href="https://www.telerik.com/design-system/docs/themes/get-started/introduction/#available-themes">lots of built in themes</a>&mdash;or even <a href="https://www.telerik.com/design-system/docs/themes/themebuilder/">full theming software!</a> If you&rsquo;re already using CSS tools <a href="https://www.telerik.com/design-system/docs/themes/integrations/tailwind/">Tailwind</a> or <a href="https://www.telerik.com/design-system/docs/themes/kendo-themes/bootstrap/">Bootstrap</a>, you should find a component library that will work well with those options. If you do have designers and they work in Figma, choosing a component library that offers <a href="https://www.telerik.com/design-system/docs/resources/figma-ui-kits/">Figma UI Kits</a> would probably be something that wins you some points with them! </p><p>When you&rsquo;re assessing tools, it&rsquo;s important to keep in mind the big picture&mdash;not just how the tool works in isolation, but how it fits into your entire project and team&rsquo;s workflow. </p><h2>How Does This Work with AI? </h2><p>Similarly, what about AI? If your team is already using VS Code and Copilot, a library that offers <a href="https://www.telerik.com/kendo-react-ui/components/ai-tools">Copilot integrations</a> would be most efficient. If you know you&rsquo;re going to need <a href="https://www.telerik.com/kendo-react-ui/components/ai-components">AI-powered features</a> in your app, finding a component library that&rsquo;s made to support that can save you a lot of time and effort. </p><p>Working with component libraries that don&rsquo;t offer specialized AI tools means that you have to make do with the generic AI output for those components&mdash;which may be OK enough for smaller projects, but can quickly become a stumbling block at scale. If your AI tool only has a basic understanding of things like the library&rsquo;s API, design system or best practices, the code you get back is going to take a lot of revision before it&rsquo;s production ready. By choosing a component library that offers integrated AI tools, you can get back a higher quality caliber of output&mdash;saving you time, tokens and iteration cycles. </p><h2>Which Option Is the Right Fit for Your Team? </h2><p>As you can see, there&rsquo;s a lot to consider when it comes to tooling decisions&mdash;and the decision rarely comes down to cost, alone. Instead, it&rsquo;s about where you want to invest engineering time: building product features or building UI infrastructure. For teams that are already feeling the limits of open-source approaches, the question isn&rsquo;t necessarily &ldquo;why pay for UI components,&rdquo; but rather &ldquo;what is the cost of continuing to build and maintain this ourselves?&rdquo; </p><p>For smaller teams and projects, open-source can be a fantastic solution&mdash;and it comes with the benefit of getting to be an active participant in the ecosystem of the tooling you use. </p><p>For larger teams and enterprise products, the stability, support and extensibility of commercial libraries may make them a better fit. When you find a library that checks all these boxes&mdash;stable maintenance, professional support, compliance certifications and strong ecosystem integrations&mdash;it may be worth investing in. </p><h3>When Open-source UI Libraries Are the Right Fit: </h3><ul><li>You&rsquo;re building a prototype, MVP or internal tool </li><li>Your UI requirements are relatively simple </li><li>Your team is comfortable owning and maintaining UI infrastructure </li><li>You&rsquo;re not worried about long-term scalability, accessibility or compliance concerns </li></ul><h3>When a Commercial UI Library Makes More Sense: </h3><ul><li>You&rsquo;re building a production-grade or enterprise application </li><li>Your UI includes complex components (such as grids, schedulers or data-heavy views) </li><li>Performance, accessibility and consistency are critical </li><li>Your team is losing time integrating, fixing or extending free UI libraries </li><li>You need predictable support and long-term stability </li></ul><p>These tradeoffs are real, and there's no universally right answer. If a commercial library sounds like the right fit, Progress KendoReact is worth a look&mdash;it's designed with exactly these enterprise considerations in mind. It&rsquo;s <a href="https://www.telerik.com/try/kendo-react-ui" target="_blank">free to try</a> for 30 days, including full support to help you get up and running. </p><p>And if that feels like more than you need at this point, there&rsquo;s always <a href="https://www.telerik.com/kendo-react-ui/components/getting-started/free-vs-premium" target="_blank">KendoReact Free</a>: a customizable, enterprise-quality React UI library with no license or sign-up required. Just npm download and start building! Check out the recap below to help figure out which option is the best choice for you. </p><h2>OSS vs. Commercial Libraries at a Glance </h2><table style="margin-right:auto;"><style>table,
 th,
    td {
      border: 1px;
      border-color: #bdbdba;
      border-style: dotted;
      border-collapse: collapse;
      margin-right: auto;
      cellspacing="5";
      text-align: left;
    }
  </style>
 <thead><tr><th data-role="resizable" style="width:33.3333%;padding:5px;"><strong>Factor</strong></th><th data-role="resizable" style="width:33.3333%;padding:5px;"><strong>Open-source Libraries</strong></th><th style="width:33.3333%;padding:5px;"><strong>Commercial UI Libraries (e.g., KendoReact)</strong></th></tr></thead><tbody><tr><td style="width:33.3333%;padding:5px;">Ownership &amp; maintenance</td><td style="width:33.3333%;padding:5px;">Community-driven, variable continuity</td><td style="width:33.3333%;padding:5px;">Dedicated engineering teams, predictable roadmap</td></tr><tr><td style="width:33.3333%;padding:5px;">Support</td><td style="width:33.3333%;padding:5px;">Forums, community, no SLA</td><td style="width:33.3333%;padding:5px;">SLA-backed support from product experts</td></tr><tr><td style="width:33.3333%;padding:5px;">Performance</td><td style="width:33.3333%;padding:5px;">Depends on implementation, often requires customizations for optimization</td><td style="width:33.3333%;padding:5px;">Built-in performance optimizations (e.g. virtualization, large datasets&mdash;grid)</td></tr><tr><td style="width:33.3333%;padding:5px;">Integration</td><td style="width:33.3333%;padding:5px;">Multiple libraries combined, inconsistent APIs</td><td style="width:33.3333%;padding:5px;">Unified component system designed to work together</td></tr><tr><td style="width:33.3333%;padding:5px;">Accessibility &amp; compliance</td><td style="width:33.3333%;padding:5px;">Varies by project, often requires audits and fixes</td><td style="width:33.3333%;padding:5px;">Built-in accessibility aligned with standards</td></tr><tr><td style="width:33.3333%;padding:5px;">Updates and upgrades</td><td style="width:33.3333%;padding:5px;">Irregular, dependent on maintainers</td><td style="width:33.3333%;padding:5px;">Regular releases, documented changes</td></tr><tr><td style="width:33.3333%;padding:5px;">AI tools &amp; compatibility</td><td style="width:33.3333%;padding:5px;">Generic AI output, not component-aware</td><td style="width:33.3333%;padding:5px;">AI tools aligned with real component APIs and usage</td></tr><tr><td style="width:33.3333%;padding:5px;">TCO</td><td style="width:33.3333%;padding:5px;">Low upfront cost, higher long-term engineering effort</td><td style="width:33.3333%;padding:5px;">License cost + reduced engineering, maintenance and integration overhead</td></tr></tbody></table><br /><p><a href="https://www.telerik.com/kendo-react-ui/components/free" target="_blank" class="Btn">Get Started with KendoReact</a></p><img src="https://feeds.telerik.com/link/10827/17362910.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:3f695ccd-2fff-4d8d-af98-598fe8161769</id>
    <title type="text">Building Component-Aware Production UIs with Angular and Kendo UI MCP</title>
    <summary type="text">Through MCP, we can use component-aware AI to help us build user interfaces in conjunction with our favorite component libraries. See it in Angular!</summary>
    <published>2026-06-15T16:26:20Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Dany Paredes </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17361570/building-component-aware-production-ui-angular-kendo-ui-mcp"/>
    <content type="text"><![CDATA[<p><span class="featured">Through MCP, we can use component-aware AI to help us build user interfaces in conjunction with our favorite component libraries. See it in Angular!</span></p><p>We as developers use AI tools like Cursor, Claude Code and Copilot; they have changed how we write code. We delegate entire sections of our applications to an agent, and for many tasks, this works really well.</p><p>But when it comes to building user interfaces with a specific framework, things get complicated fast. Results are unpredictable, the code feels incomplete and we end up spending more time fixing than building.</p><p>If you have felt that, you are not alone. The good news is that the problem is not the AI. The problem is how we are using it.</p><p>We will see why AI struggles with UI development and how a new approach called &ldquo;component-aware AI&rdquo; changes everything. But first, let&rsquo;s understand what is really missing.</p><h2 id="the-problem">The Problem</h2><p>In the world of AI, we read about context all the time, which is the key problem when things go wrong. When you ask your AI assistant to use a component from a specific framework, it relies on its training data. It does not know:</p><ul><li>The exact version of Angular you are using</li><li>The best practices for your library</li><li>The specific design rules of your organization</li></ul><blockquote><p>Do you want to learn more about the <a href="https://www.progress.com/resources/webinars/the-context-problem-behind-stalled-ai-roi">context problem</a>?</p></blockquote><p>So the AI starts guessing. And that guessing creates real problems:</p><ul><li><strong>High costs and effort:</strong> We waste many tokens just explaining our UI library to the AI. We end up copying and pasting manuals, providing code examples and fixing mistakes, making the whole process slow and expensive.</li><li><strong>The &ldquo;fix and repeat&rdquo; loop:</strong> We copy the code, find a small bug, ask the AI to fix it and repeat the cycle.</li><li><strong>Low-quality code:</strong> The code the AI gives us is often incomplete or outdated. We spend more time fixing it than it would have taken to write it from scratch.</li></ul><p>But we have good news: using a new approach called &ldquo;component-aware AI&rdquo; changes everything.</p><h2 id="the-shift-to-mcp">The Shift to MCP</h2><p>We need a new way for the AI to work. We need &ldquo;component-aware AI.&rdquo; This shift is made possible by the <a href="https://www.telerik.com/blogs/promise-model-context-protocol">Model Context Protocol (MCP)</a>. Instead of the AI trying to remember code from its training data, it can read the live documentation of your library in real time.</p><p>Think of it like the difference between following a recipe from memory versus using a smart kitchen robot with everything preconfigured. It is automated, precise and much freer from errors.</p><p>When you use an MCP like the Progress Kendo UI for <a href="https://www.telerik.com/kendo-angular-ui/components/ai-tools/agentic-ui-generator/getting-started">Angular MCP</a>, the AI assistant queries the official library data before it writes any code. This verifies that every property and every method is correct.</p><p>The best way to understand the difference is to see it in action. I have experienced the shift from working with blind AI to giving agents the right tools to deliver code with confidence and without technical debt. Let&rsquo;s do it!</p><blockquote><p>Want to build your own MCP server? Check this out: <a href="https://www.youtube.com/watch?v=mM8T8bSCTyk">https://www.youtube.com/watch?v=mM8T8bSCTyk</a></p></blockquote><h2 id="the-traditional-way-the-blind-ai">The Traditional Way (The &ldquo;Blind&rdquo; AI)</h2><p>Let&rsquo;s start with the scenario you probably already know.</p><p>Open your terminal and run these two commands to create the project and install the Kendo UI for <a href="https://www.telerik.com/kendo-angular-ui/components/grid">Angular Grid</a>:</p><pre class=" language-bash"><code class="prism  language-bash"><span class="token comment"># Create the project</span>
ng new accounting-app --style<span class="token operator">=</span>scss --ssr<span class="token operator">=</span>false
 
<span class="token comment"># Install Kendo UI Grid</span>
ng add @progress/kendo-angular-grid
</code></pre><p>Then, you ask your AI assistant (without specialized context) to build the interface:</p><blockquote><p><strong>Prompt:</strong> &ldquo;Create a General Ledger grid with Kendo UI for Angular. Show Date, Description, Debit, and Credit. Add footer totals.&rdquo;</p></blockquote><p>The project compiles and runs, but the result is full of immediate technical debt. Here is what the AI generates:</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token comment">// ❌ BLIND AI: Ignores Kendo built-in aggregation utilities</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">App</span> <span class="token punctuation">{</span>
 <span class="token keyword">protected</span> readonly ledgerItems<span class="token punctuation">:</span> LedgerEntry<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
 
 <span class="token comment">// Manual logic the developer must maintain forever</span>
 <span class="token keyword">protected</span> <span class="token keyword">get</span> <span class="token function">totalDebit</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> <span class="token keyword">this</span><span class="token punctuation">.</span>ledgerItems<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> item<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> sum <span class="token operator">+</span> item<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 punctuation">}</span>
 
 <span class="token keyword">protected</span> <span class="token keyword">get</span> <span class="token function">totalCredit</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> <span class="token keyword">this</span><span class="token punctuation">.</span>ledgerItems<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> item<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> sum <span class="token operator">+</span> item<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>
<span class="token punctuation">}</span>
</code></pre><p>Let&rsquo;s look at the problems here. For <code>totalDebit</code>, if you add more columns with totals, you have to manually write a <code>.reduce()</code> for each one. The AI does not know that Kendo UI already has <a href="https://www.telerik.com/kendo-angular-ui/components/grid/grouping/aggregates"><code>aggregateBy</code></a> built in.</p><p>Another common inconsistency is with colors. Instead of using design tokens, it uses hard-coded values like <code>#f5f7fb</code>, which breaks visual coherence across the app.</p><p>The worst part is the lack of accessibility. There are no ARIA attributes or semantic roles, which Kendo UI handles automatically when configured correctly.</p><pre class=" language-html"><code class="prism  language-html"><span class="token comment">&lt;!-- ❌ BLIND AI: Hardcoded color, no accessibility, no built-in features --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-grid</span> <span class="token attr-name">[data]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ledgerItems<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>kendo-grid-column</span> <span class="token attr-name">color</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>#f5f7fb<span class="token punctuation">"</span></span> <span class="token attr-name">field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>debit<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>Debit<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>kendo-grid-column</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>kendo-grid</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>In the end, you spend more time cleaning up AI code than you would have spent writing it from scratch. The real issue is that we are not giving the AI the right tools to perform at its best.</p><p>We can fix that, because the solution is simpler than you might think.</p><h2 id="the-agentic-way-using-the-power-of-kendo-ui-mcp">The Agentic Way: Using the Power of Kendo UI MCP</h2><p>Now, let&rsquo;s move to the productive workflow and combine the power of Kendo UI MCP with our agents.</p><p>Instead of the manual configuration and the &ldquo;blind&rdquo; approach, we start with a single command. Open your terminal and run:</p><pre class=" language-bash"><code class="prism  language-bash">npx kendo angular setup
</code></pre><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/npx-kendo-angular-set.png?sfvrsn=58d383f_2" alt="Git npx kendo angular setup" /></p><p>Here is where the magic happens. It automatically installs the license and registers the <strong>Kendo UI MCP Server</strong> across your IDEs and AI agents (VS Code, Cursor, Claude Code). From this point on, your agent has full context and no longer has to guess.</p><p>Now we ask the same AI agent to build the same application with the same prompt. This time our agent, thanks to MCP, uses the <code>Kendo UI Generator</code> tool to generate the code.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/angular-mcp-grid.png?sfvrsn=8457b8db_2" alt="Create a general ledger grid with Kendo UI for Angular. Show date, description, debit, and credit. Add footer totals." /></p><p>Let&rsquo;s look at the generated code and why it is production-ready.</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token comment">// ✅ COMPONENT-AWARE AI: Uses Signals, Modern Angular Patterns, and Kendo's aggregateBy</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> aggregateBy<span class="token punctuation">,</span> AggregateResult <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@progress/kendo-data-query'</span><span class="token punctuation">;</span>
 
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">GeneralLedgerComponent</span> <span class="token punctuation">{</span>
 <span class="token keyword">private</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 comment">// Modern signal-based state</span>
 <span class="token keyword">protected</span> ledgerEntries <span class="token operator">=</span> signal<span class="token operator">&lt;</span>LedgerEntry<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">this</span><span class="token punctuation">.</span>ledgerService<span class="token punctuation">.</span><span class="token function">getLedgerEntries</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">// Kendo's aggregateBy handles all column totals in one call, no manual .reduce() needed</span>
 <span class="token keyword">protected</span> totals <span class="token operator">=</span> computed<span class="token operator">&lt;</span>AggregateResult<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 function">aggregateBy</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">ledgerEntries</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> field<span class="token punctuation">:</span> <span class="token string">'debit'</span><span class="token punctuation">,</span> aggregate<span class="token punctuation">:</span> <span class="token string">'sum'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
     <span class="token punctuation">{</span> field<span class="token punctuation">:</span> <span class="token string">'credit'</span><span class="token punctuation">,</span> aggregate<span class="token punctuation">:</span> <span class="token string">'sum'</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>The AI now uses Signals (<code>signal</code>, <code>computed</code>) and proper dependency injection (<code>inject</code>), which are the recommended modern Angular patterns. And instead of a manual <code>.reduce()</code> per column, it uses Kendo UI <code>aggregateBy</code>. Add a new column with a total, and you just add one line to the array, nothing else.</p><p>In the SCSS file, instead of hard-coding colors, it uses official Kendo UI Design Tokens such as <code>var(--kendo-color-surface-alt)</code> and <code>var(--kendo-color-success)</code>.</p><p>And in the template, the difference is clear:</p><pre class=" language-html"><code class="prism  language-html"><span class="token comment">&lt;!-- ✅ COMPONENT-AWARE AI: Design tokens, accessibility, full features enabled --&gt;</span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>kendo-grid</span>
   <span class="token attr-name">[data]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>ledgerEntries()<span class="token punctuation">"</span></span>
   <span class="token attr-name">[reorderable]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span>
   <span class="token attr-name">[navigable]</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">role</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>table<span class="token punctuation">"</span></span>
   <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>General Ledger Entries<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>kendo-grid</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>That is the key difference. When we use <a href="https://www.telerik.com/kendo-angular-ui/components/ai-tools">Kendo UI for Angular MCP</a>, our agents do not just generate code. They generate the right code, follow best practices and deliver to a high standard.</p><p>Here are the three benefits I value most:</p><ol><li>Significantly reduced hallucinations: The AI does not guess property names. It reads the actual documentation in real time.</li><li>Accessible by default: Kendo UI components are built to follow accessibility standards. When the AI uses these components correctly, your app is automatically inclusive.</li><li>Token efficiency: No more copying large lines of documentation into your chat. The AI fetches only what it needs, saving you tokens and money.</li></ol><blockquote><p>Go deeper and explore <a href="https://www.telerik.com/kendo-angular-ui/components/ai-tools#kendo-ui-for-angular-ai-tools-overview">everything that Kendo UI MCP can do</a>.</p></blockquote><p>Now that we have seen both worlds side by side, let&rsquo;s talk about what this really means for how we choose our tools going forward.</p><h2 id="conclusion">Conclusion</h2><p>Simply using AI is not enough. And simply using a component library is not enough anymore, either.</p><p>This is the paradigm shift. We are moving from a world where a library just gives you components to one where a library also provides the context layer that makes modern development possible. Kendo UI MCP is exactly that: it acts as a bridge between your agents and the library so that the AI can do its job properly rather than guessing.</p><p>By bridging that context gap, we move from &ldquo;guessing&rdquo; to &ldquo;knowing&rdquo; and agents stop hallucinating APIs and start delivering production-ready code.</p><blockquote><p>My personal take: Next time you evaluate a UI library, do not just look at the component catalog. Ask if your framework has the tools for the way we build software today. Because that is the question I ask myself now, every time.</p></blockquote><blockquote><p>BTW, if you want to work less and ship more, it is time to <a href="https://www.telerik.com/blogs/angular-kendo-ui-mcp-making-agents-work">make your agents work for you</a> so you can focus on building what really matters.</p></blockquote><h3 id="resources">Resources</h3><ul><li><a href="https://www.telerik.com/kendo-angular-ui"><strong>Kendo UI for Angular</strong></a></li><li><strong>MCP Protocol:</strong> <a href="https://modelcontextprotocol.io/">Learn more about MCP</a></li></ul><h2 id="try-now">Try Now</h2><p>Remember, Kendo UI for Angular comes with a free 30-day trial, so you can explore all these goodies yourself.</p><p><a href="https://www.telerik.com/try/kendo-angular-ui" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/10827/17361570.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:49c497b3-3ecf-4392-b05b-8f6a9712b329</id>
    <title type="text">Telerik UI for Blazor Meets A2UI: The Next Step Toward Dynamic UI Generation</title>
    <summary type="text">Learn how A2UI enables AI systems to generate interactive, dynamically generated Blazor interfaces and how Progress Telerik UI for Blazor will help.</summary>
    <published>2026-06-15T13:30:53Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Maya Mateva </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17361485/telerik-ui-blazor-meets-a2ui-next-step-toward-dynamic-ui-generation"/>
    <content type="text"><![CDATA[<p><span class="featured">Learn how A2UI enables AI systems to generate interactive, dynamically generated Blazor interfaces and how Progress Telerik UI for Blazor will help.</span></p><p>Imagine this: instead of clicking through a travel app&mdash;picking dates, applying filters, scrolling results, opening tabs to compare&mdash;you type one sentence:</p><p><em>&ldquo;I want to get a flight from London to New York. Give me a plan as a dashboard with important data about the flight.&rdquo;</em></p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/blazor-a2ui-flight-dashboard.gif?sfvrsn=9f6fca6_2" alt="Telerik UI for Blazor A2UI produced a flight assistant dashboard that asks origin, destination, departure and return dates, then shows flight overviews, airport information, travel tips, flight prices in a bar chart, flight price data in table by month" /></p><p>What comes back is more than a paragraph explaining the basics. It&rsquo;s a whole interface with a structured results grid showing airline, price, duration and departure time. It&rsquo;s a comparison view so you can weigh your options, all assembled on the spot. No predefined screen needed. Just intent and a UI that responds to it.</p><p>That is the experience shift A2UI creates. And it is coming to <a href="https://www.telerik.com/blazor-ui">Progress Telerik UI for Blazor</a>.</p><p>Don&rsquo;t miss the live demo! <a href="https://www.telerik.com/campaigns/telerik-and-kendo-ui-2026-q2-release-webinar">Register for the Telerik and Kendo UI 2026 Q2 release webinar on June 16</a> to see A2UI transforming intent into interactive Blazor UI in real time.</p><h2 id="what-is-a2ui">What Is A2UI?</h2><p><a href="https://a2ui.org/specification/v0.8-a2ui/">A2UI (Agent-to-User Interface)</a> is an open protocol introduced by Google that defines a standard way for AI agents to communicate their intent to generate a user interface. Think of it as a universal UI language that agents can speak to any client application.</p><p>Rather than describing insights, AI describes interfaces. The application then renders those interfaces using its own UI framework, styling and interaction model.</p><p>In this model:</p><ul><li><strong>AI facilitates the UI composition layer.</strong> It reasons over context and emits a structured definition&mdash;a declarative JSON format describing which components to render and what data to populate them with.</li><li><strong>The application remains the source of truth for rendering and behavior.</strong> The client maintains a catalog of pre-approved components.</li><li><strong>The user experiences fully interactive interfaces generated from intent.</strong> Not static mockups. Not screenshots. Real components with real data.</li></ul><p>A2UI effectively shifts AI from being a narrator to being a <strong>composer of user experiences</strong>.</p><h2 id="a2ui-in-telerik-ui-for-blazor">A2UI in Telerik UI for Blazor</h2><p>Within Telerik UI for <a href="https://demos.telerik.com/blazor-ui/a2ui/ai-a2ui" target="_blank">Blazor, A2UI</a> introduces an additional layer on top of the existing component ecosystem. Components are no longer only developer-defined. They become composable primitives available for AI-driven interface construction.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/a2ui-progression.png?sfvrsn=986c7d88_2" alt="Backend, transport like AGUI, A2UI instead of text-only response – dynamically generated UI, Progress components, application" /></p><p>In effect, the library becomes a <strong>structured palette for dynamic interface generation</strong>. <strong>Your design system stays intact.</strong> <strong>Your business logic does not move.</strong></p><h2 id="why-a2ui-makes-sense-agents-think-in-text-users-work-in-interfaces">Why A2UI Makes Sense: Agents Think in Text; Users Work in Interfaces</h2><p>AI is rapidly reshaping how software is built and consumed. Over the past year, we have seen an acceleration of copilots, agents and large language models capable of reasoning over data, executing workflows and assisting users in ways that were previously out of reach.</p><p>Yet, one fundamental limitation remains.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/agents-text-users-interfaces.png?sfvrsn=ba8582d0_2" alt="Agents think in text. Users work in interfaces" /></p><p>AI is exceptionally good at producing text. But applications are not built around text. They are built around interfaces.</p><p>People perceive and interact more easily through visuals&mdash;dashboards, forms, grids, charts, filters, and workflows they can interact with directly. They want to see, explore and act. This is why A2UI has so much potential.</p><p>Modern enterprise applications have long assumed that UI is static. Developers define screens. Users adapt to them. AI, when introduced, typically sits alongside the application as a conversational layer rather than becoming part of the interface itself.</p><p>A2UI introduces the possibility to change that. When AI systems generate structured UI definitions that adapt to context and data, rather than just a block of text, we aren&rsquo;t just enabling what the <em>system says</em>, but what the <em>user sees</em>.</p><p>A single request like &ldquo;analyze my sales performance this quarter&rdquo; can translate into multiple valid experiences depending on who is asking and why&mdash;an executive KPI dashboard, a detailed filterable data grid, a chart-centric exploratory view or a hybrid analytical workspace. The underlying data is the same, but the intent is different. And AI is uniquely positioned to interpret it.</p><h2 id="a2ui-in-practice">A2UI in Practice</h2><p>Use cases for A2UI implementation are as myriad as you can imagine.</p><p>Let&rsquo;s go back to the example of booking a flight. Instead of tedious stepping through pages, one by one, filling and selecting values, you can type: <em>&ldquo;I want to fill a form for booking a flight from London to New York.&rdquo;</em></p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/a2ui-flight-form.png?sfvrsn=6dcce9c5_2" alt="In prompt box, I want to fill a form for booking a flight from London to New York. Then fields for origin, destination, departure date and time, passenger name" /></p><p>The AI interprets that intent and <strong>emits</strong> a UI definition&mdash;a JSON description of a data form (pre-filled with data where possible and using the right components), a results grid (columns: airline, price, departure, duration, stops) and a comparison chart with the top options.</p><p>The Blazor renderer receives that definition and displays the interface using real Telerik UI for Blazor components. The Grid, the Form, the Charts&mdash;all themed and all enabling you to interpret and visualize data as naturally as possible.</p><p>Different users expressing the same intent might receive different representations depending on context, preferences or available data. Maybe one person gets a chart-first view and another gets a table. That is dynamic UI generation in practice.</p><h3 id="a-step-further-building-on-an-emerging-ai-ecosystem">A Step Further: Building on an Emerging AI Ecosystem</h3><p>The flight-booking scenario reflects a much broader shift happening across the AI ecosystem. Applications are moving from static interactions toward dynamic, personalized, agent-driven experiences where intent, reasoning and UI composition are tightly connected.</p><p>This is where the A2UI approach becomes part of a larger architecture rather than a standalone concept.</p><p>In the current implementation, the demo builds on complementary layers that are increasingly relevant in modern AI systems:</p><ul><li><strong>AI interprets user intent</strong>, translating natural language into structured goals and constraints.</li><li>The <a href="https://learn.microsoft.com/en-us/agent-framework/overview/"><strong>Microsoft Agent Framework</strong></a> <strong>orchestrates reasoning and execution</strong>, coordinating tools, workflows and multi-step decision-making across systems.</li><li><a href="https://docs.ag-ui.com/introduction"><strong>AG-UI</strong></a> <strong>provides the interaction contract for agent-driven interfaces</strong>, so UI intent and structure are expressed in a consistent, agent- and renderer-agnostic way.</li><li><strong>The A2UI layer for Telerik components for Blazor defines the structure of the user experience</strong>, describing how the outcome of that reasoning should be expressed as a dynamic, interactive UI rather than plain text.</li><li><strong>Telerik UI for Blazor renders the final experience</strong>, materializing that structure using enterprise-grade components such as grids, forms and charts with full theming, interactivity and performance.</li></ul><p>Together, these components enable AI systems to move beyond conversational output and participate directly in application workflows. Instead of producing text responses, agents can reason over data and shape the user experience itself.</p><h2 id="telerik-a2ui-renderer-for-blazor—availability-preview">Telerik A2UI Renderer for Blazor&mdash;Availability: Preview</h2><p>A2UI is still an emerging concept and ecosystem. Standards, protocols, component contracts and implementation patterns are actively evolving as the industry explores what dynamic UI generation should look like in production applications.</p><p>We are approaching A2UI with the same mindset. The feature is still experimental. As the implementation is evolving, feedback is actively shaping its direction.</p><p>During this phase, we are actively observing real-world usage patterns, gathering feedback from early adopters and refining how intent translates into UI structure. Specifically, we are exploring where AI-generated UI delivers real value and how much control users need to retain over generated interfaces.</p><p>The <a href="https://a2ui.org/specification/v0.8-a2ui/">A2UI specification</a> itself is at v0.8 stable, and the ecosystem around it&mdash;transport layers like AG-UI, Agent Frameworks from Microsoft (hint: we are exploring those too) and others and renderers for different frameworks&mdash;is all moving quickly. This is the right moment to explore the pattern, give us feedback and understand how the renderer model fits your architecture. And get to the next level of creating great UI and UX experience.</p><p>The current A2UI Preview for Telerik UI for Blazor focuses on a constrained but high-impact set of building blocks:</p><ul><li><strong>Input components</strong> &ndash; text fields, date pickers, numeric inputs, dropdowns</li><li><strong>Data Grid</strong> &ndash; data exploration, visualization and comparison</li><li><strong>Charts</strong> &ndash; visual representations that adapt to what the data warrants</li></ul><p>These are intentionally chosen because they represent the backbone of most business applications. This Preview is <strong>opinionated by design</strong>. Rather than attempting to support every UI pattern, it focuses on exploring how intent can reliably map to structured, usable interfaces. <a href="https://demos.telerik.com/blazor-ui/a2ui/ai-a2ui" target="_blank">Go straight to the demo</a>, click and prompt around on an easy-to-follow use case and get in touch with feedback.</p><p>If your organization is exploring AI-powered applications in 2026, this is the ideal time to start a pilot. Use our <a href="https://www.telerik.com/account/support-center/contact-us">Support system</a> to tell us more about your use case or drop us a line in our <a href="https://feedback.telerik.com/blazor">Blazor Feedback Portal</a> or in the <a href="https://www.telerik.com/forums/blazor">Telerik Forums</a>.</p><p>The goal isn&rsquo;t to replace your existing application. The goal is to explore how AI can augment it through interfaces that adapt to the user, the context and the task at hand.</p><h2 id="webmcp-vs.-a2ui-what’s-the-difference">WebMCP vs. A2UI: What&rsquo;s the Difference?</h2><p>If you&rsquo;ve been following our recent AI initiatives, you may have also seen our <a href="https://www.telerik.com/blazor-ui/documentation/ai/web-mcp/overview">Preview support for WebMCP</a>, which enables AI agents to interact with applications through standardized tools and capabilities.</p><p>While both WebMCP and A2UI are part of the broader movement toward AI-native applications, they address different parts of the big picture.</p><p>You can think of them as complementary layers: WebMCP focuses on how <strong>AI agents interact with applications</strong>. A2UI focuses on how <strong>applications interact with users</strong> through AI-generated experiences.</p><p>Together, they create a powerful foundation for AI-native applications and are an extension of our commitment to great UI and UX.</p><p>With WebMCP, an AI agent can discover available actions, execute workflows, retrieve data and interact with business functionality exposed by an application.</p><p>With A2UI, that same agent can dynamically generate forms, grids, charts, dashboards and other user experiences that help users understand, validate and act on the results.</p><p>An AI agent can use WebMCP to retrieve information, execute actions and understand business capabilities, while A2UI can transform those results into dynamic user experiences built with Telerik UI for Blazor components.</p><p>To learn more about our WebMCP Preview support, check out our blog post: <a href="https://www.telerik.com/blogs/telerik-and-kendo-meet-webmcp">Telerik and Kendo UI Meet WebMCP</a>.</p><p>The current capabilities represent only the beginning. The beauty of dynamic UI generation is that the number of possible use cases is virtually unlimited. AI maps context and intent, while Telerik UI for Blazor provides the components that bring those experiences to life.</p><hr /><p>Want to shape how A2UI support for Telerik UI for Blazor develops? Share your thoughts on the <a href="https://feedback.telerik.com/blazor">Blazor Feedback Portal</a> or in the <a href="https://www.telerik.com/forums/blazor">Telerik forums</a>. We are reading everything.</p><p>And don&rsquo;t forget to <a href="https://www.telerik.com/campaigns/telerik-and-kendo-ui-2026-q2-release-webinar">join the webinar</a> for a live demo of A2UI.</p><img src="https://feeds.telerik.com/link/10827/17361485.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:fc4f18d8-9f5a-4741-bfdd-7844b4bdd7e0</id>
    <title type="text">Exploring the SLNX Solution File Format</title>
    <summary type="text">SLNX files are here! In this post, we'll talk about what this format is, why it exists, what it gets right and where you might get tripped up.</summary>
    <published>2026-06-11T19:09:21Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Dave Brock </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17359672/exploring-slnx-solution-file-format"/>
    <content type="text"><![CDATA[<p><span class="featured">SLNX files are here! In this post, we'll talk about what this format is, why it exists, what it gets right and where you might get tripped up.</span></p><p>If you&rsquo;ve worked with .NET for any length of time, you&rsquo;ve made peace with the <code>.sln</code> file. Not because it&rsquo;s good (it isn&rsquo;t) but because it&rsquo;s the format we have. It&rsquo;s verbose, GUID-laden and a reliable source of merge conflicts on Friday afternoons. It&rsquo;s the format we tolerate, not the one we&rsquo;d choose.</p><p>The good news is there&rsquo;s a new format in town. The <code>.slnx</code> format is Microsoft&rsquo;s XML-based replacement for the venerable <code>.sln</code> file, and it has been steadily gaining first-class support across Visual Studio, the .NET CLI, MSBuild and Rider over the last year. As of <a target="_blank" href="https://devblogs.microsoft.com/dotnet/introducing-slnx-support-dotnet-cli/">.NET 9.0.200</a> and <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/new-simpler-solution-file-format/">Visual Studio 17.13+</a>, you can use it for real projects without crossing your fingers. And <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/dotnet-new-sln-slnx-default">as of .NET 10</a>, <code>dotnet new sln</code> defaults to <code>.slnx</code>.</p><p>In this post, let&rsquo;s talk about what <code>.slnx</code> is, why it exists, what it gets right and where you might get tripped up.</p><h2 id="a-quick-sln-retrospective">A Quick SLN Retrospective</h2><p>To appreciate why we&rsquo;re getting a new format, let&rsquo;s remember what we&rsquo;ve been working with. Here&rsquo;s a typical fragment of an <code>.sln</code> file.</p><pre><code>Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.34804.81
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MyApi", "MyApi\MyApi.csproj", "{F95781B3-A973-4D19-9585-974DA143E6A1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8FC526EA-218B-4615-8410-4E1850611F38}"
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F95781B3-A973-4D19-9585-974DA143E6A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F95781B3-A973-4D19-9585-974DA143E6A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F95781B3-A973-4D19-9585-974DA143E6A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F95781B3-A973-4D19-9585-974DA143E6A1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
</code></pre><p>Oof. The SLN format is a custom Microsoft format that has some weird quirks. Every project gets a GUID. Solution folders are themselves projects with their own GUIDs. Configuration combinations even get cross-multiplied so a four-project solution with two configurations and two platforms produces <em>16 lines</em> of nearly identical setup.</p><p>The pain points are well known to us all. If even two developers add a project at the same time, both edit <code>GlobalSection(ProjectConfigurationPlatforms)</code>, and Git throws its hands up.</p><p>GUIDs, bless their hearts, are not for human eyes. It&rsquo;s a nightmare trying to figure out which project owns which configuration block. And outside of Visual Studio, generating or modifying <code>.sln</code> files reliably often requires <a target="_blank" href="https://microsoft.github.io/slngen/">a tool like <code>slngen</code></a> because no one wants to write the parser themselves.</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">Visual Studio 2026, 6 Months Later</h4></div><div class="col-8"><p class="u-fs16 u-mb0">See what&rsquo;s really behind the <a target="_blank" href="https://www.telerik.com/blogs/visual-studio-2026-6-months-later">Visual Studio 2026 updates</a> and improvements, such as performance, GitHub Copilot, Hot Reload and more.
.</p></div></div><hr class="u-mb3" /></aside><p>This is what the <code>.slnx</code> tries to fix.</p><h2 id="looking-inside-the-.slnx">Looking Inside the .slnx</h2><p>Here&rsquo;s the same project, but expressed as <code>.slnx</code>.</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Solution</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/Solution Items/<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Directory.Build.props<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Folder</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>MyApi/MyApi.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Solution</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>Notice how we have no GUIDs, no cross-multiplied configuration table and no <code>EndGlobalSection</code> markers. Even a developer who is foreign to the <code>.slnx</code> format can read it and immediately understand it.</p><p>Expanding a little, here&rsquo;s an example from a layered solution.</p><pre class=" language-xml"><code class="prism  language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Solution</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/Solution Items/<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>.editorconfig<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Directory.Build.props<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>File</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Directory.Packages.props<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Folder</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/src/<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Application/Application.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Domain/Domain.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Infrastructure/Infrastructure.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>src/Web.Api/Web.Api.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Folder</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Folder</span> <span class="token attr-name">Name</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/tests/<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tests/UnitTests/UnitTests.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Project</span> <span class="token attr-name">Path</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>tests/IntegrationTests/IntegrationTests.csproj<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Folder</span><span class="token punctuation">&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Solution</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>If you compare this to the equivalent <code>.sln</code>, you&rsquo;re looking at maybe a third of the lines, with none of the GUID overhead. If you need to override configurations for a specific project (like, say your test project shouldn&rsquo;t build in Release mode) you can express that with a <code>Configurations</code> element on the project. In reality, most projects won&rsquo;t need it at all. If you do, you can review examples in <a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence"><code>microsoft/vs-solutionpersistence</code></a> and its <a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence/wiki/Samples">samples wiki</a>.</p><p>If the <code>.slnx</code> looks similar to a <code>.csproj</code>, that&rsquo;s intentional. Microsoft&rsquo;s reasoning is that XML is already the format the rest of MSBuild speaks, the team already had a parser for it, and features like comments and attributes are first-class citizens. A JSON or YAML format might have been trendier, but it would have forced the team to invent semantics for things <code>.csproj</code> already has solved problems for.</p><h2 id="migrating-existing-projects">Migrating Existing Projects</h2><p>Existing projects are a different conversation. Here&rsquo;s what you get by migrating, and how to do it cleanly.</p><ul><li><strong>Merge conflicts are more manageable.</strong> Because there are no GUIDs that change every time you touch a project, the diffs you see in PRs are meaningful. If you add a project, you see one line added. Move a project to a different folder, and you see the path change. Nobody has to play GUID detective.</li><li><strong>You can reasonably edit by hand.</strong> You&rsquo;ll never want to hand-edit a <code>.sln</code> unless it&rsquo;s an emergency. With <code>.slnx</code>, opening it in your editor of choice is perfectly reasonable. I&rsquo;ve fixed broken solution layouts in a text editor faster than Visual Studio would have let me click through the dialogs.</li><li><strong>Tooling has standardized what a solution is.</strong> Microsoft has open-sourced the parser as the <a target="_blank" href="https://www.nuget.org/packages/Microsoft.VisualStudio.SolutionPersistence/">Microsoft.VisualStudio.SolutionPersistence NuGet package</a>. This means MSBuild, the .NET CLI and third-party tools are all reading the same file the same way.</li></ul><p>Based on the official <code>dotnet sln</code> reference, the <a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-sln">actual conversion is one command</a>.</p><pre><code>dotnet sln migrate
</code></pre><p>If you run this in a directory with an <code>.sln</code> file, you&rsquo;ll get an <code>.slnx</code> file beside it. From Visual Studio, you can also use <strong>File -&gt; Save Solution As&hellip;</strong> and pick <strong>XML Solution File (.slnx)</strong> from the dropdown. Rider supports a similar flow.</p><p>You don&rsquo;t want to keep both files in the repository. The <code>dotnet sln</code> commands don&rsquo;t reliably pick the right one when you have both, and it&rsquo;s easy for the two to get out of sync as people add or remove projects. Pick when to cut over, delete the old file and move on. While there is <a target="_blank" href="https://github.com/edvilme/dotnet-sln-sync">a community tool</a> called <code>dotnet-sln-sync</code> that helps, that should only be used temporarily and not long term.</p><p>As you convert over, here&rsquo;s a quick checklist to avoid any headaches:</p><ul><li><strong>Verify your <code>global.json</code> allows the right SDK.</strong> You need at least .NET 9.0.200 for full CLI support. If your <code>global.json</code> pins to something older, the migrate command won&rsquo;t work.</li><li><strong>Check your CI build agents.</strong> If you&rsquo;re on self-hosted Azure DevOps agents, for example, confirm they&rsquo;re running an SDK new enough to handle <code>.slnx</code>.</li><li><strong>Audit your pipelines.</strong> Anywhere you have references directly to <code>.sln</code>, update the path. Wildcard references like <code>**/*.sln</code> will miss the new file. If you are using templates where you have a mix of <code>.sln</code> and <code>.slnx</code>, a pattern like <code>**/*.sln*</code> will be useful.</li><li><strong>Look out for dependent tools.</strong> Tools like <code>slngen</code> don&rsquo;t yet support <code>.slnx</code> at the time of this post (you can <a target="_blank" href="https://github.com/microsoft/slngen/issues/643">track the issue here</a>). If you&rsquo;re reliant on a tool outside of the Microsoft umbrella, check before you migrate.</li></ul><h2 id="some-rough-edges">Some Rough Edges</h2><p>The <code>.slnx</code> format is no longer &ldquo;preview&rdquo; in any practical sense, but the ecosystem around it is still catching up.</p><ul><li><strong>Visual Studio file association.</strong> Double-clicking a <code>.slnx</code> file doesn&rsquo;t open Visual Studio by default. You can fix this with a file association or just open it from inside the IDE.</li><li><strong>C# Dev Kit in VS Code.</strong> It works, but you may need to set <a target="_blank" href="https://devblogs.microsoft.com/dotnet/introducing-slnx-support-dotnet-cli/"><code>dotnet.defaultSolution</code></a> to the path of your <code>.slnx</code> if it isn&rsquo;t auto-detected.</li><li><strong>Globbing.</strong> You can&rsquo;t write <code>&lt;Project Path="src/**/*.csproj" /&gt;</code> and have it discover projects automatically. The team&rsquo;s reasoning, captured in the <a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence/issues/61">globbing feature request</a>, is that globbing slows down solution loading on large repos because Visual Studio has to scan the file system before it can render anything. That makes sense, <em>but</em> I think plenty of us would happily trade a slower cold load for never having to add another project entry by hand.</li><li><strong>Third-party tools.</strong> Anything that parses <code>.sln</code> directly needs to update. Hopefully the open-source parser library accelerates that, but you&rsquo;ll find holdouts.</li></ul><p>None of these are dealbreakers for new projects. For older codebases with a lot of tooling baked in, it&rsquo;s worth doing a small pilot before flipping the whole repo.</p><h2 id="wrapping-up">Wrapping Up</h2><p>The <code>.slnx</code> format isn&rsquo;t a revolutionary feature. It doesn&rsquo;t change how you write code, it doesn&rsquo;t add new build capabilities, and it doesn&rsquo;t speed up your application. It is, however, one of those quality-of-life upgrades that quietly results in fewer merge conflicts, less time staring at GUIDs, less friction in your CI pipelines once they&rsquo;re set up.</p><p>If you&rsquo;re starting a new .NET solution, just use <code>.slnx</code> from day one. And with .NET 10, you&rsquo;ll get it without lifting a finger. If you&rsquo;re maintaining an existing <code>.sln</code> file, plan a migration when you have a slow week, audit your tooling and cut over cleanly. The format isn&rsquo;t going anywhere, and getting ahead of the transition is easier than waiting for some downstream tool to force your hand.</p><p>Thanks for reading, and happy coding!</p><h2 id="further-reading">Further Reading</h2><ul><li><a target="_blank" href="https://devblogs.microsoft.com/visualstudio/new-simpler-solution-file-format/">New, simpler solution file format</a></li><li><a target="_blank" href="https://devblogs.microsoft.com/dotnet/introducing-slnx-support-dotnet-cli/">Introducing support for SLNX in the .NET CLI</a></li><li><a target="_blank" href="https://github.com/microsoft/vs-solutionpersistence"><code>microsoft/vs-solutionpersistence</code></a></li><li><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-sln"><code>dotnet sln</code> command reference</a></li><li><a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/10.0/dotnet-new-sln-slnx-default">.NET 10 breaking change: <code>dotnet new sln</code> defaults to SLNX</a></li></ul><img src="https://feeds.telerik.com/link/10827/17359672.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:7ce6c14b-f242-4a6e-8889-98c451e38cbd</id>
    <title type="text">Why You’ve Outgrown Free &amp; OpenSource React UI Libraries</title>
    <summary type="text">Free UI libraries work great… until they don’t. Many teams outgrow them without realizing it—until things start slowing down.</summary>
    <published>2026-06-11T16:44:30Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Hassan Djirdeh </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17359601/why-youve-outgrown-free-open-source-react-ui-libraries"/>
    <content type="text"><![CDATA[<span class="featured"><p>TL;DR:</p><ul><li>Free libraries are great early</li><li>They break at scale</li><li>The cost becomes engineering time, not just licensing</li><li>That&rsquo;s when teams start evaluating alternatives</li></ul></span>
<p>The challenge isn&rsquo;t the license cost.</p><p>The real cost of free UI libraries shows up later&mdash;in engineering time, maintenance overhead, and the effort required to make everything work together at scale.</p><p><strong>Free UI libraries work great&hellip; until they don&rsquo;t.</strong></p><p>And when they start breaking, it&rsquo;s rarely obvious at first. Many teams outgrow them without realizing it&mdash;until things start slowing down.</p><p>Free and open-source UI libraries are a natural starting point for many React projects. They&rsquo;re familiar, community-driven and come with zero upfront cost. For prototypes, MVPs and simpler applications, they often work exactly as expected.</p><p>However, as products grow, their limitations tend to surface gradually. A data grid that handled a few hundred rows starts to slow down at scale. A dependency becomes unmaintained. Different UI pieces begin to behave inconsistently. Similar cracks can appear across other foundational components.</p><p>In this article, we&rsquo;ll explore the signs that your project may have outgrown free UI libraries and how to evaluate whether it&rsquo;s time to consider a more comprehensive solution.</p><h2 id="when-free-ui-libraries-make-sense">When Free UI Libraries Make Sense</h2><p>It&rsquo;s important to note that free UI libraries can be genuinely useful. Libraries like <a target="_blank" href="https://react-bootstrap.github.io/">React Bootstrap</a>, <a target="_blank" href="https://www.radix-ui.com/">Radix</a> and <a target="_blank" href="https://headlessui.com/">Headless UI</a> have made it easier for developers to build interfaces quickly. They lower the barrier to entry, provide solid defaults and benefit from large communities of contributors who improve them over time.</p><p>For prototyping, MVPs and projects where the component requirements are straightforward, free libraries are often the most practical choice. There&rsquo;s no procurement process, no license to manage and the ecosystem familiarity means new team members can ramp up quickly.</p><p>The challenge arises when the project scales, whether in terms of data volume, feature complexity or team size. The limitations that didn&rsquo;t matter at 500 users start to surface at 50,000, and at that point we may need to take additional steps to extend or replace UI components rather than expecting them to scale with us.</p><h2 id="performance-at-scale">Performance at Scale</h2><p>One of the first places teams feel the strain is <strong>performance</strong>. A table component that renders a few hundred rows without issue can become a bottleneck when the data grows into the thousands or tens of thousands. This is a common scenario in enterprise applications where dashboards, admin panels and reporting tools need to handle very large datasets.</p><p>To go into a bit more detail, many free table and grid components render all rows into the DOM at once. When the dataset is small, this is fine, but with very large datasets, users can experience noticeable lag and slow scroll performance.</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token comment">// A basic table rendering all rows directly</span>
<span class="token keyword">const</span> <span class="token function-variable function">BasicTable</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> data <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">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>table</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>thead</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tr</span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span><span class="token punctuation">&gt;</span></span>Name<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span><span class="token punctuation">&gt;</span></span>Email<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>th</span><span class="token punctuation">&gt;</span></span>Status<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>th</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tr</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>thead</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tbody</span><span class="token punctuation">&gt;</span></span>
        <span class="token punctuation">{</span>data<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>row<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>tr</span> <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>row<span class="token punctuation">.</span>id<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>td</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>row<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>row<span class="token punctuation">.</span>email<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>td</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>row<span class="token punctuation">.</span>status<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>td</span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tr</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 tag"><span class="token tag"><span class="token punctuation">&lt;/</span>tbody</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>table</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><p>In the above basic example, once the dataset reaches 5,000 or 10,000 entries, the browser is rendering thousands of DOM nodes simultaneously. This causes scrolling to slow down, filtering to become sluggish and the overall user experience to degrade.</p><aside><hr /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">The Great React Grid-Off: KendoReact vs. MUI vs. AG Grid</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Curious how the best React grids on the market compete head-to-head? <a target="_blank" href="https://www.telerik.com/blogs/great-react-grid-off-kendoreact-vs-mui-vs-ag-grid">KendoReact vs. MUI vs. AG Grid</a>&mdash;let&rsquo;s see it!</p></div></div><hr class="u-mb3" /></aside><p>To address this, teams typically need to implement features like row virtualization, which only renders the rows currently visible in the viewport. Building this from scratch or layering it on top of a free component that wasn&rsquo;t designed for it is where the engineering cost starts to compound. We might leverage a third-party library like <a target="_blank" href="https://github.com/bvaughn/react-virtualized">react-virtualization</a>, or we might end up managing a container ref, a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver">ResizeObserver</a>, absolute positioning and scroll offset calculations ourselves.</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token comment">// pseudo-code</span>
<span class="token keyword">const</span> parentRef <span class="token operator">=</span> <span class="token function">useRef</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 keyword">const</span> virtualizer <span class="token operator">=</span> <span class="token function">useVirtualizer</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  count<span class="token punctuation">:</span> data<span class="token punctuation">.</span>length<span class="token punctuation">,</span>
  getScrollElement<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> parentRef<span class="token punctuation">.</span>current<span class="token punctuation">,</span>
  estimateSize<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token number">36</span><span class="token punctuation">,</span>
  overscan<span class="token punctuation">:</span> <span class="token number">5</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 punctuation">(</span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">ref</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>parentRef<span class="token punctuation">}</span></span> <span class="token attr-name">style</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> height<span class="token punctuation">:</span> <span class="token string">"500px"</span><span class="token punctuation">,</span> overflow<span class="token punctuation">:</span> <span class="token string">"auto"</span> <span class="token punctuation">}</span><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">style</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> height<span class="token punctuation">:</span> virtualizer<span class="token punctuation">.</span><span class="token function">getTotalSize</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> position<span class="token punctuation">:</span> <span class="token string">"relative"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token punctuation">{</span>virtualizer<span class="token punctuation">.</span><span class="token function">getVirtualItems</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>virtualRow<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span>
        <span class="token operator">&lt;</span>div
          key<span class="token operator">=</span><span class="token punctuation">{</span>virtualRow<span class="token punctuation">.</span>key<span class="token punctuation">}</span>
          style<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span>
            position<span class="token punctuation">:</span> <span class="token string">"absolute"</span><span class="token punctuation">,</span>
            top<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
            transform<span class="token punctuation">:</span> <span class="token template-string"><span class="token string">`translateY(</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>virtualRow<span class="token punctuation">.</span>start<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">px)`</span></span><span class="token punctuation">,</span>
            height<span class="token punctuation">:</span> <span class="token template-string"><span class="token string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>virtualRow<span class="token punctuation">.</span>size<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">px`</span></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>data<span class="token punctuation">[</span>virtualRow<span class="token punctuation">.</span>index<span class="token punctuation">]</span><span class="token punctuation">.</span>name<span class="token punctuation">}</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 punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</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 punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>This is just for a concept like row virtualization. We still need to layer on sorting, filtering, grouping, column pinning and keyboard navigation. Each of those features introduces its own state management, edge cases and regression risk. What started as a simple table becomes an internal platform project. At this point, the problem is no longer just performance. It&rsquo;s the engineering time required to implement, test, and maintain these solutions on top of tools that weren&rsquo;t designed for them.</p><h3 id="what-this-looks-like-with-kendoreact">What This Looks Like with KendoReact</h3><p>The <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid/">React Data Grid</a> in the Progress KendoReact component library is built for exactly this scenario. Virtual scrolling (i.e., row virtualization) is enabled by default, meaning the Grid only renders visible rows plus a small buffer. It handles millions of records with constant memory usage, maintains 60 fps scroll performance and keeps the DOM lightweight regardless of dataset size.</p><p><a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid/optimization/column-virtualization">Column virtualization</a> is also available for datasets with many columns, rendering only the columns visible during horizontal scrolling.</p><p>Instead of wiring all of this ourselves, our Grid setup can look as simple as this:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Grid</span>
  <span class="token attr-name">data</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>data<span class="token punctuation">}</span></span>
  <span class="token attr-name">autoProcessData</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span>
    filter<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    sort<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    group<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
    page<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>
  <span class="token attr-name">filterable</span>
  <span class="token attr-name">sortable</span>
  <span class="token attr-name">groupable</span>
  <span class="token attr-name">pageable</span>
<span class="token punctuation">/&gt;</span></span>
</code></pre><p>Sorting, filtering, grouping and paging are all handled through the autoProcessData prop. No manual state wiring and no glue code between separate virtualization, sorting and filtering libraries. With regards to performance, here&rsquo;s a visual example of how the grid behaves when loading and scrolling within a million+ records:</p><p>You can test the performance of the KendoReact Data Grid in the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid/performance">Performance | KendoReact Data Grid documentation</a>.</p><p>Beyond tables, similar performance ceilings can show up in complex components like <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/scheduler">schedulers</a>, <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/treeview">tree views</a> and <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/charts">charts</a> when the volume of data or the complexity of interactions increases.</p><h2 id="maintenance-risk-and-the-support-gap">Maintenance Risk and the Support Gap</h2><p>Fully open-source projects are maintained by people, and people have lives outside of code. Maintainers change jobs, experience burnout, shift their focus to new projects or simply move on. This is completely understandable and is not a criticism of the open-source community, but it is a risk that teams need to account for.</p><p>With the emergence of AI, open-source maintainers now face an additional burden: a growing flood of AI-generated pull requests and bug reports that consume their limited time and energy, <a target="_blank" href="https://github.com/matplotlib/matplotlib/pull/31132#issuecomment-3882240722">as this real GitHub comment from an AI agent illustrates</a>.</p><p>We&rsquo;ve all opened a GitHub repository that looks healthy at first glance, with thousands of stars and active discussions. Then we notice the last meaningful commit was over a year ago. If the library sits at the core of our UI, the options aren&rsquo;t ideal: we can wait and hope it gets attention again, or we can fork it and take on maintenance ourselves.</p><p>This risk compounds when something goes wrong in production. With free libraries, the support path typically looks like this: search GitHub Issues (or create our own), post a question on Stack Overflow or dig through the source code ourselves. These are all viable approaches, but none of them come with a guaranteed response time. Over time, this becomes ongoing maintenance overhead&mdash;time spent tracking issues, adapting to breaking changes or maintaining internal forks instead of building product features.</p><p>Commercial UI libraries like KendoReact <strong>change both sides of this equation</strong>. They have dedicated engineering teams whose job is to maintain, update and improve the components. <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/changelogs/ui-for-react">Release schedules</a> are predictable, breaking changes are documented and there&rsquo;s an organizational commitment to long-term support. When we open a support ticket, a team that knows the codebase intimately is responsible for helping us resolve the issue. This doesn&rsquo;t eliminate debugging entirely, but it significantly reduces the time spent on problems that originate outside our own code.</p><h3 id="the-integration-tax">The Integration Tax</h3><p>A less obvious cost of relying on free libraries is what we might call the integration tax. No single free library covers every component a modern application needs, so teams often end up pulling from multiple sources. A date picker from one library, a data grid from another, form components from a third, a modal dialog from yet another.</p><p>Each of these libraries has its own API patterns, styling approach and theming mechanism. Keeping them all compatible requires effort that isn&rsquo;t always visible in sprint planning.</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token comment">// The import sprawl of mixing multiple UI libraries</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> DatePicker <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"react-datepicker-lib"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> DataGrid <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"another-grid-lib"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Modal <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"yet-another-modal-lib"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Select <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"some-select-lib"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Tabs <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"a-tabs-package"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Tooltip <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"tooltip-library-xyz"</span><span class="token punctuation">;</span>

<span class="token comment">// Each with its own:</span>
<span class="token comment">// - Styling system (CSS modules, styled-components, plain CSS, Tailwind)</span>
<span class="token comment">// - Event handling patterns</span>
<span class="token comment">// - TypeScript type definitions (or lack thereof)</span>
<span class="token comment">// - Breaking change policies</span>
</code></pre><p>This illustrates the integration tax: the hidden engineering effort required to make multiple tools behave like a cohesive system, and to keep them that way over time.</p><p>The above is a very simplified illustration, but the pattern can be understood. When different UI packages and tooling each have their own way of handling themes, events and styling, the glue code needed to make them work together becomes maintenance of its own.</p><p>A <a target="_blank" href="https://www.telerik.com/kendo-react-ui">comprehensive enterprise-ready React UI library</a>, by comparison, provides a single API surface, a unified theming system and components that are designed to work together from the start. The date picker, the grid, the forms, the dialogs, the scheduler are all built by the same team, tested together and styled consistently.</p><h2 id="accessibility-and-compliance-at-scale">Accessibility and Compliance at Scale</h2><p>Accessibility is increasingly moving from a &ldquo;nice to have&rdquo; to a legal requirement. The <a target="_blank" href="https://www.telerik.com/blogs/what-does-european-accessibility-act-mean-developers">European Accessibility Act</a>, which went into effect on June 28, 2025, makes accessibility legally required for digital products used in the EU. In the United States, <a target="_blank" href="https://www.telerik.com/blogs/hidden-costs-inaccessible-apps-enterprises-how-avoid">accessibility lawsuits continue to rise</a>, targeting businesses of all sizes.</p><p>Many free UI libraries include some level of accessibility support, but the depth and consistency of that support vary widely. ARIA attributes might be present on some components but missing on others. Keyboard navigation might work for basic interactions, but break down in more complex flows and screen reader support might be untested or incomplete.</p><p>For teams that need to meet <a target="_blank" href="https://www.w3.org/TR/WCAG22/">WCAG 2.2</a> or <a target="_blank" href="https://www.w3.org/TR/WCAG21/">WCAG 2.1</a> compliance, this means auditing every component, identifying gaps and writing custom code to fill them. That audit and remediation process can be substantial, especially when dealing with complex interactive components like grids, trees and menus, where the accessibility requirements are non-trivial.</p><p>Commercial UI libraries that treat accessibility as a first-class concern build it into the component architecture from the beginning. This includes proper ARIA roles and attributes, keyboard navigation, focus management, high-contrast theme support and screen reader compatibility across components.</p><p>The cost of getting this right is absorbed by the library team rather than being passed on to every application team that uses the components. For KendoReact, as an example, <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/accessibility">accessibility compliance is a strategic and ongoing commitment</a>.</p><h2 id="when-do-we-outgrow-free-ui-libraries">When Do We Outgrow Free UI Libraries?</h2><p>Free UI libraries serve an important role in the React ecosystem. They lower barriers, accelerate early development and provide solid building blocks for countless projects. Choosing them at the start of many projects can be the right decision.</p><p>KendoReact also includes a <a target="_blank" href="https://www.telerik.com/kendo-react-ui/free-react-components">Free tier</a> with 50+ production-ready React components available for use without a license.</p><p>However, projects evolve, and tools that worked well at one stage can become sources of friction at another. You may have outgrown free UI libraries if:</p><ul><li>You&rsquo;re spending time implementing features like virtualization or complex state handling yourself.</li><li>You&rsquo;re maintaining forks of third-party components.</li><li>You&rsquo;re integrating multiple libraries with different APIs and styling systems.</li><li>Accessibility or compliance requires additional engineering effort.</li><li>Fixing UI issues depends on external maintainers or community responses.</li></ul><p>The question isn&rsquo;t whether free libraries are good or bad. It&rsquo;s whether the total cost of using them, including developer time for workarounds, is still better than the alternative. Teams shifts from asking &ldquo;Why pay for UI components?&rdquo; to &ldquo;What is the cost of continuing to build and maintain this ourselves?&rdquo;</p><p>For teams that have reached that stage, it&rsquo;s worth stepping back and evaluating the options more deliberately, including what a more integrated, enterprise-ready approach can offer in terms of performance, consistency and long-term reliability. This is where robust professional libraries like <a target="_blank" href="https://www.telerik.com/kendo-react-ui">KendoReact</a> step in, offering production-ready components, built-in performance optimizations, and a unified system that reduces the need for custom integrations and ongoing maintenance work.</p><p>For additional details on KendoReact and what it offers, check out the following resources:</p><ul><li><a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/">KendoReact Examples &amp; Demos</a></li><li><a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/accessibility">KendoReact Accessibility</a></li><li><a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid/performance">KendoReact Data Grid Performance</a></li><li><a target="_blank" href="https://www.telerik.com/themebuilder">KendoReact ThemeBuilder</a></li></ul><img src="https://feeds.telerik.com/link/10827/17359601.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:9c42fac5-42a2-417f-b5a6-e359bcaadc44</id>
    <title type="text">SCIM Provisioning for DevCraft Subscriptions: Automating License Management for Your Teams</title>
    <summary type="text">Progress DevCraft Complete and DevCraft Ultimate subscription licenses just got a whole lot easier to manage across your team with SCIM provisioning.</summary>
    <published>2026-06-10T18:13:57Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Dragan Grigorov </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17357629/scim-provisioning-telerik-kendo-ui-licenses"/>
    <content type="text"><![CDATA[<p><span class="featured">Progress DevCraft Complete and DevCraft Ultimate subscription licenses just got a whole lot easier to manage across your team with SCIM provisioning.</span></p><p>Managing access across growing development teams can quickly become time-consuming when done manually.</p><p>That&rsquo;s why we&rsquo;re introducing <strong>System for Cross-domain Identity Management (SCIM)</strong> for Progress DevCraft Complete and DevCraft Ultimate subscription licenses, along with Fiddler and ThemeBuilder Enterprise licenses&mdash;bringing automated license seat management directly into your organization&rsquo;s Identity Provider.</p><p>If <strong>SSO simplified how your team signs in</strong>, SCIM extends that experience by <strong>automating how access is assigned and maintained</strong>.</p><p> If you haven&rsquo;t set up SSO yet, follow the steps in our <a target="_blank" href="https://www.telerik.com/blogs/sso-telerik-kendo-ui-simpler-more-secure-access-account">SSO guide</a>.</p><h2 id="what-is-scim-and-why-it-matters">What Is SCIM and Why It Matters</h2><p>For customers using <strong>DevCraft Complete and Ultimate subscription licenses</strong>, as well as <strong>Fiddler</strong> and <strong>ThemeBuilder Enterprise licenses</strong>, SCIM allows license access to be managed directly through your organization&rsquo;s Identity Provider (IdP), such as Microsoft Entra ID or Okta.</p><p>Instead of managing users separately in Telerik, access becomes part of your existing internal processes. When developers are assigned to the right group in your IdP, they automatically receive access to their DevCraft subscription. When they are removed, their access is updated accordingly.</p><p>This helps organizations:</p><ul><li>Keep license access aligned with internal systems</li><li>Eliminate manual seat management</li><li>Reduce the risk of unused or outdated assignments</li></ul><p> Your Identity Provider becomes the <strong>single place to manage access</strong> to your licenses.</p><h2 id="how-to-set-it-up">How to Set It Up</h2><p>SCIM builds on top of SSO and is designed to be quick to configure.</p><h3 id="enable-sso">1. Enable SSO</h3><p>SCIM requires SSO to be already configured for your domain.</p><p>If you haven&rsquo;t enabled it yet, follow the steps in our <a target="_blank" href="https://www.telerik.com/blogs/sso-telerik-kendo-ui-simpler-more-secure-access-account">SSO guide</a>.</p><h3 id="open-scim-setup-in-your-telerik-account">2. Open SCIM Setup in Your Telerik Account</h3><ul><li>Go to <a target="_blank" href="https://www.telerik.com/account/sso-management"><strong>Manage SSO and SCIM</strong></a> in your Telerik account.</li><li>Click on <strong>SCIM Setup</strong>.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/scim-setup.png?sfvrsn=5f9e7cc_2" alt="Configure SSO & SCIM: SCIM Setup" /></p><h3 id="generate-your-scim-key">3. Generate Your SCIM Key</h3><p>In the SCIM Setup screen:</p><ul><li>Copy the SCIM URL and use it when configuring your Identity Provider.</li><li>Add a Key Note that should help differentiate your keys in the future.</li><li>Generate and copy the <strong>SCIM API key</strong> and <strong>Finish Setup</strong></li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/generate-scim-key.png?sfvrsn=8870d1e8_2" alt="SCIM URL, Key Note, Key" /></p><blockquote><p>⚠️ <strong>Important:</strong> Please copy the key, as once the setup is complete, you will no longer have access to the full key.</p></blockquote><p>The generated SCIM API Key will be used as a Bearer authentication token in your IdP.</p><h3 id="configure-scim-in-your-identity-provider">4. Configure SCIM in Your Identity Provider</h3><p>This step connects your Identity Provider with Telerik, allowing it to send user and group updates automatically.</p><p>In your Identity Provider (for example, Okta):</p><ul><li>Add the Telerik SCIM URL.</li><li>Use the generated API key for authentication.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/scim-provision-integration.png?sfvrsn=7651526f_2" alt="Okta window for Telerik SCIM Test App. Provisioning, Integration tab. SCIM 2.0 Base Url, OAuth Bearer Token" /></p><ul><li>Enable provisioning&mdash;verify the Create, Update and Deactivate are enabled.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/okta-scim-provision.png?sfvrsn=59e4ccad_2" alt="Okta window for Telerik SCIM Test App. Provisioning, To App tab. Okta to SCIM. Create users, update user attributes, deactivate users are enabled" /></p><h3 id="map-your-devcraft-license-to-your-idp-group">5. Map Your DevCraft License to Your IdP Group</h3><p>In <a target="_blank" href="https://www.telerik.com/account/sso-management">Manage SSO &amp; SCIM</a> in Your Account:</p><ul><li>Identify your DevCraft Complete or Ultimate subscription license that you want to enable the SCIM feature, and click on <strong>Configure</strong>.</li><li><strong>Enable SCIM Provisioning</strong>.</li><li>Copy the <strong>IdP Group Identifier</strong> and <strong>Apply</strong>.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/configure-sso-scim-provisioning.png?sfvrsn=73394aee_2" alt="Configure SSO & SCIM Provisioning for License Name" /></p><p> From this point on, access is controlled through group membership.</p><ul><li>Use the IdP Group Identifier to <strong>create a group with the same name in your IdP Provider</strong> and assign that group to the SCIM App. Optionally, add a group description as a human-readable name in the IdP.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/okta-group-idp-provider.png?sfvrsn=4af0484b_2" alt="Okta window for Telerik SCIM Test App. Assignments, Groups. Subscription key." /></p><ul><li>You can start adding users to the IdP group. For a start, if there are already seated users on the Telerik side, add those users to the IdP group as well. From there on, any IdP group change will be reflected on the Telerik side.</li></ul><h3 id="verify-user-synchronization">6. Verify User Synchronization</h3><p>After completing the setup, you can verify that the configuration is correct and that users are syncing as expected from the <a target="_blank" href="https://www.telerik.com/account/manage-licensed-users/product-list">Licensed Users</a> view.</p><ul><li>Users added to the mapped IdP group will start appearing automatically.</li><li>Existing users (with Telerik accounts) should show as <strong>Assigned</strong>, and can continue signing in via SSO as before.</li><li>New users (without a Telerik account) will appear with an <strong>Invited</strong> status. They will receive an email invitation. They need to complete their account setup on their first login.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/devcraft-complete-subscription-developer-list.png?sfvrsn=b22a1a8e_2" alt="Licensed Users page. DevCraft Complete Subscriptions. List of developers with states invited or assigned." /></p><p> This handles both new and existing users seamlessly.</p><h2 id="what-happens-after-scim-is-enabled">What Happens After SCIM Is Enabled?</h2><p>Once SCIM is enabled for your license, access management becomes fully automated.</p><p>User access is driven entirely by group membership in your Identity Provider, while Telerik reflects those changes automatically. New users are guided through a simple onboarding experience at login, and the Licensed Users view becomes read-only for visibility and reporting.</p><p> There&rsquo;s no need to manually manage seats in Telerik.-Your Identity Provider handles all updates.</p><h2 id="bring-automated-access-to-your-telerik-licenses">Bring Automated Access to Your Telerik Licenses</h2><p>SCIM makes it easier to manage access to your licenses by extending your existing identity management into license management.</p><p>Once combined with SSO, it provides a smooth experience where both authentication and access are handled together&mdash;directly from your Identity Provider.</p><p>Whether you&rsquo;re looking to reduce manual work, keep access aligned with your organization or scale more efficiently as your team grows, SCIM is designed to help you get there!</p><p>Head to the <a target="_blank" href="https://www.telerik.com/account/sso-management">Manage SSO and SCIM</a> page now to get started.</p><img src="https://feeds.telerik.com/link/10827/17357629.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:e793d386-96e8-4722-9af0-282b11768a2d</id>
    <title type="text">Practicing Vertical Slice Architecture in ASP.NET Core</title>
    <summary type="text">Tired of organizing code through technical layers? How about going beyond the basics and “slicing” the system? Vertical Slice Architecture structures the application not by the type of code to be written, but by the problem to be solved. Let’s learn how to implement it in ASP.NET Core.</summary>
    <published>2026-06-09T20:10:26Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Assis Zang </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17357138/practicing-vertical-slice-architecture-aspnet-core"/>
    <content type="text"><![CDATA[<p><span class="featured">Tired of organizing code through technical layers? How about going beyond the basics and &ldquo;slicing&rdquo; the system? Vertical Slice Architecture structures the application not by the type of code to be written, but by the problem to be solved. Let&rsquo;s learn how to implement it in ASP.NET Core.</span></p><p>If you&rsquo;ve ever created an API in ASP.NET Core using the classic approach of dividing it into Controllers, Services and Repositories, you&rsquo;ve probably faced some difficulties in creating the necessary structure. With each new feature, more files are scattered, more cross-dependencies appear, and it becomes more difficult to understand what truly belongs to each use case.</p><p>But what if, instead of organizing the code by technical layers, you organized it by system functionalities? That&rsquo;s exactly what Vertical Slice Architecture proposes.</p><p>Instead of a giant folder for Services and another for Repositories, each functionality becomes a vertical &ldquo;slice&rdquo; of the system, containing everything it needs: endpoint, business rules, validations, DTOs and data access. Each request then has its own isolated flow.</p><p>In this post, we&rsquo;ll explore Vertical Slice Architecture and how to apply it in a practical ASP.NET Core project.</p><h2 id="layered-architecture-and-its-trade-offs">Layered Architecture and Its Trade-offs</h2><p>Layered Architecture is one of the best-known and most widely used architectural styles in enterprise software development. It&rsquo;s commonly found in ASP.NET Core, Java/Spring and other enterprise platforms. The main point of this style is that it organizes the system into horizontal layers, each with well-defined responsibilities.</p><p>The classic architecture typically divides the system into four main layers (despite variations):</p><ol><li>Presentation (UI / API): Controllers, endpoints, user interfaces</li><li>Application / Service: Orchestrates use cases</li><li>Domain / Business: Business rules</li><li>Infrastructure / Data Access: Database, external integrations, files, etc.</li></ol><p>Each layer depends only on the layer immediately below it (or on abstractions), creating a predictable flow of dependencies.</p><p>Despite its popularity, this approach has some drawbacks, such as flow coupling. Although the layers are separate, use cases traverse all of them: Controller -&gt; Service -&gt; Domain -&gt; Repository -&gt; Database. This creates structural flow coupling.</p><p>A simple change in one use case can require changes in multiple layers. This results in scattered files and constant navigation between projects/folders to find important parts of the code.</p><p>Another negative point is that this architecture organizes code by technical type, not by functionality. For example, if you want to understand the &ldquo;Create Order&rdquo; use case, you will need to open multiple files in different folders (Controllers, Services folder, Repositories folder, etc.).</p><p>When can Layered Architecture start to become a problem? In large systems with many use cases, a high volume of changes per feature, a complex domain that requires strong modeling, and large teams working on multiple fronts. In scenarios like this, an excellent alternative is Vertical Slice Architecture.</p><h2 id="vertical-slice-architecture-as-an-alternative">Vertical Slice Architecture as an Alternative</h2><p>Vertical Slice Architecture is an architectural style where the system is designed and organized by features (functionalities), rather than by technical layers as in traditional layer-based approaches.</p><p>Vertical Slice emerged as an alternative to problems with traditional approaches, such as the difficulty in maintenance due to classes being scattered throughout the program.</p><p>The proposal of Vertical Slice is simple: organize the code by functionality (feature) and not by technical type. Imagine you need to create functionalities for creating and canceling an order.</p><p>In a layered approach, we would have the standard structure:</p><pre><code>Controller 
- OrderController 
Service 
- OrderService
Repository 
- OrderRepository
Dtos 
- OrderDto
</code></pre><p>In a Vertical Slice Architecture, you would organize things by use case:</p><pre><code>Features/
CreateOrder/
CreateOrderEndpoint.cs
CreateOrderHandler.cs
CreateOrderRequest.cs
CreateOrderValidator.cs

CancelOrder/
CancelOrderEndpoint.cs
CancelOrderHandler.cs
CancelOrderRequest.cs
CancelOrderValidator.cs
</code></pre><p>Much clearer now, isn&rsquo;t it? The image below shows a simple comparison between Vertical Slice Architecture and Layered Architecture:</p><p><img title="vertical slice vs layered" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/vertical-slice-vs-layered.png?sfvrsn=743f49ce_2" alt="Vertical Slice VS Layered" /></p><h2 id="implementing-vertical-slice-architecture">Implementing Vertical Slice Architecture</h2><p>Now we&rsquo;re going to create an API using Vertical Slice Architecture. Our API will be used to manage a subscription service. You can access the complete code in this GitHub repository: <a target="_blank" href="https://github.com/zangassis/vertical-slice-subs">Vertical Slice Subs Source Code</a>.</p><p>Our project will follow this structure:</p><p><img title="project structure" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/project-structure.png?sfvrsn=10deb58f_2" alt="Project structure" /></p><h3 id="creating-the-domain-model">Creating the Domain Model</h3><p>To create the base application, you can use the following command in the terminal:</p><pre class=" language-bash"><code class="prism  language-bash">dotnet new web -o SubscriptionManagement
</code></pre><p>Then, inside the project, create a new folder called &ldquo;Features,&rdquo; and inside it another folder called &ldquo;Subscriptions&rdquo;. Add the following class and enum to the folder Subscriptions:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Subscription</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 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 keyword">public</span> <span class="token keyword">string</span> ServiceName <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">default</span><span class="token operator">!</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">decimal</span> Price <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
    <span class="token keyword">public</span> DateOnly StartDate <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> DateOnly<span class="token operator">?</span> TrialEndsAt <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> SubscriptionStatus 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 keyword">private</span> <span class="token function">Subscription</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 function">Subscription</span><span class="token punctuation">(</span><span class="token keyword">string</span> serviceName<span class="token punctuation">,</span> <span class="token keyword">decimal</span> price<span class="token punctuation">,</span> DateOnly startDate<span class="token punctuation">,</span> DateOnly<span class="token operator">?</span> trialEndsAt<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        ServiceName <span class="token operator">=</span> serviceName<span class="token punctuation">;</span>
        Price <span class="token operator">=</span> price<span class="token punctuation">;</span>
        StartDate <span class="token operator">=</span> startDate<span class="token punctuation">;</span>
        TrialEndsAt <span class="token operator">=</span> trialEndsAt<span class="token punctuation">;</span>
        Status <span class="token operator">=</span> SubscriptionStatus<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">Cancel</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> SubscriptionStatus<span class="token punctuation">.</span>Canceled<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">"Subscription already canceled."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        Status <span class="token operator">=</span> SubscriptionStatus<span class="token punctuation">.</span>Canceled<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">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">enum</span> SubscriptionStatus
<span class="token punctuation">{</span>
    Active <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">,</span>
    Canceled <span class="token operator">=</span> <span class="token number">2</span>
<span class="token punctuation">}</span>
</code></pre><p>The next step is to create the database related files. In the root of the project, create a new folder called &ldquo;Infrastructure&rdquo; and add the following class to it:</p><ul><li>AppDbContext</li></ul><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>
<span class="token keyword">using</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Infrastructure<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>Subscription<span class="token operator">&gt;</span> Subscriptions <span class="token operator">=</span><span class="token operator">&gt;</span> <span class="token generic-method function">Set<span class="token punctuation">&lt;</span>Subscription<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 punctuation">}</span>
</code></pre><h2 id="-feature-1-createsubscription"> Feature 1: CreateSubscription</h2><h3 id="command">Command</h3><p>Our first feature will be used to create a new subscription. Inside the <code>Features/Subscriptions/</code> folder, create a new folder called &ldquo;CreateSubscriptions&rdquo; (all the classes below should be created within this structure: <code>Features/Subscriptions/CreateSubscriptions</code>), and inside it, add the following record:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> record <span class="token function">CreateSubscriptionCommand</span><span class="token punctuation">(</span>
    <span class="token keyword">string</span> ServiceName<span class="token punctuation">,</span>
    <span class="token keyword">decimal</span> Price<span class="token punctuation">,</span>
    DateOnly StartDate<span class="token punctuation">,</span>
    DateOnly<span class="token operator">?</span> TrialEndsAt
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h3 id="validator">Validator</h3><p>Next, let&rsquo;s create a validator. Create the class below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> FluentValidation<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CreateSubscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CreateSubscriptionValidator</span> <span class="token punctuation">:</span> AbstractValidator<span class="token operator">&lt;</span>CreateSubscriptionCommand<span class="token operator">&gt;</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token function">CreateSubscriptionValidator</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token function">RuleFor</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>ServiceName<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">NotEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">MaximumLength</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 function">RuleFor</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>Price<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">GreaterThan</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 function">RuleFor</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>TrialEndsAt<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">GreaterThan</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>StartDate<span class="token punctuation">)</span>
            <span class="token punctuation">.</span><span class="token function">When</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>TrialEndsAt<span class="token punctuation">.</span>HasValue<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="handler">Handler</h3><p>The concept of a Handler is common in projects that use Vertical Slice Architecture. A Handler is the component responsible for executing the application&rsquo;s use case&mdash;that is, it contains the logic of the functionality.</p><p>Typically, the Handler is the class or function that receives a command or query and executes the corresponding operation, coordinating business rules, database access, calls to other services and response return.</p><p>Handlers work well in architectures that use Command Query Responsibility Segregation (CQRS), especially when using MediatR. Here, the concept doesn&rsquo;t depend on MediatR; it already exists simply with the organization by slices.</p><p>Now, let&rsquo;s create our first Handler. To do this, create the classes below:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CreateSubscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">CreateSubscriptionCommand</span><span class="token punctuation">(</span>
    <span class="token keyword">string</span> ServiceName<span class="token punctuation">,</span>
    <span class="token keyword">decimal</span> Price<span class="token punctuation">,</span>
    DateOnly StartDate<span class="token punctuation">,</span>
    DateOnly<span class="token operator">?</span> TrialEndsAt
<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> SubscriptionManagement<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CreateSubscriptions<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">CreateSubscriptionHandler</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 operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">Handle</span><span class="token punctuation">(</span>CreateSubscriptionCommand command<span class="token punctuation">,</span> AppDbContext dbContext<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> subscription <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Subscription</span><span class="token punctuation">(</span>
            command<span class="token punctuation">.</span>ServiceName<span class="token punctuation">,</span>
            command<span class="token punctuation">.</span>Price<span class="token punctuation">,</span>
            command<span class="token punctuation">.</span>StartDate<span class="token punctuation">,</span>
            command<span class="token punctuation">.</span>TrialEndsAt
        <span class="token punctuation">)</span><span class="token punctuation">;</span>

        dbContext<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span><span class="token function">Add</span><span class="token punctuation">(</span>subscription<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">await</span> dbContext<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">return</span> Results<span class="token punctuation">.</span><span class="token function">Created</span><span class="token punctuation">(</span>$<span class="token string">"/subscriptions/{subscription.Id}"</span><span class="token punctuation">,</span> subscription<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 the handler class has a method to create a new subscription, and returns an HTTP <code>Created</code> status.</p><h3 id="endpoint">Endpoint</h3><p>In Vertical Slice Architecture, it&rsquo;s also common to declare separate endpoints. In this case, we&rsquo;ll have one endpoint for each use case. Create the following class:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> FluentValidation<span class="token punctuation">;</span>
<span class="token keyword">using</span> SubscriptionManagement<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CreateSubscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CreateSubscriptionEndpoint</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">Map</span><span class="token punctuation">(</span>WebApplication app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        app<span class="token punctuation">.</span><span class="token function">MapPost</span><span class="token punctuation">(</span><span class="token string">"/subscriptions"</span><span class="token punctuation">,</span> 
            <span class="token keyword">async</span> <span class="token punctuation">(</span>CreateSubscriptionCommand command<span class="token punctuation">,</span> 
                AppDbContext dbContext<span class="token punctuation">,</span> 
                IValidator<span class="token operator">&lt;</span>CreateSubscriptionCommand<span class="token operator">&gt;</span> validator<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">var</span> validation <span class="token operator">=</span> <span class="token keyword">await</span> validator<span class="token punctuation">.</span><span class="token function">ValidateAsync</span><span class="token punctuation">(</span>command<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>validation<span class="token punctuation">.</span>IsValid<span class="token punctuation">)</span>
                    <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">ValidationProblem</span><span class="token punctuation">(</span>validation<span class="token punctuation">.</span><span class="token function">ToDictionary</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 keyword">await</span> CreateSubscriptionHandler<span class="token punctuation">.</span><span class="token function">Handle</span><span class="token punctuation">(</span>command<span class="token punctuation">,</span> dbContext<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>Here we are defining an endpoint for creating a new subscription. It starts by validating the data, and if it&rsquo;s valid, it uses the handler to create the new record and return the expected status.</p><h2 id="-feature-2-cancelsubscription"> Feature 2: CancelSubscription</h2><p>The subscription creation flow is ready. Now let&rsquo;s consider the cancellation use case. Following the same logic as before, within the <code>Features/Subscriptions</code> structure, create a new folder called <code>CancelSubscriptions</code> and add the following classes to that folder:</p><h3 id="command-1">Command</h3><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CancelSubscriptions<span class="token punctuation">;</span>

<span class="token keyword">public</span> record <span class="token function">CancelSubscriptionCommand</span><span class="token punctuation">(</span>Guid Id<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><h3 id="handler-1">Handler</h3><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> Microsoft<span class="token punctuation">.</span>EntityFrameworkCore<span class="token punctuation">;</span>
<span class="token keyword">using</span> SubscriptionManagement<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CancelSubscriptions<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">CancelSubscriptionHandler</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 operator">&lt;</span>IResult<span class="token operator">&gt;</span> <span class="token function">Handle</span><span class="token punctuation">(</span>
        CancelSubscriptionCommand command<span class="token punctuation">,</span>
        AppDbContext dbContext<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        <span class="token keyword">var</span> subscription <span class="token operator">=</span> <span class="token keyword">await</span> dbContext<span class="token punctuation">.</span>Subscriptions
            <span class="token punctuation">.</span><span class="token function">FirstOrDefaultAsync</span><span class="token punctuation">(</span>x <span class="token operator">=</span><span class="token operator">&gt;</span> x<span class="token punctuation">.</span>Id <span class="token operator">==</span> command<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>subscription <span class="token keyword">is</span> <span class="token keyword">null</span><span class="token punctuation">)</span>
            <span class="token keyword">return</span> Results<span class="token punctuation">.</span><span class="token function">NotFound</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">try</span>
        <span class="token punctuation">{</span>
            subscription<span class="token punctuation">.</span><span class="token function">Cancel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">await</span> dbContext<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">InvalidOperationException</span> ex<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 keyword">new</span> <span class="token punctuation">{</span> error <span class="token operator">=</span> ex<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 keyword">return</span> Results<span class="token punctuation">.</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><h3 id="endpoint-1">Endpoint</h3><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> SubscriptionManagement<span class="token punctuation">.</span>Infrastructure<span class="token punctuation">;</span>

<span class="token keyword">namespace</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CancelSubscriptions<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">CancelSubscriptionEndpoint</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">Map</span><span class="token punctuation">(</span>WebApplication app<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        app<span class="token punctuation">.</span><span class="token function">MapDelete</span><span class="token punctuation">(</span><span class="token string">"/subscriptions/{id:guid}"</span><span class="token punctuation">,</span>
            <span class="token keyword">async</span> <span class="token punctuation">(</span>Guid id<span class="token punctuation">,</span> AppDbContext dbContext<span class="token punctuation">)</span> <span class="token operator">=</span><span class="token operator">&gt;</span>
            <span class="token punctuation">{</span>
                <span class="token keyword">var</span> command <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">CancelSubscriptionCommand</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 keyword">await</span> CancelSubscriptionHandler<span class="token punctuation">.</span><span class="token function">Handle</span><span class="token punctuation">(</span>command<span class="token punctuation">,</span> dbContext<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><h3 id="program-class">Program Class</h3><p>The final step is to configure the Program class to use the endpoints we created above, to save time, we&rsquo;ll use an in-memory database. So add the following code to the Program class:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">using</span> FluentValidation<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> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CancelSubscriptions<span class="token punctuation">;</span>
<span class="token keyword">using</span> SubscriptionManagement<span class="token punctuation">.</span>Features<span class="token punctuation">.</span>Subscriptions<span class="token punctuation">.</span>CreateSubscriptions<span class="token punctuation">;</span>
<span class="token keyword">using</span> SubscriptionManagement<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>

builder<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>opt <span class="token operator">=</span><span class="token operator">&gt;</span>
    opt<span class="token punctuation">.</span><span class="token function">UseInMemoryDatabase</span><span class="token punctuation">(</span><span class="token string">"subscriptions-db"</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">AddValidatorsFromAssemblyContaining<span class="token punctuation">&lt;</span>CreateSubscriptionValidator<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>

CreateSubscriptionEndpoint<span class="token punctuation">.</span><span class="token function">Map</span><span class="token punctuation">(</span>app<span class="token punctuation">)</span><span class="token punctuation">;</span>
CancelSubscriptionEndpoint<span class="token punctuation">.</span><span class="token function">Map</span><span class="token punctuation">(</span>app<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>Note that the Program class is quite simple, only creating configurations such as the use of the in-memory database, validations and the calling of the endpoint methods: <code>CreateSubscriptionEndpoint.Map(app);</code> and <code>CancelSubscriptionEndpoint.Map(app);</code>.</p><h2 id="-testing-the-application"> Testing the Application</h2><p>Now, let&rsquo;s run the endpoints and verify that the application is functional.</p><p>Run the application and make a POST request to the route <code>http://localhost:5127/subscriptions</code> using the JSON below as the body:</p><pre class=" language-json"><code class="prism  language-json"><span class="token punctuation">{</span>
  <span class="token string">"serviceName"</span><span class="token punctuation">:</span> <span class="token string">"Aspflix"</span><span class="token punctuation">,</span>
  <span class="token string">"price"</span><span class="token punctuation">:</span> <span class="token number">39.90</span><span class="token punctuation">,</span>
  <span class="token string">"startDate"</span><span class="token punctuation">:</span> <span class="token string">"2026-03-01"</span><span class="token punctuation">,</span>
  <span class="token string">"trialEndsAt"</span><span class="token punctuation">:</span> <span class="token string">"2026-03-15"</span>
<span class="token punctuation">}</span>
</code></pre><p>If everything goes well, you will receive the following response in a debugger like Progress Telerik <a target="_blank" href="https://www.telerik.com/fiddler/fiddler-everywhere">Fiddler Everywhere</a>:</p><p><img title="create a subscription" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/create-a-subscription.png?sfvrsn=29b0495f_2" alt="Create a subscription" /></p><p>And to test the cancellation, simply execute a DELETE request to the route: <code>http://localhost:5127/subscriptions/ID_HERE</code>.</p><p>And you will receive the following response:</p><p><img title="cancel a subscription" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/cancel-a-subscription.png?sfvrsn=7b047aec_2" alt="Cancel a subscription" /></p><p>If you try to cancel again, you will receive a validation error.</p><p><img title="cancel a subscription error" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/cancel-a-subscription-error.png?sfvrsn=197162f9_2" alt="Cancel a subscription error" /></p><h2 id="conclusion">Conclusion</h2><p>Vertical Slice Architecture offers an alternative approach focused on system functionalities, rather than predefined structures.</p><p>As we saw in the article, this brings advantages such as ease of maintenance, due to the low cognitive requirements for understanding the project. It also offers agility in finding functions, as business rules are separated by use case. Furthermore, we implemented a subscription project in ASP.NET Core using Slice Architecture.</p><p>I hope this post helps you decide which architectural approach to use when creating or refactoring your 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">Creating More Realistic Tests with In-Memory Databases in ASP.NET Core
</h4></div><div class="col-8"><p class="u-fs16 u-mb0"><a target="_blank" href="https://www.telerik.com/blogs/creating-more-realistic-tests-memory-databases-aspnet-core">Testing ASP.NET Core APIs with in-memory</a> SQLite and JustMock enables validation of real-world scenarios like pagination, keys and rules without a real database.</p></div></div></aside><img src="https://feeds.telerik.com/link/10827/17357138.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:ab0667e5-0b14-47dd-ac74-880d6313ec8e</id>
    <title type="text">Data Fetching with TanStack Query for Vue</title>
    <summary type="text">Learn how to set up TanStack Query in a Vue app, fetch data with useQuery, leverage automatic caching and control refetching behavior.</summary>
    <published>2026-06-09T17:26:45Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Marina Mosti </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17357066/data-fetching-tanstack-query-vue"/>
    <content type="text"><![CDATA[<p><span class="featured">Learn how to set up TanStack Query in a Vue app, fetch data with useQuery, leverage automatic caching and control refetching behavior.</span></p><p>If you have been building Vue applications for any amount of time, you&rsquo;ve likely run into the same pattern over and over: call <code>fetch</code> or <code>axios</code> inside <code>onMounted</code>, store the result in a <code>ref</code>, manually track whether the request is loading, handle errors, and then figure out when and how to refetch the data when it goes stale. It works, but it doesn&rsquo;t scale well, and it quickly becomes a mess of boilerplate scattered across your components.</p><p>In my previous article on <a target="_blank" href="https://www.telerik.com/blogs/vue-basics-pinia-state-management">Pinia state management</a>, I mentioned TanStack Query as a powerful tool for managing asynchronous state that comes from your API. In this article, we&rsquo;ll explore how to set it up in a Vue app, fetch data with <code>useQuery</code>, leverage automatic caching and control refetching behavior.</p><h2 id="what-is-tanstack-query">What Is TanStack Query?</h2><p><a target="_blank" href="https://tanstack.com/query/latest">TanStack Query</a> (which curiously enough started as a React lib, React Query) is an async state management library that has since become framework-agnostic, with first class support for Vue. It is specifically designed to manage data that lives on your API and needs to be fetched, cached, synchronized and updated.</p><p>TanStack Query gives you out-of-the-box automatic caching, background refetching, request deduplication, loading and error states&mdash;all of this for free with the basic hands-off configuration.</p><p>It&rsquo;s important to understand when to reach for TanStack Query vs. something like Pinia. Pinia is excellent for managing client state, things like UI preferences, form data, or any state that originates and lives entirely in the browser.</p><p>TanStack Query, on the other hand, shines when dealing with server state, data that comes from an API and that other users or processes could change at any time. They complement each other rather than compete.</p><p>Big disclaimer though: I highly recommend not mixing the two. I&rsquo;ve yet to find a production grade application where I needed to use Pinia in conjuction with Tanstack. If you do end up needing both, make sure that you don&rsquo;t overlap them. Keep your client and server data separate or it can become spaghetti code very quickly.</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">Vue Basics: State Management in Vue</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Learn how to <a target="_blank" href="https://www.telerik.com/blogs/vue-basics-state-management-vue">scale Vue state management</a> with ref/reactive, props/emits, provide/inject and Pinia as your app grows.</p></div></div><hr class="u-mb3" /></aside><h2 id="setting-up-tanstack-query">Setting Up TanStack Query</h2><p>Setting up TanStack Query for a Vue 3 application is straightforward. First, install the package:</p><pre><code>npm install @tanstack/vue-query
# or
yarn add @tanstack/vue-query
</code></pre><p>Then, head over to your <code>main.js</code> (or wherever you are mounting your app) and register the plugin.</p><p><strong>main.js</strong></p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">import</span> <span class="token punctuation">{</span> createApp <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span>
<span class="token keyword">import</span> <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">import</span> App <span class="token keyword">from</span> <span class="token string">'./App.vue'</span>

<span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">createApp</span><span class="token punctuation">(</span>App<span class="token punctuation">)</span>

app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>VueQueryPlugin<span class="token punctuation">)</span>

app<span class="token punctuation">.</span><span class="token function">mount</span><span class="token punctuation">(</span><span class="token string">'#app'</span><span class="token punctuation">)</span>
</code></pre><p>That&rsquo;s it! Under the hood, <code>VueQueryPlugin</code> creates a <code>QueryClient</code> for us and provides it to the entire application. If you need to customize default options, you can pass a configuration object to the plugin.</p><p>In the example below, we can set a global <code>staleTime</code> (the time that it takes the application to consider data that is already pulled &ldquo;stale&rdquo; and, thus, marks it for refetch).</p><pre class=" language-javascript"><code class="prism  language-javascript">app<span class="token punctuation">.</span><span class="token function">use</span><span class="token punctuation">(</span>VueQueryPlugin<span class="token punctuation">,</span> <span class="token punctuation">{</span>
  queryClientConfig<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>
        staleTime<span class="token punctuation">:</span> <span class="token number">1000</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">5</span><span class="token punctuation">,</span> <span class="token comment">// 5 minutes</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>With the plugin registered, we are now ready to start fetching data.</p><h2 id="your-first-query-with-usequery">Your First Query with useQuery</h2><p>The core of TanStack Query is the <code>useQuery</code> composable. It takes a configuration object and returns a set of reactive properties that we can use directly in our templates. Let&rsquo;s build a simple component that fetches a list of users.</p><p><strong>UserList.vue</strong></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, isLoading, isError, error } = useQuery({
  queryKey: ['users'],
  queryFn: fetchUsers,
})
&lt;/script&gt;

&lt;template&gt;
  &lt;p v-if="isLoading"&gt;Loading users...&lt;/p&gt;
  &lt;p v-else-if="isError"&gt;Something went wrong: {{ error.message }}&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>Let&rsquo;s break this down. The <code>useQuery</code> composable receives an object with two essential properties:</p><ul><li><code>queryKey</code>: An array that uniquely identifies this query. TanStack Query uses this key to cache, deduplicate and refetch the data. Think of it as a unique ID for this particular piece of server state.</li><li><code>queryFn</code>: An async function that actually fetches the data. It <strong>must</strong> return a promise that resolves with the data or throws an error.</li></ul><p>The composable returns several reactive properties, but the ones you&rsquo;ll use most often are:</p><ul><li><code>data</code>: The resolved data from the query function. It&rsquo;s <code>undefined</code> until the query resolves!</li><li><code>isLoading</code>: A boolean that is <code>true</code> while the query is fetching for the <em>first time</em> (no cached data exists yet).</li><li><code>isFetching</code>: A boolean that is <code>true</code> while the query is fetching. This includes background refetches.</li><li><code>isError</code>: A boolean that is <code>true</code> if the query encountered an error.</li><li><code>error</code>: The error object if the query failed.</li></ul><p>Notice how clean our template is. We don&rsquo;t need to manually set up loading flags or try/catch blocks. TanStack Query handles all of that for us.</p><p>I highly recommend that you read through the <a target="_blank" href="https://tanstack.com/query/latest/docs/framework/react/guides/queries">Query basics</a> page from the Tanstack documentation to really understand the different states and how to manage them.</p><h3 id="query-keys">Query Keys</h3><p>Query keys deserve a bit more attention. They are not just static identifiers; they can be dynamic. For example, if we wanted to fetch a single user by ID, we could write:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">const</span> <span class="token punctuation">{</span> data <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useQuery</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  queryKey<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">'user'</span><span class="token punctuation">,</span> userId<span class="token punctuation">]</span><span class="token punctuation">,</span>
  queryFn<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">fetchUserById</span><span class="token punctuation">(</span>userId<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>Whenever <code>userId</code> changes, TanStack Query will automatically recognize it as a <em>different</em> query and fetch the new data accordingly. If the data for that particular key was already cached, it will be returned instantly while a background refetch happens. This is extremely powerful and removes a lot of the manual <code>watch</code> logic you would otherwise have to write.</p><p>If you&rsquo;re having a hard time wrapping your head around this one, think about what an API endpoint usually looks like:</p><ul><li><code>myapp.com/user/1</code></li><li><code>myApp.com/user/2</code></li></ul><p>The <code>user</code> portion of the <code>queryKey</code> closely matches our API endpoint syntax (this doesn&rsquo;t have to be like this, this is merely a suggestion). More importantly, the <code>userId</code> reactive value will represent the ID (1/2) that we are sending to the API. We definitely want the caches for both of the calls above to be <em>different</em> from one another.</p><h2 id="caching-and-stale-data">Caching and Stale Data</h2><p>One of the biggest advantages of TanStack Query is its built-in caching. Once a query resolves, the result is stored in memory and associated with its query key. If another component (or the same component after a navigation) requests data with the same query key, the cached data is returned immediately (if it&rsquo;s not stale), so no other call has to be made to the API.</p><p>This is particularly noticeable when navigating between pages. Say you have a user list on one page and a user detail on another. When your user navigates back to the list, the data will already be there. No loading spinner, no flash of empty content.</p><p>TanStack Query manages this through two important concepts:</p><ul><li><code>staleTime</code>: How long (in milliseconds) the data is considered &ldquo;fresh.&rdquo; While data is fresh, TanStack Query will <em>never</em> refetch it. The default is <code>0</code>, meaning data is immediately considered stale after fetching.</li><li><code>gcTime</code> (garbage collection time): How long unused cached data is kept in memory before being garbage collected. The default is 5 minutes.</li></ul><p>You can configure these per query:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">const</span> <span class="token punctuation">{</span> data <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useQuery</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  queryKey<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">'users'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  queryFn<span class="token punctuation">:</span> fetchUsers<span class="token punctuation">,</span>
  staleTime<span class="token punctuation">:</span> <span class="token number">1000</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token comment">// 10 minutes</span>
  gcTime<span class="token punctuation">:</span> <span class="token number">1000</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">30</span><span class="token punctuation">,</span>    <span class="token comment">// 30 minutes</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>In this example, after the data is first fetched, it will be considered fresh for 10 minutes. During that time, any component mounting with the same <code>['users']</code> query key will get the cached data instantly with zero network requests. After 10 minutes, the data is marked stale and will be refetched the next time it is needed. If no component uses the <code>['users']</code> query for 30 minutes, the cache entry is garbage collected entirely.</p><p>To be quite honest with you, I&rsquo;ve personally never bother setting or modifying the default <code>gcTime</code>, but it&rsquo;s an important concept to keep in mind when using Tanstack.</p><h2 id="refetching">Refetching</h2><p>TanStack Query doesn&rsquo;t just fetch your data once and forget about it. It comes with several mechanisms to keep your data up to date, both automatic and manual.</p><h3 id="automatic-refetching">Automatic Refetching</h3><p>By default, TanStack Query will automatically refetch stale queries in the following situations:</p><ul><li><strong>Window focus</strong>: When the user leaves your app and comes back (tabs away and returns), stale queries are refetched. This is incredibly useful and one of my favorite benefits of Tanstack that you get for free&mdash;your users always see fresh data without lifting a finger.</li><li><strong>Network reconnect</strong>: If the user loses their internet connection and reconnects, stale queries are refetched automatically.</li></ul><p>You can control these behaviors individually:</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">const</span> <span class="token punctuation">{</span> data <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useQuery</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  queryKey<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">'users'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  queryFn<span class="token punctuation">:</span> fetchUsers<span class="token punctuation">,</span>
  refetchOnWindowFocus<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token comment">// disable window focus refetch</span>
  refetchOnReconnect<span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>    <span class="token comment">// this is the default</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><h3 id="polling-with-refetchinterval">Polling with refetchInterval</h3><p>For data that needs to be updated on a regular cadence, for example a notifications counter or a live feed, you can set up polling with <code>refetchInterval</code>.</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">const</span> <span class="token punctuation">{</span> data <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useQuery</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  queryKey<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">'notifications'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  queryFn<span class="token punctuation">:</span> fetchNotifications<span class="token punctuation">,</span>
  refetchInterval<span class="token punctuation">:</span> <span class="token number">1000</span> <span class="token operator">*</span> <span class="token number">30</span><span class="token punctuation">,</span> <span class="token comment">// refetch every 30 seconds</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>This will fire a background refetch every 30 seconds regardless of user interaction and regardless of stale state. The UI updates seamlessly since the cached data is replaced when the new data arrives. Keep in mind that all <code>data</code> returned by <code>useQuery</code> is reactive.</p><h3 id="manual-refetching">Manual Refetching</h3><p>Sometimes you want to give the user explicit control over when to refresh data. The <code>useQuery</code> composable returns a <code>refetch</code> function for exactly this purpose.</p><p><strong>UserList.vue</strong></p><pre class=" language-vue"><code class="prism  language-vue">&lt;script setup&gt;
import { useQuery } from '@tanstack/vue-query'

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

&lt;template&gt;
  &lt;button @click="refetch" :disabled="isFetching"&gt;
    Refresh
  &lt;/button&gt;

  &lt;p v-if="isLoading"&gt;Loading users...&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>Notice we&rsquo;re using <code>isFetching</code> here instead of <code>isLoading</code> to control the button&rsquo;s disabled state. The distinction is important: <code>isLoading</code> is only <code>true</code> when there is <em>no cached data</em> and a fetch is in progress (first load), while <code>isFetching</code> is <code>true</code> whenever <em>any</em> fetch is happening, including background refetches. This allows us to show the cached data in the list while indicating that a refresh is in progress.</p><h3 id="conditional-queries-with-enabled">Conditional Queries with Enabled</h3><p>One last option worth mentioning is <code>enabled</code>. Sometimes you don&rsquo;t want a query to fire immediately. Perhaps you need to wait for a user to select something first, or you want to set up a <code>computed</code> property with some conditions that need to occur first.</p><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">const</span> <span class="token punctuation">{</span> data <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useQuery</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  queryKey<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">'users'</span><span class="token punctuation">,</span> userId<span class="token punctuation">]</span><span class="token punctuation">,</span>
  queryFn<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">fetchUserById</span><span class="token punctuation">(</span>userId<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">,</span>
  enabled<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token operator">!</span><span class="token operator">!</span>userId<span class="token punctuation">.</span>value<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>The query will only execute when <code>enabled</code> evaluates to <code>true</code>. Once <code>userId</code> gets a value. Tanstack query will fire automatically once the reactive property returns <code>true</code>. If this property reverts to <code>false</code> the query no longer refetch when stale.</p><h2 id="wrapping-up">Wrapping Up</h2><p>TanStack Query takes a lot of the pain out of data fetching in Vue applications. With minimal setup, we get automatic caching, configurable stale times, background refetching on window focus and reconnect, polling, and clean loading and error states.</p><p>We&rsquo;ve only scratched the surface here. TanStack Query also supports mutations for creating and updating data, optimistic updates, infinite queries for pagination and a fantastic set of <a target="_blank" href="https://tanstack.com/query/latest/docs/framework/vue/devtools">devtools</a> for debugging your queries in the browser.</p><p>I recommend taking a deep dive into the <a target="_blank" href="https://tanstack.com/query/latest/docs/framework/vue/overview">official documentation</a> as it is very well written and packed with examples.</p><p>Happy fetching!</p><img src="https://feeds.telerik.com/link/10827/17357066.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:288b4cc6-8c92-4dec-a932-bed600474298</id>
    <title type="text">Angular 22: The Evolution of Modern Angular</title>
    <summary type="text">Signals, OnPush, declarative async resources: Angular 22 makes standard several features that help developers build better apps.</summary>
    <published>2026-06-05T21:01:27Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Dany Paredes </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17354975/angular-22-evolution-modern-angular"/>
    <content type="text"><![CDATA[<p><span class="featured">Signals, OnPush, declarative async resources: Angular 22 makes standard several features that help developers build better apps.</span></p><p>A few days ago, we checked out <a target="_blank" href="https://www.telerik.com/blogs/google-io-2026-developers-moving-ai-agentic-era">Google I/O 2026</a>, and today we need to talk about our favorite framework: <strong>Angular</strong>. Why? Because <strong>version 22</strong> is here!</p><p>Angular v22 is a major update focused on stability, performance and ergonomic APIs. It brings a more polished signal-driven platform and better data loading primitives that make modern apps feel faster and simpler to build.</p><p>Before we look at the most interesting changes, let&rsquo;s talk about the foundation. Angular v22 stabilizes the features we have been testing in the last few versions. If you want to build a high-performance app today, these are the new standards.</p><h2 id="stable-signal-forms-and-httpresource">Stable Signal Forms and httpResource</h2><p>Reactive Forms were great, but they often felt separate from the modern Signal-based reactivity. In v22, <strong>Signal Forms are officially stable</strong>.</p><p>What does this mean for us?</p><ul><li><strong>Better reactivity:</strong> No more unnecessary updates when a single field changes.</li><li><strong>Better type safety:</strong> We finally have a form system that works perfectly with TypeScript.</li></ul><p>Do you remember <code>HttpClient</code> and the manual <code>toSignal</code> conversions? They worked, but they added extra code. Now we have <code>httpResource</code>.</p><p>It is the new standard for fetching data. It treats your HTTP requests as signals, automatically handling the loading, error, and data states for you.</p><blockquote><p><strong>Learn more:</strong> Dive deeper into <a target="_blank" href="https://www.telerik.com/blogs/getting-started-httpresource-api-angular">Getting Started with httpResource API</a> and learn more about <a target="_blank" href="https://www.telerik.com/blogs/angular-signal-forms-vs-reactive-forms">Angular Signal Forms vs. Reactive Forms</a>.</p></blockquote><p>But what if your request depends on another signal? In v22, we have the new <code>chain()</code> method. It allows you to link resources together. If the first resource is loading, the second one waits automatically. This is a big win for complex data!</p><p><strong>Wait, what if you are already using RxJS?</strong> Don&rsquo;t worry, Angular has a solution for that too. Let&rsquo;s see how v22 handles the transition from Observables to Resources.</p><h2 id="rxresource-vs.-httpresource">RxResource vs. httpResource</h2><p>A common question is: &ldquo;Should I stop using RxJS?&rdquo; The answer is <strong>no</strong>. Angular v22 introduces <code>rxResource</code> as the perfect partner for your existing Observable streams.</p><ul><li><strong><code>httpResource</code></strong>: Use this for standard API calls. It is optimized for the Fetch API and handles JSON automatically.</li><li><strong><code>rxResource</code></strong>: Use this when your data source is an existing Observable. It allows you to use the power of RxJS operators inside the clean, Signal-based Resource API.</li></ul><p>Now that we have our data fetching ready, how do we make sure our users and agents can navigate efficiently? Let&rsquo;s talk about the new smart navigation features in the Router.</p><h2 id="smart-navigation-and-incremental-hydration">Smart Navigation and Incremental Hydration</h2><p>The Angular Router received some updates in v22 that solve common problems.</p><ul><li><strong>URL decoupling:</strong> You can now keep a clean URL in the address bar while the app shows a different state. With the new <code>browserUrl</code> input on <code>RouterLink</code>, you can show <code>/settings</code> while the router actually uses <code>/</code><code>account/advanced-preferences</code>.</li><li><strong>Parameter inheritance:</strong> Child routes now inherit parameters from their parents by default, and you don&rsquo;t need extra configuration to get an ID from a parent route.</li></ul><p>But a great UI and smart routing mean nothing if the page is slow to load. This brings us to the most impressive performance update in v22: <strong>incremental hydration</strong>.</p><p>Server-Side Rendering (SSR) is no longer a problem. In v22, <strong>incremental hydration is the default</strong>.</p><p>Angular now loads your application in small parts as they become visible. Combined with <strong>resource caching</strong>, the client can use the data fetched on the server without making a second API call. The user sees the data instantly, and the &ldquo;flicker&rdquo; of reloading data is gone.</p><blockquote><p><strong>Learn more:</strong> Explore <a target="_blank" href="https://www.telerik.com/blogs/new-angular-hydration">The New Angular Hydration</a> for in-depth details.</p></blockquote><p>But performance is also about how the browser handles changes. This is where the biggest change in Angular&rsquo;s history reaches its peak.</p><h2 id="onpush-by-default">OnPush by Default</h2><p>For years, <code>ChangeDetectionStrategy.Eager</code> (previously known as `Default`) was the standard for Angular&rsquo;s change detection. With v22, <code>ChangeDetectionStrategy.OnPush</code>is now the default for new components!</p><p>By using <code>ChangeDetectionStrategy.OnPush</code> as the default, we get:
</p><ul><li><strong>Better performance:</strong> The framework only checks what it needs to, exactly when it needs to, avoiding unnecessary checks.</li><li><strong>A step closer to Zoneless:</strong> This sets the stage for eventually dropping <code>zone.js</code> in the future.</li></ul><p>If you have an older app, don&rsquo;t worry! There is a migration tool that adds <code>ChangeDetectionStrategy.Eager</code> (the new name for the old behavior) to your existing components exactly as before.</p><p>Now, how do we make sure our fast code actually works? Let&rsquo;s talk about the new speed king of testing.</p><h2 id="vitest">Vitest</h2><p>Testing used to be the slow part of our work. Not anymore. Angular v22 officially replaces Karma/Jasmine with <strong>Vitest</strong> as the default test runner.</p><p>It is very fast and shares the same configuration as your build pipeline. Also, the new <code>migrate-karma-to-vitest</code> tool makes the change very easy, even for complex tests!</p><blockquote><p>Do you want to try <a target="_blank" href="https://www.telerik.com/blogs/unit-testing-angular-modern-testing-vitest">Vitest</a>?</p></blockquote><h2 id="angular-aria-accessibility-for-everyone">Angular Aria: Accessibility for Everyone</h2><p>Building custom components with correct ARIA roles and focus management can be difficult. In v22, <strong>Angular Aria is officially stable</strong>.</p><p>Think of it as <strong>&ldquo;headless accessibility.&rdquo;</strong> It provides the logic you need to build your own accessible components. It handles the complicated parts of ARIA attributes and keyboard interactions for you.</p><p>Why does this matter for AI? Because accessible apps are much easier for AI Agents to understand!</p><blockquote><p>Check this example about <a target="_blank" href="https://www.telerik.com/blogs/build-accessible-components-angular-aria">how to build accessible components with Angular Aria</a>.</p></blockquote><h2 id="async-dependency-injection">Async Dependency Injection</h2><p>The <code>injectAsync()</code> function allows us to lazy-load services only when they are actually needed, while still creating the instance with Angular dependency injection. This keeps your app small and fast.</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">CheckoutComponent</span> <span class="token punctuation">{</span>
  <span class="token comment">// We declare the lazy injection in the injection context (class field)</span>
  <span class="token keyword">private</span> paymentGatewayLoader <span class="token operator">=</span> <span class="token function">injectAsync</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">import</span><span class="token punctuation">(</span><span class="token string">'./payment-gateway.service'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>m <span class="token operator">=&gt;</span> m<span class="token punctuation">.</span>PaymentGatewayService<span class="token punctuation">)</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">async</span> <span class="token function">onCheckoutCompleted</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// We only load the heavy PaymentGatewayService when the user actually checks out!</span>
    <span class="token keyword">const</span> paymentGateway <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">paymentGatewayLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">await</span> paymentGateway<span class="token punctuation">.</span><span class="token function">processTransaction</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><h2 id="building-a-dashboard-with-angular-22">Building a Dashboard with Angular 22</h2><p>Instead of just explaining features, let&rsquo;s build a practical dashboard example that uses the stable core of Angular v22: <code>@Service()</code>, <code>httpResource()</code> and the new template syntax.</p><blockquote><p><strong>Want to follow along?</strong> You can clone the complete project from GitHub: <a target="_blank" href="https://github.com/danywalls/angular-22-nba-finals">angular-22-nba-finals</a>. Just run <code>npm start</code> and you&rsquo;ll have the dashboard running locally.</p></blockquote><h3 id="the-data-service-with-service">The Data Service with @Service</h3><p>Now, let&rsquo;s create our service. We will use the new <code>@Service()</code> decorator (which replaces <code>@Injectable()</code>) and <code>httpResource</code> to fetch the games. Notice how <code>httpResource</code> handles loading and error states for you.</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token comment">// src/app/nba.service.ts</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Service <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">NbaGame</span> <span class="token punctuation">{</span>
  id<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
  gameNumber<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
  homeTeam<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  homeScore<span class="token punctuation">:</span> <span class="token keyword">number</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  awayTeam<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  awayScore<span class="token punctuation">:</span> <span class="token keyword">number</span> <span class="token operator">|</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  status<span class="token punctuation">:</span> <span class="token string">'Final'</span> <span class="token operator">|</span> <span class="token string">'Scheduled'</span><span class="token punctuation">;</span>
  date<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  location<span class="token punctuation">:</span> <span class="token keyword">string</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">NbaService</span> <span class="token punctuation">{</span>
  <span class="token comment">// httpResource is the standard for fetching data</span>
  gamesResource <span class="token operator">=</span> httpResource<span class="token operator">&lt;</span>NbaGame<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/nba-finals'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><h3 id="the-ui-layer--error-states">The UI Layer &amp; Error States</h3><p>We display the data using the new <code>@for</code> block and keep the error handling inside the component template. This keeps the example focused on stable Angular v22 behavior.</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token comment">// src/app/app.ts</span>
@<span class="token function">Component</span><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;app-dashboard&gt;&lt;/app-dashboard&gt;
    &lt;router-outlet&gt;&lt;/router-outlet&gt;
  `</span></span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>Our Dashboard component remains simple:</p><pre class=" language-typescript"><code class="prism  language-typescript"><span class="token comment">// src/app/dashboard.component.ts</span>
<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> NbaService <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./nba.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-dashboard'</span><span class="token punctuation">,</span>
  template<span class="token punctuation">:</span> <span class="token template-string"><span class="token string">`
    &lt;div class="dashboard"&gt;
      &lt;h1&gt;NBA Finals 2026: Knicks vs Spurs&lt;/h1&gt;

      @if (nbaService.gamesResource.isLoading()) {
         &lt;p&gt;Loading game data...&lt;/p&gt;
      } @else {
        &lt;div class="cards"&gt;
          @for (game of nbaService.gamesResource.value(); track game.id) {
            &lt;div class="card"&gt;
              &lt;div class="meta"&gt;Game {{ game.gameNumber }} &bull; {{ game.date }}&lt;/div&gt;
              &lt;div class="score"&gt;
                {{ game.homeTeam }} {{ game.homeScore }} vs {{ game.awayScore }} {{ game.awayTeam }}
              &lt;/div&gt;
              &lt;div class="status"&gt;{{ game.status }}&lt;/div&gt;
            &lt;/div&gt;
          }
        &lt;/div&gt;
      }
    &lt;/div&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">DashboardComponent</span> <span class="token punctuation">{</span>
  nbaService <span class="token operator">=</span> <span class="token function">inject</span><span class="token punctuation">(</span>NbaService<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Save changes and run <code>ng server</code> navigate in the browser and tada!!!</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/angular-22-signals-dashboard.png?sfvrsn=5aeda1c4_2" alt="Angular 22 example dashboard using signals and httpResource" /></p><p>Done!! We play with the stable part of Angular v22 using <code>@Service()</code> for a clean singleton service definition, <code>httpResource()</code> for declarative, signal-based HTTP loading and <code>@for</code> with <code>@if</code> in templates to render loading and data states.</p><p>And, of course, AI is not forgotten by the Angular team. They updated and added new tools for Angular MCP and Skills. Let&rsquo;s see them!</p><h2 id="angular-mcp-and-skills">Angular MCP and Skills</h2><p>Of course, Angular continues embracing AI. The Angular MCP exposes app state and builds workflows for tools that understand Angular projects. That includes server-side MCP tools such as <code>devserver.wait_for_build</code>, <code>devserver.start</code> and <code>devserver.stop</code>.</p><p>Version 22 also introduces Agent Skills for Angular, a lightweight set of skill definitions that give AI assistants direct knowledge of modern Angular idioms and best practices. These skills are useful for teams that want to integrate agent-assisted coding into their workflow without pain.</p><ul><li>Learn more about Angular MCP: <a target="_blank" href="https://angular.dev/ai/mcp">angular.dev/ai/mcp</a></li><li>Learn more about Agent Skills for Angular: <a target="_blank" href="https://github.com/angular/skills">github.com/angular/skills</a></li></ul><h2 id="recap">Recap</h2><p>Angular 22 is a declaration that the way we build for the web has evolved. And by using <strong>Signals</strong>, <strong>OnPush performance</strong> and <strong>declarative async resources</strong>, we can build applications that are faster, simpler and easier to maintain.</p><p>This release is about making stable patterns:</p><ul><li>Use <code>httpResource()</code> for fetch-heavy components.</li><li>Use <code>@Service()</code> for singleton services.</li><li>Use <code>injectAsync()</code> when you want to defer heavy service loading.</li><li>Let template syntax like <code>@for</code> and <code>@if</code> keep your view code readable.</li><li>Embrace the AI era with Agent Skills and MCP tools.</li></ul><p>My advice is don&rsquo;t just read this article&mdash;build something! Take the dashboard we sketched and turn it into an interactive experience with Progress <a target="_blank" href="https://www.telerik.com/kendo-angular-ui/ai-chat">Kendo UI for Angular AI Chat</a>. A chat interface is a powerful way to explore the series score in real time and it&rsquo;s a great way to test Angular 22 with a modern Kendo UI scenario.</p><p>Feel free to <a target="_blank" href="https://youtu.be/h5OJUSS_8IA?is=WBc_bZuuQnx9b0EY">watch for all the new features in Angular 22</a>:</p><div data-sf-ec-immutable="" class="-sf-relative" contenteditable="false" style="width:560px;height:315px;"><div data-sf-disable-link-event=""><iframe width="560" height="315" src="https://www.youtube.com/embed/h5OJUSS_8IA?si=0Nzrxxm5BbAjRJz6" title="What’s new in Angular v22" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"></iframe></div></div><p>&nbsp;</p><p>For a complete overview and to verify all the new features, you can check the official release post: <a href="https://blog.angular.dev/announcing-angular-v22-c52bb83a4664" target="_blank">Announcing Angular v22</a>.</p><p><strong>Happy coding!</strong></p><img src="https://feeds.telerik.com/link/10827/17354975.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:f5808969-92f6-4e38-b801-f36a47627abf</id>
    <title type="text">Visual Studio 2026, 6 Months Later</title>
    <summary type="text">Throughout this post, we’ll discuss Visual Studio updates and improvements, such as performance, GitHub Copilot, Hot Reload and more.</summary>
    <published>2026-06-04T21:42:24Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Dave Brock </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17354313/visual-studio-2026-6-months-later"/>
    <content type="text"><![CDATA[<p><span class="featured">Throughout this post, we&rsquo;ll discuss Visual Studio updates and improvements, such as performance, GitHub Copilot, Hot Reload and more.</span></p><p>I remember exactly where I was when <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/visual-studio-2026-is-here-faster-smarter-and-a-hit-with-early-adopters/">Visual Studio 2026 dropped</a> back in November: at my desk, watching the Microsoft launch event with one eye and refactoring an <code>IDataReader</code> implementation with the other.</p><p>The keynote was typical Microsoft. There was a lot of &ldquo;reimagining how developers work.&rdquo; There was, of course, a slide with the word &ldquo;intelligent&rdquo; on it at least eight times. And the word &ldquo;Copilot&rdquo; appeared on screen so many times I started to wonder if Microsoft&rsquo;s marketing team had been replaced by an infinite loop that just prints <code>Copilot</code> forever.</p><p>Then I installed the thing.</p><p>The first week with VS 2026 was the most disoriented I&rsquo;ve felt in a Visual Studio release since <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/a-design-with-all-caps/">the menu bar went all-caps in 2012</a> and I spent a month reading angry forum threads about it.</p><p>In 2026, I was tripped up by the just-slightly different Solution Explorer, the Copilot panel that lives where my Test Explorer used to live, the general Copilot onslaught and the interesting design choices. I felt like everything on my desk had been moved slightly to the left.</p><p>That was temporary. As with most new technology, eventually things felt normal again. I was finding workflows that genuinely beat my old ones and I stopped noticing the cosmetic changes. So here&rsquo;s where I am six months later &hellip; same machine, same kind of solution I work in every day, just a different IDE around it.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/vs2026-overview.png?sfvrsn=4ef376fb_2" alt="" /><br /><span style="font-size:14px;">We meet again, old friend. At least the menu bar isn't in caps.</span></p><p>Six months in, I feel comfortable. Six months is long enough to live with the rough edges, and find what&rsquo;s genuinely useful. So I&rsquo;m here to share them with you.</p><p>As an aside: to keep my sanity intact, I&rsquo;m going to use the word &ldquo;Copilot&rdquo; only when I absolutely have to. Microsoft has decided that every feature in Visual Studio is now Copilot-something &hellip; Copilot Chat, Copilot Edits, Copilot Agent, Copilot Workspaces, GitHub Copilot, Copilot for Azure, Copilot for Pull Requests and so on. So when I say &ldquo;the agent&rdquo; or &ldquo;the AI thing in the sidebar,&rdquo; you&rsquo;ll know what I mean.</p><h2 id="the-performance-story-is-real">The Performance Story Is Real</h2><p>I&rsquo;ll lead with this, mostly because it was the thing I was most skeptical about. Every Visual Studio release for the past decade has touted &ldquo;X% faster solution load!&rdquo; and, to me, every Visual Studio release for the last decade has felt (roughly) the same speed once you test it with your beefy codebase.</p><p>VS 2026 is genuinely faster. Not &ldquo;marketing-faster&rdquo; &hellip; actually faster. The <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/visual-studio-2022/">64-bit-everywhere story that started with VS 2022</a> has matured into something that handles large solutions without the periodic &ldquo;please hold&rdquo; pauses we&rsquo;re all familiar with. In Microsoft&rsquo;s VS2026 launch post, they say they <a target="_blank" href="https://devblogs.microsoft.com/visualstudio/visual-studio-2026-is-here-faster-smarter-and-a-hit-with-early-adopters/">cut hangs by over 50%</a>.</p><p>The IntelliSense responsiveness is the part I notice most. Typing a <code>.</code> after an object now produces suggestions roughly when I expect them to appear. A low bar? Definitely, but previous versions frequently limboed under it.</p><h2 id="agent-mode-is-useful-when-you-pick-the-right-job">Agent Mode Is Useful, When You Pick the Right Job</h2><p>GitHub Copilot&rsquo;s <a target="_blank" href="https://learn.microsoft.com/en-us/visualstudio/ide/copilot-agent-mode?view=visualstudio">agent mode in Visual Studio 2026</a>&mdash;or, as Microsoft prefers, <em>GitHub Copilot Agent Mode powered by Copilot in Visual Studio with Copilot</em>&mdash;has improved tremendously since Visual Studio 2022. Six months in, I think it&rsquo;s genuinely useful, but for a specific set of tasks.</p><p>In my experience, it works well with scoped, well-defined refactors. If I want to ask: <em>Hey Copilot, add a new health check for this Cosmos container and follow the pattern in my existing health checks.</em> (Yes, I sometimes talk to Copilot like a home assistant.) Or: <em>Take my new controller and add the same logging pattern as the others in this project.</em> And, after discovering some methods in a codebase with 11 nested if statements: <em>Convert this <code>for</code> loop to a LINQ expression but keep it readable.</em> It&rsquo;s effective enough that I&rsquo;ve stopped writing this kind of code by hand.</p><p>In my experience, things get harder when the agent needs to understand why something exists. The agent will happily refactor a piece of code that only exists because of, say, a weird third-party integration constraint from 2018, and the resulting code will be cleaner, more maintainable &hellip; and broken in production. Did I actually break something in production? Not this time. But I would have.</p><p>To be fair to the agent, this is exactly where it needs context. It doesn&rsquo;t read minds (although it sure does try). Here&rsquo;s a pattern I&rsquo;ve leaned on.</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token comment">// AGENT NOTES:</span>
<span class="token comment">// - This service must remain synchronous; the upstream caller cannot</span>
<span class="token comment">//   be made async without a contract change.</span>
<span class="token comment">// - The string "PRIME" is required by the third-party integration. </span>
<span class="token comment">//   I don't like it but they won't change it. Do not "improve" it </span>
<span class="token comment">//   to PersonType.Prime.ToString().</span>
<span class="token comment">// - Cancellation tokens must be forwarded; do NOT pass `default`.</span>
</code></pre><p>When the agent has clear context like this, it really delivers. Below is the agent producing two new health check files plus a registration update in <code>Program.cs</code>, all from one prompt asking it to mirror an existing pattern. The result is a careful refactoring with the right substitutions swapped in. That&rsquo;s exactly the kind of work I&rsquo;m happy to hand off.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/gh-copilot-agent.png?sfvrsn=a2b5eefe_2" alt="" /><br /><span style="font-size:14px;">The agent panel and one of the two generated files. Three files changed, zero hand-written by me. (My team is thankful for the latter.)</span></p><p>This sounds like babysitting, and it is. Welcome to AI-driven development, my friends. The 10 seconds of setup saves me from a 10-minute cleanup later. When the prep is solid, the result is solid.</p><p>For more information on <a target="_blank" href="https://learn.microsoft.com/en-us/visualstudio/ide/copilot-chat-context?view=visualstudio#use-custom-instructions">custom instructions, check out the Microsoft documentation</a>.</p><h2 id="test-explorer-isnt-frustrating">Test Explorer Isn&rsquo;t Frustrating</h2><p>In my experience, the Test Explorer seems to be much improved in Visual Studio 2026. If I had to sum it up in previous versions, I&rsquo;d say &ldquo;it&rsquo;s fine&rdquo; &hellip; after all, do we ever get jazzed about tests? In previous releases, I kept saying &ldquo;it&rsquo;s fine&rdquo; until I had a few thousand tests, at which point it started getting opinionated about which tests to run.</p><p>The new version is faster, the filtering predictably works, and it doesn&rsquo;t lose track of which tests are running when you switch branches mid-execution. Try it yourself. Run your full test suite and watch the Test Explorer not embarrass itself. It&rsquo;s a small joy.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/test-explorer.png?sfvrsn=222a8ba_2" alt="" /><br /><span style="font-size:14px;">One of those things you don't notice until it stops getting in your way.</span></p><h2 id="better-git-integration">Better Git Integration</h2><p>Visual Studio&rsquo;s Git tooling has historically been one of the IDE&rsquo;s more underwhelming features. Until recently, my workflow was:</p><ol><li>Make code changes in Visual Studio.</li><li>Run Git commands in my terminal (or a third-party tool like GitHub Desktop)</li><li>Ignore the IDE&rsquo;s Git panel entirely.</li></ol><p>With VS 2026, I&rsquo;m finally doing real Git work inside the tool. The new staging interface is better. The conflict resolution UI is also better than it used to be. The &ldquo;please show me an actual diff and not a vague summary&rdquo; feature is good.</p><p>Now, it&rsquo;s no GitKraken and it doesn&rsquo;t try to be. But it&rsquo;s good enough that I no longer switch over to a terminal for routine operations, and that works for me.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/git-conflict.png?sfvrsn=62f8884a_2" alt="" /><br /><span style="font-size:14px;">Three-way merge view, in the IDE I'm already in. Rejoice!</span></p><h2 id="hot-reload-that-reloads-more-often">Hot Reload That Reloads More Often</h2><p>Hot Reload has been one of those features that worked on Friday afternoons if I said &ldquo;please work&rdquo; (especially for ASP.NET Core). Any other time, I&rsquo;d change a method body, save, watch the IDE think very hard about it, and then get: <em>&ldquo;Hot Reload was unable to apply your changes. Restart the application?&rdquo;</em></p><p>The &ldquo;hot&rdquo; in &ldquo;Hot Reload&rdquo; was <a target="_blank" href="https://www.reddit.com/r/dotnet/comments/1my662s/will_microsoft_ever_fix_hot_reload_in_net/">stretching the truth</a> at times (with the understanding that getting it right <a target="_blank" href="https://www.reddit.com/r/dotnet/comments/1my662s/comment/naac88s/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">isn&rsquo;t as simple in .NET</a> as it is for languages like JavaScript).</p><p>With VS 2026, I now reach for it instinctively instead of cynically. Method-body edits in controllers, services and Razor pages just apply. You change a string in a controller, hit Save and see the change. You know, the way it was supposed to work in 2021. The way it kind of worked in 2023. The way it now actually works in 2026.</p><p>When Hot Reload can&rsquo;t apply a change (because the change requires a metadata update or you&rsquo;ve added a field or whatever the actual reason is), the IDE now tells me clearly why instead of vaguely throwing up its hands. That alone goes a long way. I know whether to keep iterating or to just restart the host.</p><h3 id="debugger-improvements-especially-with-async-traces">Debugger Improvements (Especially with Async Traces)</h3><p>Have you ever stared at an async stack trace and felt your soul leave your body? Just me?</p><p>Historically, when an exception bubbled up from somewhere deep in an <code>async</code>/<code>await</code> chain, the stack trace was a wall of <code>MoveNext</code> calls and state machine internals. It was tremendously useful if you wanted to learn about the C# compiler, but not great if you wanted to know which of your methods actually threw.</p><p>Things are looking better in Visual Studio 2026. The debugger now stitches together async call chains into something that reads like the call graph you actually wrote. The <code>MoveNext</code> noise is collapsed by default. The original calling method is shown as the parent frame and not buried six levels down. When an exception is thrown inside a <code>Task.WhenAll</code>, you can see which task threw without reverse-engineering the parallel structure.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/debugger.png?sfvrsn=53980e30_2" alt="" /><br /><span style="font-size:14px;">An async exception, with the actual call chain visible. No <code>MoveNext</code> translation required.</span></p><p>On top of that, conditional breakpoints feel faster to evaluate, and the tracepoints (where you can log a message without breaking) is a one-click affair instead of a buried right-click option.</p><p>Small wins. But debugging is the thing I do every day, and small wins on the thing you do every day add up fast.</p><h2 id="the-verdict">The Verdict</h2><p>Look, the Copilot stuff is genuinely impressive. The agent demos at the keynote were the kind of thing that makes you want to fire up the installer immediately. And after six months, I&rsquo;ll admit that I use it every day.</p><p>But Visual Studio 2026 is a genuine step forward for the boring reasons. The performance is real. The Test Explorer doesn&rsquo;t stress me out. The debugger provides stack traces a human can read. The agent mode handles the kind of work I&rsquo;d previously do by hand. Those are the wins that show up in my daily work, not the ones that show up in keynote demos.</p><p>If you&rsquo;re on Visual Studio 2022 and wondering whether to upgrade: yes. Ignore the marketing, and let the unglamorous improvements do their job. Six months later, you won&rsquo;t be excited about Visual Studio 2026 &hellip; and that&rsquo;s exactly the point. You&rsquo;ll just be working faster.</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">Extension Properties: C# 14&rsquo;s Game-Changing Feature for Cleaner Code</h4></div><div class="col-8"><p class="u-fs16 u-mb0">This post introduces <a target="_blank" href="https://www.telerik.com/blogs/extension-properties-csharp-14-game-changing-feature-cleaner-code">extension properties</a>, a new feature of C# 14 that allows you to use properties in your extension methods.</p></div></div></aside><img src="https://feeds.telerik.com/link/10827/17354313.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:3187b492-d5dc-4bc2-94f1-80f26e3730cb</id>
    <title type="text">Google I/O 2026 for Developers: Moving from AI to the Agentic Era</title>
    <summary type="text">ICYMI: Google I/O 2026 announced how the tech giant is embracing the Agentic Era. Here’s what it means for developers.</summary>
    <published>2026-06-03T20:16:05Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Dany Paredes </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17353557/google-io-2026-developers-moving-ai-agentic-era"/>
    <content type="text"><![CDATA[<p><span class="featured">ICYMI: Google I/O 2026 announced how the tech giant is embracing the Agentic Era. Here&rsquo;s what it means for developers.</span></p><p>Another year, another Google I/O. This time, the vibe is different. We are not just talking about &ldquo;AI in a sidebar&rdquo; or &ldquo;chatbots.&rdquo; We are witnessing the birth of the Agentic Era.</p><p><a target="_blank" href="https://www.youtube.com/watch?v=FZ-3BjbfNlI">Watch the Google I/O Developer Keynote Recap</a>:</p><div data-sf-ec-immutable="" class="-sf-relative" contenteditable="false" style="width:560px;height:315px;"><div data-sf-disable-link-event=""><iframe width="560" height="315" src="https://www.youtube.com/embed/FZ-3BjbfNlI?si=f7jP61PYQ6wE0qp1" title="Developer Keynote (Google I/O '26)" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"></iframe></div></div><br /><p>As developers, we have all felt the pain: building a beautiful UI, only to have an AI agent try to &ldquo;scrape&rdquo; it. The agent fails to find the right button or misinterprets a CSS class. In 2026, Google is changing the game by providing the infrastructure for agents that do not just &ldquo;chat,&rdquo; but actually act.</p><p>Let&rsquo;s dive into the key updates from the <a target="_blank" href="https://developers.googleblog.com/all-the-news-from-the-google-io-2026-developer-keynote/">Developer Keynote</a> that will change how we build software this year.</p><p>These are the top highlights of Google I/O 2026 for developers:</p><ul><li>Gemini 3.5 and Managed Agents: Act, Don&rsquo;t Just Talk.</li><li>Antigravity 2.0 and Managed Agents: The Agent-First Infrastructure.</li><li>WebMCP: Making Your UI &ldquo;Agent-Ready.&rdquo;</li><li>AI-Powered DevTools: Debugging for Humans and Agents.</li><li>Android: The End of Slow Migrations.</li><li>Firebase, Built-in AI and Performance: The Core of the Agentic Web.</li></ul><p>Let&rsquo;s explore each one.</p><h2 id="gemini-3.5-and-managed-agents-act-dont-just-talk">1. Gemini 3.5 and Managed Agents: Act, Don&rsquo;t Just Talk</h2><p>The biggest shift this year is the introduction of Gemini 3.5 and the Managed Agents API. This is not just about a smarter LLM: it is about orchestration.</p><p>With a single API call, you can now set up a remote sandbox and an agent harness. This allows you to deploy autonomous agents that can execute code, manage files and interact with your backend. You do not have to manage the underlying compute or security infrastructure.</p><p>This integration goes beyond just code generation: it includes a &ldquo;one-click deploy&rdquo; from Google AI Studio directly to Cloud Run. This makes the path from prototype to production much faster. For Android developers, native Kotlin support enables a &ldquo;vibe coding&rdquo; experience. In this mode, the model understands complex app structures as if it were a senior teammate.</p><blockquote><ul><li><a target="_blank" href="https://aistudio.google.com/">Learn more about Gemini 3.5 capabilities.</a></li><li><a target="_blank" href="https://youtu.be/aqmpZocmR8o?t=149">Watch Gemini 3.5 in action.</a></li></ul></blockquote><p>But having a brain capable of action is only the first step. To truly use this power, we need an ecosystem that can safely manage these agents. This is where the next generation of infrastructure comes in.</p><h2 id="antigravity-2.0-and-managed-agents-the-agent-first-infrastructure">2. Antigravity 2.0 and Managed Agents: The Agent-First Infrastructure</h2><p>The most powerful announcement for our productivity is the launch of Antigravity 2.0 and the all-new Antigravity CLI. This platform is more than just a tool: it is a complete surface for managing specialized subagents. These subagents can tackle complex workflows, such as UI development or backend migrations. The best part is that all of this happens while being protected by built-in terminal sandboxing and credential masking.</p><p>If you want to skip the infrastructure setup, the new Managed Agents in the Gemini API is a great solution. With one API call, Google provides a fully set-up agent harness with a remote sandbox. This removes the friction of setup, allowing you to focus on the logic of your agent.</p><blockquote><ul><li><a target="_blank" href="https://developers.google.com/antigravity">Check out the Antigravity SDK documentation.</a></li><li><a target="_blank" href="https://www.youtube.com/watch?v=aqmpZocmR8o&amp;t=2717s">Watch Antigravity 2.0 in action.</a></li></ul></blockquote><p>Once we have the infrastructure to build and deploy agents, the next challenge is enabling our web applications to actually talk back to them. Let&rsquo;s look at how Google is standardizing this conversation.</p><h2 id="webmcp-making-your-ui-‘agent-ready’">3. WebMCP: Making Your UI &lsquo;Agent-Ready&rsquo;</h2><p>On the web side, the big news is WebMCP (Model Context Protocol for the Web). This protocol allows your website to expose structured tools directly to browser-based AI agents.</p><p>If you have been following my work on the Progress <a target="_blank" href="https://www.telerik.com/blogs/angular-kendo-ui-mcp-making-agents-work-for-you">Kendo UI for Angular MCP Server</a>, you know how powerful this is. While Kendo UI MCP helps the developer build apps by giving the agent specific knowledge, WebMCP helps the user&rsquo;s agent navigate the app once it is built. Together, they create a complete AI lifecycle from creation to consumption.</p><p>Instead of an AI agent &ldquo;guessing&rdquo; how to interact with your data by clicking randomly, it can discover and use specific tools via WebMCP. For example, it can use a &ldquo;Filter&rdquo; or &ldquo;Export&rdquo; command in a Kendo UI Grid. This improves reliability and security for the end user.</p><blockquote><ul><li><a target="_blank" href="https://www.youtube.com/watch?v=bo3i0FzDUYo">Discover WebMCP Architecture.</a></li></ul></blockquote><p>With our apps now acting as toolkits for agents, we need a way to see what is happening under the hood. Thankfully, the debugging tools we use every day have also received an AI-first update.</p><h2 id="ai-powered-devtools-debugging-for-humans-and-agents">4. AI-Powered DevTools: Debugging for Humans and Agents</h2><p>One of the most innovative updates is the transformation of Chrome DevTools into an AI-first environment. Google has introduced the AI Assistance panel. This is a context-aware chat interface powered by Gemini that understands the technical state of your page.</p><p>To enable these features today, make sure your DevTools language is set to English (US). Then, enable the &ldquo;AI Innovations&rdquo; flag in the DevTools Settings.</p><p>The dedicated DevTools interface for AI agents also grants autonomous assistants access to the accessibility tree and network traffic. This allows AI coding agents to identify and fix bugs or performance issues by themselves.</p><blockquote><ul><li><a target="_blank" href="https://www.youtube.com/watch?v=aqmpZocmR8o&amp;t=2717s">Watch AI Assistance in DevTools.</a></li></ul></blockquote><p>But the shift toward an agentic future is not limited to the browser. These same patterns are now landing in the mobile ecosystem, starting with a massive update to how we migrate and build Android apps.</p><h2 id="android-the-end-of-slow-migrations">5. Android: The End of Slow Migrations</h2><p>For mobile developers, the Android Migration Agent in Android Studio is a breakthrough. It can migrate source code from React Native or iOS into native Kotlin Android apps very quickly.</p><p>This is not just about translating syntax: the agent understands the architectural details of Navigation 3 and modern Jetpack libraries. By using open-source Android Skills, the migration follows industry-standard patterns. This means you do not just get a working app, you get a high-quality native codebase that follows Google&rsquo;s best practices.</p><blockquote><ul><li><a target="_blank" href="https://github.com/android/skills">Explore Android Skills on GitHub.</a></li><li><a target="_blank" href="https://www.youtube.com/watch?v=dXCCleAddEA">Watch Android AI Updates.</a></li></ul></blockquote><p>None of these platforms would matter without a high-performance engine. To close our recap, let&rsquo;s look at the core browser APIs and Firebase updates that make this entire experience possible.</p><h2 id="firebase-built-in-ai-and-performance">6. Firebase, Built-in AI and Performance</h2><p>Firebase is also becoming agent-ready with Firebase Data Connect. This feature brings PostgreSQL to Firebase, allowing you to build full-stack apps with AI Studio integration. You can now build and launch apps directly to Cloud Run without leaving your development environment.</p><p>On the browser side, Chrome 148 brings the Prompt API (powered by Gemini Nano) to the stable channel. It is now multimodal, which means you can process images, audio and text directly on the user&rsquo;s device. The inclusion of Gemma 197M means these models are fast, even on older hardware.</p><p>Additionally, the new Soft Navigations API finally treats client-side transitions in SPAs as first-class citizens. This gives us accurate Core Web Vitals for our Angular dashboards. We also saw the release of HTML-in-Canvas, which allows us to render real DOM elements inside 3D environments.</p><blockquote><ul><li><a target="_blank" href="https://developer.chrome.com/blog/chrome-148-beta/">Check the Chrome 148 release notes.</a></li><li><a target="_blank" href="https://www.youtube.com/watch?v=uT7MVcCQ4rw">Watch Performance Updates.</a></li></ul></blockquote><h2 id="recap-why-these-tools-matter">Recap: Why These Tools Matter</h2><p>Google I/O 2026 has made it clear: the browser and the OS are no longer just renderers. They are execution environments for intelligence. The shift from AI Assistance to Agentic Workflows is a massive opportunity for us as developers to redefine how users interact with software.</p><p>By making the web machine-readable with WebMCP and providing the Antigravity platform, Google is setting the stage for a proactive web. Our role is shifting from building static interfaces to building intelligent platforms.</p><h3 id="what-you-can-do-now">What You Can Do Now</h3><ol><li><strong>Experiment with WebMCP:</strong> Think about which repetitive task in your current app could be handled by a secure subagent or a structured tool. You can also play with the <a target="_blank" href="https://www.telerik.com/blogs/angular-kendo-ui-mcp-making-agents-work-for-you">Kendo UI MCP</a> to see it in action!</li><li><strong>Optimize for Soft Navigations:</strong> If you are building web apps with React or Angular, start using the new API to get the real performance story of your dashboards.</li><li><strong>Try the Managed Agents API:</strong> Test the loop between AI Studio and Cloud Run to see how fast you can go from idea to an agentic prototype.</li></ol><p>If you have time, you can also <a target="_blank" href="https://io.google/2026/explore/developer-keynote-1">check out the full video of the Developer Keynote</a> for even more demos and deep dives.</p><p>Happy coding!</p><hr data-sf-ec-immutable="" /><blockquote><aside><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">Telerik and Kendo UI Meet WebMCP</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Learn how Progress Telerik UI and Kendo UI tools <a target="_blank" href="https://www.telerik.com/blogs/telerik-and-kendo-meet-webmcp"> leverage WebMCP</a> to expose enterprise application functionality directly to AI agents through standardized tools.</p></div></div></aside></blockquote><img src="https://feeds.telerik.com/link/10827/17353557.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:54ada40d-ff98-496d-94ae-1dbc6613c6e0</id>
    <title type="text">Advanced Form Validation in Blazor 10</title>
    <summary type="text">These new validation features available in Blazor as of .NET 10 help manage forms with nested objects and lists, plus perform complex and cross-field validations.</summary>
    <published>2026-06-03T13:34:16Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Héctor Pérez </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17353276/advanced-form-validation-blazor-10"/>
    <content type="text"><![CDATA[<p><span class="featured">These new validation features available in Blazor as of .NET 10 help manage forms with nested objects and lists, plus perform complex and cross-field validations.</span></p><p>Form validation is essential when working with data in Blazor applications. Before .NET 10, if you wanted to perform validation of nested objects, you had to take a series of additional steps that could add complexity to your projects. In this article, we will examine the improvements and new features in .NET 10 to solve this problem through a practical exercise. Let&rsquo;s go!</p><h2 id="understanding-the-problem-of-nested-validations-before-.net-10">Understanding the Problem of Nested Validations Before .NET 10</h2><p>To see the problem when working with nested objects and their validation, let&rsquo;s create a sample Blazor project. For this demonstration, I am working with a project using the <strong>Blazor Web App</strong> template configured with <strong>Interactive Server</strong> rendering <strong>globally</strong>.</p><p>Immediately after creating the project, perform the <a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/getting-started/web-app">installation and configuration steps for the Progress Telerik UI for Blazor controls</a>, as we will use them to quickly build modern interfaces.</p><h3 id="creating-models-in-the-application">Creating Models in the Application</h3><p>Suppose we are building an attendee registration application for a technical event. As a first step, I have defined a class <code>EventRegistration</code> with the following structure:</p><pre class=" language-csharp"><code class="prism  language-csharp"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">EventRegistration</span>
<span class="token punctuation">{</span>
    <span class="token keyword">public</span> Attendee Attendee <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> BillingAddress BillingAddress <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> List<span class="token operator">&lt;</span>SessionSelection<span class="token operator">&gt;</span> Sessions <span class="token punctuation">{</span> <span class="token keyword">get</span><span class="token punctuation">;</span> <span class="token keyword">set</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">HandleValidSubmit</span><span class="token punctuation">(</span>EditContext context<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>        
        hasValidated <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>        
        isSubmitted <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
        
    <span class="token keyword">private</span> <span class="token keyword">void</span> <span class="token function">HandleInvalidSubmit</span><span class="token punctuation">(</span>EditContext context<span class="token punctuation">)</span>
    <span class="token punctuation">{</span>
        hasValidated <span class="token operator">=</span> <span class="token keyword">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code above, I added the field <code>hasValidated</code> to check if the form has been submitted. I also modified <code>OnInitialized</code> by adding the handler for the event <code>OnValidationStateChanged</code>, which allows the UI to update reactively. I also added the helper <code>IsFieldValid&lt;T&gt;</code> and the methods <code>GetAttendeeHeaderClass</code> and <code>GetBillingHeaderClass</code>. This is the result:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/form-section-error-summary.png?sfvrsn=64b57388_2" alt="Form displaying an error summary for entire sections" /></p><p>With this, we have managed to give the user better feedback about the sections they need to complete before proceeding.</p><h2 id="conclusion">Conclusion</h2><p>Throughout this article, we have explored the new features available in .NET 10 that will undoubtedly help you manage forms with nested objects and lists. We&rsquo;ve also looked at how to perform cross-validations and, finally, the process of carrying out more complex validations by checking data in sections.</p><p>Now it&rsquo;s your time to implement these new features in your own projects and provide better feedback to your users.</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">120+ Enterprise-Grade Blazor UI Components</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Accelerate app development with <a target="_blank" href="https://www.telerik.com https://www.telerik.com/blazor-ui">Telerik UI for Blazor</a>&mdash;a complete set of 120+ high-performance, accessible controls, MCP Servers and more. Available with a free 30-day trial.</p></div></div></aside><img src="https://feeds.telerik.com/link/10827/17353276.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:d3c5b41c-9e8d-4bba-88a3-60fa450dd551</id>
    <title type="text">Create and Publish an NPM Package Automatically</title>
    <summary type="text">Follow these steps to publish a TypeScript pakcage to npm.</summary>
    <published>2026-06-02T21:18:01Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Jonathan Gamble </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17352916/create-publish-npm-package-automatically"/>
    <content type="text"><![CDATA[<p><span class="featured">Follow these steps to publish a TypeScript pakcage to npm.</span></p><p>There are not many good articles on publishing a TypeScript package to npm automatically. The best article I have seen is by the one and only Matt Pocock, <a target="_blank" href="https://www.totaltypescript.com/how-to-create-an-npm-package#32-set-up-a-tsconfigjson">How to Create an NPM Package</a>. This is wonderful for understanding the package part, but publishing is not well documented. Recently, the npm publish keys have gone away, so we must connect our workflow manually with <a target="_blank" href="https://docs.npmjs.com/trusted-publishers">Trusted Publishing</a>.</p><p><strong>Let&rsquo;s go through all steps to publish a TS npm package automagically!</strong></p><h2 id="create-repository">Create Repository</h2><ol><li><p>Create a folder with the name of your package and open it up in VS Code.</p></li><li><p>Create your repository on GitHub with same name.</p></li><li><p>Run <code>git init</code> in root.</p></li><li><p>Create a <code>.gitignore</code> file in root. <code>dist</code> will be where it builds the JavaScript.</p><pre class=" language-bash"><code class="prism  language-bash">node_modules
dist
.env
.env.*
</code></pre></li></ol><p><strong>Note:</strong> You may not have an <code>.env</code> file, but it is good practice.</p><ol start="5"><li>Create your <code>README.md</code> with whatever content.</li><li>Create a <code>LICENSE</code> file if you want as well. There are <a target="_blank" href="https://generate-license-file.js.org/">packages</a> for this or you can copy and paste.</li><li>Run <code>git add .</code> and <code>git commit -m 'first'</code> to commit your first file.</li><li>Run <code>git branch -M main</code> to make <code>main</code> the default branch.</li><li>Add your remote origin with:</li></ol><pre class=" language-bash"><code class="prism  language-bash"><span class="token function">git</span> remote add origin https://github.com/YOUR_REPOSITORY
</code></pre><ol start="10"><li>Push package with <code>git push -u origin main</code>.</li></ol><p><strong>Note:</strong> My example package will be named <code>format-price</code>.</p><h2 id="create-your-package.json-file">Create Your Package.json File</h2><ol><li>Create <code>package.json</code> in root.</li></ol><pre class=" language-bash"><code class="prism  language-bash"><span class="token punctuation">{</span>
  <span class="token string">"name"</span><span class="token keyword">:</span> <span class="token string">"format-price"</span>,
  <span class="token string">"version"</span><span class="token keyword">:</span> <span class="token string">"1.0.0"</span>,
  <span class="token string">"description"</span><span class="token keyword">:</span> <span class="token string">"A demo of a simple TypeScript package that formats a price value."</span>,
  <span class="token string">"keywords"</span><span class="token keyword">:</span> <span class="token punctuation">[</span>
    <span class="token string">"demo"</span>,
    <span class="token string">"typescript"</span>,
    <span class="token string">"price"</span>,
    <span class="token string">"formatting"</span>
  <span class="token punctuation">]</span>,
  <span class="token string">"homepage"</span><span class="token keyword">:</span> <span class="token string">"https://github.com/jdgamble555/format-price"</span>,
  <span class="token string">"bugs"</span><span class="token keyword">:</span> <span class="token punctuation">{</span>
    <span class="token string">"url"</span><span class="token keyword">:</span> <span class="token string">"https://github.com/jdgamble555/format-price/issues"</span>
  <span class="token punctuation">}</span>,
  <span class="token string">"author"</span><span class="token keyword">:</span> <span class="token string">"Jonathan Gamble (https://code.build)"</span>,
  <span class="token string">"repository"</span><span class="token keyword">:</span> <span class="token punctuation">{</span>
    <span class="token string">"type"</span><span class="token keyword">:</span> <span class="token string">"git"</span>,
    <span class="token string">"url"</span><span class="token keyword">:</span> <span class="token string">"git+https://github.com/jdgamble555/format-price.git"</span>
  <span class="token punctuation">}</span>,
  <span class="token string">"files"</span><span class="token keyword">:</span> <span class="token punctuation">[</span>
    <span class="token string">"dist"</span>
  <span class="token punctuation">]</span>,
  <span class="token string">"type"</span><span class="token keyword">:</span> <span class="token string">"module"</span>,
  <span class="token string">"main"</span><span class="token keyword">:</span> <span class="token string">"dist/index.js"</span>,
  <span class="token string">"scripts"</span><span class="token keyword">:</span> <span class="token punctuation">{</span>
    <span class="token string">"build"</span><span class="token keyword">:</span> <span class="token string">"tsc"</span>,
    <span class="token string">"test"</span><span class="token keyword">:</span> <span class="token string">"vitest run"</span>,
    <span class="token string">"ci"</span><span class="token keyword">:</span> <span class="token string">"npm run build &amp;&amp; npm test"</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><ul><li><code>name</code> - name it</li><li><code>description</code> - describe it</li><li><code>version</code> - use <a target="_blank" href="https://semver.org/">semantic versioning</a> for <a target="_blank" href="https://github.com/changesets/changesets">changesets</a></li><li><code>keywords</code> - add keywords</li><li><code>homepage</code> - add your homepage</li><li><code>bugs</code> - where to report bugs</li><li><code>author</code> - who created this</li><li><code>repository</code> - where to view git source</li><li><code>files</code> - the folder to install</li><li><code>type</code> - use module for TypeScript</li><li><code>main</code> - entry point for node</li><li><code>scripts</code>
 <ul><li><code>build</code> - build TypeScript file</li><li><code>test</code> - test with vitest</li><li><code>ci</code> - continuous integration command for workflow</li></ul></li></ul><h2 id="install-dependencies">Install Dependencies</h2><ul><li><code>npm i -D typescript</code> to install it as development dependency</li><li><code>npm i -D vitest</code> for testing</li></ul><h2 id="config-typescript">Config TypeScript</h2><ul><li>Create <code>tsconfig.json</code> file.</li></ul><pre class=" language-json"><code class="prism  language-json"><span class="token punctuation">{</span>
    <span class="token string">"compilerOptions"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
        <span class="token string">"esModuleInterop"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">"skipLibCheck"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">"target"</span><span class="token punctuation">:</span> <span class="token string">"es2022"</span><span class="token punctuation">,</span>
        <span class="token string">"allowJs"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">"resolveJsonModule"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">"moduleDetection"</span><span class="token punctuation">:</span> <span class="token string">"force"</span><span class="token punctuation">,</span>
        <span class="token string">"isolatedModules"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">"verbatimModuleSyntax"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">"strict"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">"noUncheckedIndexedAccess"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">"noImplicitOverride"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">"module"</span><span class="token punctuation">:</span> <span class="token string">"NodeNext"</span><span class="token punctuation">,</span>
        <span class="token string">"outDir"</span><span class="token punctuation">:</span> <span class="token string">"dist"</span><span class="token punctuation">,</span>
        <span class="token string">"rootDir"</span><span class="token punctuation">:</span> <span class="token string">"src"</span><span class="token punctuation">,</span>
        <span class="token string">"sourceMap"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">"declaration"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
        <span class="token string">"declarationMap"</span><span class="token punctuation">:</span> <span class="token boolean">true</span>
    <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>See <a target="_blank" href="https://www.totaltypescript.com/how-to-create-an-npm-package#32-set-up-a-tsconfigjson">Matt Pocock&rsquo;s Total TypeScript</a> for more info and if you want prettier, etc.</p><h2 id="add-your-typescript-content">Add Your TypeScript Content</h2><p>For this test package, we are updating the price to standard format.</p><ul><li><code>/src/index.ts</code></li></ul><pre class=" language-tsx"><code class="prism  language-tsx">export const formatPrice = (price: number) =&gt; {
    return new Intl.NumberFormat('en-US', {
        style: 'currency',
        currency: 'USD',
    }).format(price);
};
</code></pre><h3 id="add-testing-script">Add Testing Script</h3><ul><li><code>/src/index.test.ts</code></li></ul><pre class=" language-tsx"><code class="prism  language-tsx">import { formatPrice } from "./index.js";
import { test, expect } from "vitest";

test("formatPrice", () =&gt; {
    expect(formatPrice(1234.56)).toBe("$1,234.56");
});

test("formatPrice with small value", () =&gt; {
    expect(formatPrice(5)).toBe("$5.00");
});
</code></pre><p><strong>Note:</strong> You can test your package with <code>npm run test</code>, which just runs <code>npx vitest run</code> under the hood.</p><h2 id="workflow-1">Workflow 1</h2><p>GitHub has the ability to run scripts once you commit. Every workflow file will be a YAML file with the extension <code>.yml</code>.</p><h3 id="create-default-continuous-integration-workflow">Create Default Continuous Integration Workflow</h3><ul><li><code>.github/workflows/ci.yml</code></li></ul><pre class=" language-json"><code class="prism  language-json">name<span class="token punctuation">:</span> CI

on<span class="token punctuation">:</span>
    pull_request<span class="token punctuation">:</span>
    push<span class="token punctuation">:</span>
        branches<span class="token punctuation">:</span>
            <span class="token operator">-</span> main

concurrency<span class="token punctuation">:</span>
    group<span class="token punctuation">:</span> $<span class="token punctuation">{</span><span class="token punctuation">{</span> github<span class="token punctuation">.</span>workflow <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> github<span class="token punctuation">.</span>ref <span class="token punctuation">}</span><span class="token punctuation">}</span>
    cancel<span class="token operator">-</span><span class="token keyword">in</span><span class="token operator">-</span>progress<span class="token punctuation">:</span> <span class="token boolean">true</span>

jobs<span class="token punctuation">:</span>
    ci<span class="token punctuation">:</span>
        runs<span class="token operator">-</span>on<span class="token punctuation">:</span> ubuntu<span class="token operator">-</span>latest

        steps<span class="token punctuation">:</span>
            <span class="token operator">-</span> uses<span class="token punctuation">:</span> actions<span class="token operator">/</span>checkout@v4

            <span class="token operator">-</span> name<span class="token punctuation">:</span> Use Node<span class="token punctuation">.</span>js
              uses<span class="token punctuation">:</span> actions<span class="token operator">/</span>setup<span class="token operator">-</span>node@v4
              <span class="token keyword">with</span><span class="token punctuation">:</span>
                  node<span class="token operator">-</span>version<span class="token punctuation">:</span> <span class="token string">'20'</span>

            <span class="token operator">-</span> name<span class="token punctuation">:</span> Install dependencies
              run<span class="token punctuation">:</span> npm install

            <span class="token operator">-</span> name<span class="token punctuation">:</span> Run CI
              run<span class="token punctuation">:</span> npm run ci
</code></pre><ul><li>This will install node, install dependencies and run the continuous integration script.</li></ul><h3 id="github">GitHub</h3><p>When you commit your package, you will see the workflow runs in the Action tab on GitHub.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image.png?sfvrsn=ab1ba40b_2" alt="Action tab GitHub" /></p><p>This workflow runs <code>npm run ci</code>, which basically makes sure the tests pass. It is queued first and then run.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-1.png?sfvrsn=112577e2_2" alt="npm run cli" /></p><p>And if it does not pass, you will see an error.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-2.png?sfvrsn=2446f684_2" alt="error" /></p><p>Your last commit will fail.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-3.png?sfvrsn=558b093e_2" alt="commit fail" /></p><p>Which also notifies you in your email.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-4.png?sfvrsn=bbeb2636_2" alt="commit fail email" /></p><p>And, of course, you see a check once you re-commit with tests that pass.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-5.png?sfvrsn=4b9abcdc_2" alt="fixed" /></p><h2 id="versioning-with-changeset">Versioning with ChangeSet</h2><p>Use <a target="_blank" href="https://github.com/changesets/changesets">changesets</a> for versioning.</p><ul><li>Install it with <code>npm i -D @changesets/cli</code></li><li>Initialize it with <code>npx changeset init</code></li></ul><h3 id="create-a-new-version">Create a New Version</h3><ul><li>Run <code>npx changeset</code>
 <ul><li>Select <code>patch</code>, <code>minor</code> or <code>major</code> for the type of update.</li><li>Enter a summary for the log describing the change.</li><li>You will see a new file under <code>.changeset</code> like <code>smooth-ads-lick.md</code> or some random name. This will be the log that will be appended to your root <a target="_blank" href="http://CHANGELOG.md"><code>CHANGELOG.md</code></a> file.</li></ul></li><li>Run <code>npx changeset version</code> to automatically update your package version.
    <ul><li>The generated file <a target="_blank" href="http://smooth-ads-lick.md"><code>smooth-ads-lick.md</code></a> (will be different name) has been deleted, and your log in the root <code>CHANGELOG.md</code> file has been appended.</li></ul></li></ul><pre class=" language-markdown"><code class="prism  language-markdown"><span class="token title important"><span class="token punctuation">#</span> format-price</span>

<span class="token title important"><span class="token punctuation">##</span> 1.0.2</span>

<span class="token title important"><span class="token punctuation">###</span> Patch Changes</span>

<span class="token list punctuation">-</span> testing for log

<span class="token title important"><span class="token punctuation">##</span> 1.0.1</span>

<span class="token title important"><span class="token punctuation">###</span> Patch Changes</span>

<span class="token list punctuation">-</span> first patch
</code></pre><ul><li>Commit to GitHub as is:
    <ul><li><code>git add .</code></li><li><code>git commit -m 'patch'</code></li><li><code>git push</code></li></ul></li></ul><h3 id="github-1">GitHub</h3><p>Now, let&rsquo;s set up for auto publishing.</p><p>Go to <code>Settings</code> in your GitHub project, then click <code>Actions</code> and <code>General</code> on the left menu.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/screenshot_2026-03-07_191810.png?sfvrsn=d7727404_2" alt="Screenshot 2026-03-07 191810.png" /></p><ul><li>You need to allow GitHub to publish to npm with read and write permissions.</li></ul><h3 id="optional-github-ruleset">Optional GitHub Ruleset</h3><p>Under <code>Rules</code> and then <code>Rulesets</code>, it is wise (you will get a warning anyway) to protect your main branch.</p><ul><li>Name the ruleset <code>Restrict Deletions</code> or whatever you like.</li><li>Make sure <code>default</code> is selected under target branches.</li><li>By default, <code>Restrict deletions</code> and <code>Block force pushes</code> will be selected.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-6.png?sfvrsn=b4303860_2" alt="GitHub Ruleset" /></p><h2 id="workflow-2">Workflow 2</h2><p>Create the second workflow file in the <code>.github/workflows</code> folder called <code>release.yml</code>.</p><pre class=" language-markdown"><code class="prism  language-markdown">name: Publish Package

on:
<span class="token code keyword" spellcheck="false">    push:</span>
<span class="token code keyword" spellcheck="false">        branches:</span>
<span class="token code keyword" spellcheck="false">            - main</span>

permissions:
<span class="token code keyword" spellcheck="false">    contents: write # required for tags + version PR</span>
<span class="token code keyword" spellcheck="false">    pull-requests: write # required for Version Packages PR</span>
<span class="token code keyword" spellcheck="false">    id-token: write # required for npm trusted publishing (OIDC)</span>

jobs:
<span class="token code keyword" spellcheck="false">    publish:</span>
<span class="token code keyword" spellcheck="false">        runs-on: ubuntu-latest</span>

<span class="token code keyword" spellcheck="false">        steps:</span>
<span class="token code keyword" spellcheck="false">            - uses: actions/checkout@v4</span>
<span class="token code keyword" spellcheck="false">              with:</span>
<span class="token code keyword" spellcheck="false">                  fetch-depth: 0 # Changesets needs tag + commit history</span>

<span class="token code keyword" spellcheck="false">            - uses: actions/setup-node@v4</span>
<span class="token code keyword" spellcheck="false">              with:</span>
<span class="token code keyword" spellcheck="false">                  node-version: '20'</span>
<span class="token code keyword" spellcheck="false">                  registry-url: 'https://registry.npmjs.org'</span>

<span class="token code keyword" spellcheck="false">            - name: Update npm</span>
<span class="token code keyword" spellcheck="false">              run: npm install -g npm@latest</span>

<span class="token code keyword" spellcheck="false">            - name: Install dependencies</span>
<span class="token code keyword" spellcheck="false">              run: npm ci</span>

<span class="token code keyword" spellcheck="false">            - name: Build (if needed)</span>
<span class="token code keyword" spellcheck="false">              run: npm run build --if-present</span>

<span class="token code keyword" spellcheck="false">            - name: Run tests</span>
<span class="token code keyword" spellcheck="false">              run: npm test</span>

<span class="token code keyword" spellcheck="false">            - name: Changesets version / publish</span>
<span class="token code keyword" spellcheck="false">              uses: changesets/action@v1</span>
<span class="token code keyword" spellcheck="false">              with:</span>
<span class="token code keyword" spellcheck="false">                  # When there *are* pending changesets &rarr; create/update Version Packages PR</span>
<span class="token code keyword" spellcheck="false">                  version: npm run changeset:version</span>

<span class="token code keyword" spellcheck="false">                  # After the Version Packages PR is merged &rarr; publish &amp; create tags</span>
<span class="token code keyword" spellcheck="false">                  publish: npm run changeset:publish</span>
<span class="token code keyword" spellcheck="false">              env:</span>
<span class="token code keyword" spellcheck="false">                  GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}</span>
<span class="token code keyword" spellcheck="false">                  NPM_CONFIG_PROVENANCE: true</span>
</code></pre><p>This workflow will run the tests with <code>npm test</code>, then create a PR. Once the PR is merged (this is manual for security&mdash;you don&rsquo;t want this automatic), the version is updated, and that version is published to npm.</p><p><strong>Note:</strong> Notice the permissions at the top. We need this to allow changes to the version. This is saved in GitHub under <code>Releases</code> and <code>Tags</code>.</p><h2 id="publish-to-npm">Publish to npm</h2><p>This assumes you have an npm account. You can create one at <a target="_blank" href="https://www.npmjs.com/">npm</a> if you don&rsquo;t have one. Make sure to enable 2FA.</p><p>Publish your first version to npm by running.</p><ul><li><code>npm login</code> &ndash; You will log in with the browser, then enter your 2FA code.</li><li>Run <code>npm publish --access public</code> to publish a public package.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-7.png?sfvrsn=62b98a10_2" alt="npm 11 packages" /></p><p>Once you publish, you will see your package published in npm. This means anyone can install it with <code>npm i format-price</code> in their repo.</p><p><strong>Note:</strong> Our final goal is to publish automatically with a git commit.</p><h3 id="configure-github-publishing">Configure GitHub Publishing</h3><p>Click on the package, then go to <code>Settings</code>.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-8.png?sfvrsn=f8e3f7da_2" alt="Settings" /></p><ul><li>Create a new Connection to GitHub Actions.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-9.png?sfvrsn=fc949920_2" alt="Trusted Publisher page with fields for org or user, repo, workflow filename, environment name - to set up new connection" /></p><ul><li>Enter your GitHub username and repository, as well as your YAML file <code>release.yml</code>.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-10.png?sfvrsn=d5c36c46_2" alt="prepped trusted publisher can be edited or deleted" /></p><ul><li>You will see a connection enabled for GitHub to be a Trusted Publisher.</li></ul><h3 id="auto-publishing">Auto Publishing</h3><p>Now that the connection is setup, you can publish automatically.</p><ul><li>Create a change to your file.</li><li>Run <code>npm run build</code> and then whatever other formatting or pre-publishing commands you use.</li><li>Run <code>npx changeset</code> to create a new version. You can use <code>patch</code> for testing.</li><li>Create a new commit
    <ul><li><code>git add .</code></li><li><code>git commit -m 'auto publish'</code></li><li><code>git push</code></li></ul></li><li>Go to your project on GitHub, and you should see a successful Pull Request.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-11.png?sfvrsn=1ff07600_2" alt="Pull requests - 1- version packages" /></p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-12.png?sfvrsn=1eac6d67_2" alt="successful pull request - no conflicts with base branch" /></p><ul><li>Click the <code>Merge pull request</code> button to merge the versioning and changes. It will also auto publish and auto update your versions. Feel free to add any more description info for the PR.</li><li>Wait a few minutes for the publish to update, and you will now see a <code>Releases</code> option in your GH sidebar.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-05/image-13.png?sfvrsn=a85f1001_2" alt="GitHub sidebar includes Releases" /></p><ul><li>Don&rsquo;t forget to pull the version changes locally with <code>git pull</code>.</li></ul><p><strong>You now have a fully automated npm publishing pipeline!</strong></p><p>You can install your package anywhere with <code>npm i format-price</code>!</p><p><strong>Example Repo:</strong> <a target="_blank" href="https://github.com/jdgamble555/format-price">GitHub</a></p><hr /><p><strong>Read next:</strong> <a target="_blank" href="https://www.telerik.com/blogs/typedb-graph-database-things">TypeDB: A Graph Database for Things</a></p><img src="https://feeds.telerik.com/link/10827/17352916.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:5c3a107b-0444-4d8f-a104-97f53edd0760</id>
    <title type="text">How a 6-Person Team Shipped an AI-First Platform with KendoReact</title>
    <summary type="text">For teams building AI-driven applications with complex workflows, treating the UI layer as infrastructure can dramatically reduce friction as the product evolves. For Icanpreneur, KendoReact became that foundation.</summary>
    <published>2026-05-28T14:47:38Z</published>
    <updated>2026-06-20T12:08:23Z</updated>
    <author>
      <name>Kathryn Grayson Nanz </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/10827/17350156/how-a-6-person-team-shipped-an-ai-first-platform-with-kendoreact"/>
    <content type="text"><![CDATA[<p><span class="featured">For teams building AI-driven applications with complex workflows, treating the UI layer as infrastructure can dramatically reduce friction as the product evolves. For Icanpreneur, KendoReact became that foundation.</span></p><p>Wiring up an LLM endpoint takes an afternoon; the harder engineering problem is building a UI layer that can absorb the inherent unpredictability of AI without introducing layout thrashing, inconsistent interaction patterns or sprawl that a small team can't maintain. </p><p>That's the problem Icanpreneur solved with KendoReact. </p><blockquote>&ldquo;The hard part with AI is not just calling a model &ndash; it&rsquo;s designing complex, trustworthy workflows around it. That&rsquo;s where KendoReact helped a lot.&rdquo; </blockquote><p><a href="https://www.icanpreneur.com/">Icanpreneur&rsquo;s platform</a> orchestrates guided, AI-assisted workflows that blend business logic, structured data and real-time feedback into a familiar, approachable experience. Users move through Lean Canvas modeling, validation flows and strategic planning steps with AI augmenting their thinking along the way. They&rsquo;re now used not only by early-stage founders but also by accelerators, innovation hubs and product teams inside organizations such as Founder Institute, Campus X, Science Park Graz, ABLE Activator, Sofia Tech Park, Visa Innovation Program Europe and Telerik Academy&rsquo;s Upskill Product Management program. </p><p>Raw AI output can be unpredictable. Without a stable, consistent UI foundation, that translates into friction and mistrust. The consistency, predictability and performance of <a href="https://www.telerik.com/kendo-react-ui">KendoReact</a> in the UI layer turned the output of Icanpreneur&rsquo;s AI assistant, IVA, into something usable and trustworthy. For Icanpreneur&rsquo;s six-person team, KendoReact was the infrastructure that made AI usable at scale. </p><h2>Icanpreneur&rsquo;s Architecture </h2><p>At a high level, Icanpreneur&rsquo;s platform is structured as a layered system that separates UI composition and user interaction, server / API management and LLM orchestration.</p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/screenshot-2026-05-28-at-9-44-37-am.png?sfvrsn=127900_2" height="844" style="max-width:100%;height:auto;" title="Screenshot 2026-05-28 at 9.44.37 AM" width="1016" alt="A graphic depiction of the frontend architecture " sf-size="411556" /><p>KendoReact is the core of the Icanpreneur UI layer, allowing them to guide users through complex multi-pane screens, support long-running workflows with consistent UI patterns and handle advanced multi-step journeys without overwhelm. Every major module (Lean Canvas, conversational interview console, go-to-market editor, persona builder) is composed from the same KendoReact primitive set, themed consistently and governed by the same layout contracts. That decision paid compounding dividends as the product scaled. </p><p>In an AI-first platform, it can be tempting to start thinking about the UI as set dressing; just a thin layer over the APIs to make things &ldquo;look pretty&rdquo; for the users. However, AI interaction patterns are still very new and unfamiliar to many users. A UI that naturally folds AI into the user experience can be a real differentiator in the competitive market. </p><blockquote>&ldquo;Founders in partner programs started telling us that the interview, insights and go-to-market flow &lsquo;feels like one tool &ndash; simple and intuitive, not five stitched together.'&rdquo; </blockquote><p>AI technology is impressive but UI engineers are still the ones who translate that potential into true value for the user. In Icanpreneur&rsquo;s case, they needed stable layout primitives, reliable form controls and high-performance data visualization components &ndash; all of which had to present AI output reliably (while responses were streaming, partial or evolving) without triggering unnecessary re-renders or layout shifts. KendoReact provided that and more, empowering the team to focus their effort on user experience, business logic and AI orchestration. </p><h3>Composability as a Force Multiplier </h3><p>One of the most important architectural decisions was treating KendoReact not as a collection of finished widgets or mere building blocks to be combined but as true UI infrastructure. KendoReact powers everything from research dashboards and conversational interview consoles to mini-CRMs and multi-step go-to-market editors.</p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/screenshot-2026-05-28-at-9-45-10-am.png?sfvrsn=1b568e58_2" height="802" style="max-width:100%;height:auto;" title="Screenshot 2026-05-28 at 9.45.10 AM" width="1370" alt="A screenshot of the Icanpreneur interface, built with KendoReact components" sf-size="418054" /><p>Foundation pieces were combined to create higher-order workflows that guide users through their interactions with IVA. Rather than building custom UI for each flow, the team defined composable patterns built on KendoReact primitives. For example, the multi-step validation flow reused the same layout + navigation structure, AI feedback panels reused consistent container and typography patterns and interview summaries reused standardized layout and card structures. Because the Icanpreneur team didn&rsquo;t need to design complex new interaction patterns for each additional feature, they were able to implement quickly and iterate fast &ndash; smoothly layering their AI workflows on top of KendoReact&rsquo;s component system. </p><h3>Consistent UX for Novel AI Workflows </h3><p>For Icanpreneur users, this meant that they never had to open a page and feel unsure of where to go or what to do next &ndash; even though the AI-powered technology may be new, it leveraged familiar and consistent patterns to guide them through the experience. </p><blockquote>&ldquo;Because all the AI-driven experiences reuse the same KendoReact components as the rest of the app, they behave in a predictable way. Users don&rsquo;t have to &lsquo;learn' a new interface just because AI is involved &ndash; it feels like one coherent workspace.&rdquo; </blockquote><h3>Design-to-Code Fidelity </h3><p>With KendoReact, Icanpreneur designers and engineers worked from the same component language, which meant no translation layer between design and implementation, no pixel-chasing and no divergence between what's mocked and what ships. Designers also created a custom design system using the <a href="https://www.telerik.com/figma-kits">Kendo UI Figma Kits</a> and the <a href="https://www.telerik.com/design-system/docs/">Progress Design System Kit</a>, which greatly reduced the time needed to create mockups and new pages. </p><blockquote>&ldquo;Using the Kendo UI Figma Kits and a custom Kendo theme, we aligned design and development from day one. Most new features now start as a quick sketch in our KendoReact-based design system and turn into a working screen in days instead of weeks.&rdquo; </blockquote><h3>Increased Development Speed </h3><p>AI-assisted workflows evolve quickly: new steps get added, feedback formats change and validation criteria expand. Because KendoReact components are extensible and <a href="https://www.telerik.com/kendo-react-ui/components/styling">themeable</a>, the Icanpreneur team could meet these challenges while still preserving UX consistency. As workflows grew, the UI layer remained adaptable; less time debugging or re-writing UI logic meant faster revision cycles and more shipped features. </p><blockquote>&ldquo;New workflow-style features (such as a new research flow or AI-assisted editor) now typically go from idea to shipped version in days instead of weeks, because we mostly compose existing KendoReact patterns instead of building UI from scratch.&rdquo; </blockquote><h2>The UX of AI </h2><p>Some of the biggest challenges in AI-first applications are handling uncertainty (usually in the form of partial / still evolving responses) and guiding users through new workflows. The Icanpreneur team leveraged KendoReact&rsquo;s design tools to create UX patterns that integrate AI feedback into existing, structured UI flows, so users are never left wondering &ldquo;what now?&rdquo;.</p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/screenshot-2026-05-28-at-9-46-59-am.png?sfvrsn=e6e4d053_2" height="836" style="max-width:100%;height:auto;" title="Screenshot 2026-05-28 at 9.46.59 AM" width="1370" alt="A screenshot of the Icanpreneur interface, built with KendoReact components" sf-size="477986" /><p>One of the most unique features of Icanpreneur is their Synthetic Customer Interview feature, which allows their AI assistant, IVA, to answer questions as though it was a potential customer in their target market. Not all teams have easy access to customers to run interviews with, so this allows founders to &ldquo;stress-test&rdquo; their hypotheses quickly across multiple scenarios. That output helps them refine their questions and assumptions before talking to real people. Afterwards, IVA reviews all the data (across both real and synthetic customer interviews) to summarize, highlight patterns and extract quotes and evidence that can be leveraged in personas and further market research. </p><blockquote>&ldquo;When we designed IVA, the conversational interview console and the research workspace, we could prototype and ship quickly because we already had chat-style layouts built from existing KendoReact <a href="https://www.telerik.com/kendo-react-ui/components/layout">Layout</a> and <a href="https://www.telerik.com/kendo-react-ui/components/form">Form</a> components, multi-step flows for things like research setup and go-to-market editors and reusable <a href="https://www.telerik.com/kendo-react-ui/components/layout/expansionpanel">Panels</a>, <a href="https://www.telerik.com/kendo-react-ui/components/layout/drawer">Drawers</a>, <a href="https://www.telerik.com/kendo-react-ui/components/dialogs/dialog">Dialogs</a> and <a href="https://www.telerik.com/kendo-react-ui/components/grid">Data Grids</a> for displaying AI outputs, suggestions and insights aggregation&rdquo; </blockquote><p>Using KendoReact meant that not only could they leverage these familiar user patterns &ndash; but also that common AI concerns (such as slow, partial or unexpected responses) could be handled with UI structures and error responses that users already knew how to interact with. </p><h2>Performance and Scalability </h2><p>Icanpreneur workflows are complex, multi-stage journeys. Moving from idea to hypothesis, validating with AI or human-led interviews, identifying patterns and extracting valuable feedback, generating personas and finally creating landing pages or pitch decks &ndash; any one of these alone would be demanding but all together they offer a true development challenge. Each step builds upon the previous and the context must be preserved as the user moves between them. Without careful performance engineering, rendering and re-rendering these views would quickly degrade the user experience. </p><p>AI workflows can introduce frequent state updates as responses stream in or evolve - for example, when IVA synthesizes interview insights or drafts positioning. In poorly structured UIs, this can lead to layout thrashing or lag. However, these updates render inside structured, well-optimized KendoReact components, so the Icanpreneur UI remains stable.</p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/screenshot-2026-05-28-at-9-47-32-am.png?sfvrsn=c831f8dc_2" height="818" style="max-width:100%;height:auto;" title="Screenshot 2026-05-28 at 9.47.32 AM" width="1380" alt="A screenshot of the Icanpreneur interface, built with KendoReact components" sf-size="752777" /><p>As features accumulate, custom CSS and one-off component implementations are a common source of bundle bloat and regression risk. At Icanpreneur, new features and modules all plug into the same KendoReact UI system (rather than introducing new patterns and components). That allows even a small team to control UI sprawl and manage bundle growth. Custom CSS &ndash; a common pain point for fast-growing applications &ndash; is significantly reduced and centralized, because the UI is themed consistently. This kept initial load times reasonable even as the feature surface expanded. </p><blockquote>&ldquo;When we introduce new AI-driven experiences, they use the same KendoReact components, so we see far fewer UI regressions. That made it much easier to roll out new AI features without exploding our QA surface.&rdquo; </blockquote><p>That smaller, bounded QA surface meant faster turnaround and faster time to ship, while the lower complexity means that the small team was able to manage the expedited growth without being overwhelmed. New features that reuse existing KendoReact components inherit known-good behavior, so regression risk didn't scale with feature additions. </p><p>KendoReact&rsquo;s components are designed for high-density, data-heavy enterprise applications; no reinvention (or re-optimization) was required even for complex components like grids, forms and dialogs. KendoReact reduced the need to solve hard performance problems manually, allowing Icanpreneur to access enterprise-level quality with a startup-size team. </p><h2>Icanpreneur: Powered by KendoReact </h2><p>Icanpreneur began as a structured way to guide founders from idea to product-market fit. Today it operates as a full AI co-founder, with IVA empowering entrepreneurs to ideate, validate and grow their companies as a trusted partner. </p><p>By treating the UI layer as infrastructure and building on KendoReact from day one, the team was able to: </p><ul><li>Scale complex, AI-driven workflows with consistency </li></ul><ul><li>Ship new features rapidly without fragmenting UX </li></ul><ul><li>Deliver a coherent experience across classic and AI-powered screens </li></ul><p>For teams building AI-driven workflows, the UI layer is crucial: it's what determines whether AI output becomes a usable, trustworthy product or a source of friction. Icanpreneur's architecture is a case study in treating that layer seriously from day one. Or, as the Icanpreneur team said themselves: </p><blockquote>&ldquo;A six-person core team is able to maintain and evolve a fairly large, AI-first product (research, interviews, personas, positioning, landing pages, sales decks, etc.) without a separate &ldquo;component team&rdquo; or design system squad &ndash; KendoReact is that system for us.&rdquo; </blockquote><p>For teams building AI-driven applications with complex workflows, treating the UI layer as infrastructure can dramatically reduce friction as the product evolves. For Icanpreneur, KendoReact became that foundation: explore how KendoReact could become that foundation for your team, as well.</p><img src="https://feeds.telerik.com/link/10827/17350156.gif" height="1" width="1"/>]]></content>
  </entry>
</feed>
