<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~files/atom-premium.xsl"?>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedpress="https://feed.press/xmlns" xmlns:media="http://search.yahoo.com/mrss/" xmlns:podcast="https://podcastindex.org/namespace/1.0">
  <feedpress:locale>en</feedpress:locale>
  <link rel="hub" href="https://feedpress.superfeedr.com/"/>
  <logo>https://static.feedpress.com/logo/telerik-blogs-web-react-61850d7ea2a5d.jpg</logo>
  <title type="text">Telerik Blogs | Web | React</title>
  <subtitle type="text">The official blog of Progress Telerik - expert articles and tutorials for developers.</subtitle>
  <id>uuid:0caf40ea-26fa-4f73-a9d9-c30735ae69c1;id=3284</id>
  <updated>2026-07-05T18:13:58Z</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-react"/>
  <entry>
    <id>urn:uuid:1bd8e293-fa30-4fab-803e-f42e54e29d42</id>
    <title type="text">Check Out These Amazing Projects from the Progress x GitNation Hackathon!</title>
    <summary type="text">We hosted a hackathon in partnership with GitNation, and the response blew us away. In just 48 hours, we got over 30 finished projects! Check out our winners and some of our favorites.</summary>
    <published>2026-06-29T16:35:30Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>Kathryn Grayson Nanz </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17369834/check-out-amazing-projects-progress-gitnation-hackathon"/>
    <content type="text"><![CDATA[<p><span class="featured">Our Progress x GitNation hackathon at React Summit and JSNation netted 30+ projects that blew us away. Check out our winners and some of our favorites.</span></p><p>A few weeks ago, the Progress team headed to Amsterdam for <a target="_blank" href="https://reactsummit.com/">React Summit</a> and <a target="_blank" href="https://jsnation.com/">JSNation</a>: two of the back-to-back biggest events on the JavaScript calendar. </p><p>Between the packed schedule, booth conversations, social events and time hanging out with a few hundred of your closest developer friends, it was a seriously week. Not to mention, our team finally got to spend some time together in-person&mdash;something that doesn&rsquo;t happen very often when you&rsquo;re scattered across the globe! Although we all left feeling exhausted (and at least in my case, wildly jet-lagged), it was absolutely worth the trip.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/1781519496198.jpg?sfvrsn=819290e8_2" height="575" style="max-width:100%;height:auto;" title="1781519496198" width="1280" alt="Kiril, on stage at React Summit. " sf-size="105038" /></p><p>Our own Kiril Peyanski took the React Summit stage to deliver a talk on generative frontend, exploring a new paradigm where LLMs generate the logic that maps data to UI using React Server Components and Server Functions to make interfaces that adapt to the user. And when I tell you the room was packed, I mean <em>literally</em> standing room only and overflowing out the door!</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/1781519492695.jpg?sfvrsn=f86ecc35_2" height="963" style="max-width:100%;height:auto;" title="1781519492695" width="1280" alt="Kiril&#39;s talk, overflowing with attendees " sf-size="204170" /></p><p>As part of the conference, we also hosted a hackathon in partnership with <a target="_blank" href="https://gitnation.com/">GitNation</a>, challenging attendees to build something that makes tech conferences better. We left the specifics up to our hackers. It could mean better for attendees, better for speakers, better for organizers or something else entirely. We wanted to see how creative folks would get, and the response blew us away. The hackathon lasted just 48 hours, and we had over 30 finished projects!</p><p>After judging (which, let me tell you, was a real challenge), three projects stood out from the crowd:</p><p><strong>Our grand prize winners, <a target="_blank" href="https://www.hackathonparty.com/hackathons/43/projects/502">HALLWAY</a>:</strong> The team behind HALLWAY built a mobile-first app that turns the chaos of a conference day into a personalized, connected flow. Answer a few quick questions, and it builds your schedule, fills your free gaps with curated intros to the right people, and places you in a small group for the evening. </p><p><strong>Our first runner-up, <a target="_blank" href="https://www.hackathonparty.com/hackathons/43/projects/593">Conf Pilot</a>:</strong> Conf Pilot is an MCP server that brings live conference schedules directly into AI chat. Ask "what&rsquo;s next?" and instead of a wall of text, you get a fully interactive, themed widget with track filters, live countdown timers and calendar links&mdash;all without leaving your AI assistant. One of the first projects to use the new MCP Apps structured content pattern, and a seriously impressive build for a one-person submission! </p><p><strong>Our second runner-up, <a target="_blank" href="https://www.hackathonparty.com/hackathons/43/projects/524">Stuck Stack</a>:</strong> Stuck Stack turns the conference into a live help marketplace. Post what you&rsquo;re blocked on, find someone nearby who can help you in five minutes, and watch blockers move across a live board from Open &rarr; Matched &rarr; Solved. Organizers get a real-time dashboard that can even detect when enough attendees are stuck on the same thing and suggest a pop-up help clinic on the spot. Clever, useful and beautifully designed.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/1781519491888.jpg?sfvrsn=6fdf2f90_2" height="575" style="max-width:100%;height:auto;" title="1781519491888" width="1280" alt="The Progress team on stage with the overall winners, team HALLWAY " sf-size="113685" /></p><p>However, we couldn&rsquo;t give prizes to everyone&mdash;as much as we wished we could! When I tell you the judging was a challenge, it&rsquo;s because we had other submissions like these that were so, <em>so</em> impressive. While these unfortunately didn&rsquo;t end up placing, we do want to make sure they get their time in the sun as well, so you can also appreciate their incredible work! </p><p><strong><a target="_blank" href="https://www.hackathonparty.com/hackathons/43/projects/579">The Hallway</a></strong> took a new swing at the age-old problem of networking. Your AI agent walks into the conference before you do, negotiates with other attendees&rsquo; agents to find the right matches and only reveals identities once both humans consent. </p><p><strong><a target="_blank" href="https://www.hackathonparty.com/hackathons/43/projects/592">Unison</a></strong> tackled something nobody else touched&mdash;language barriers. It dubs conference talks in real-time, letting speakers present in one language while every attendee hears it in their own, with no headsets and about a three-second lag. Two-way, too: attendees can ask questions in their language and the speaker receives them in theirs.</p><p><strong><a target="_blank" href="https://www.hackathonparty.com/hackathons/43/projects/595">CrowdShift</a></strong> flipped the speaker experience on its head. Speakers normally prepare blind, with no idea who&rsquo;s actually in the room. CrowdShift pulls registration data and builds a continuously updated audience brief, so a speaker can see that their crowd shifted from 65% senior to 55% junior and adapt their talk in real time.</p><p><strong><a target="_blank" href="https://www.hackathonparty.com/hackathons/43/projects/588">ConferenceCast</a></strong> treated a multi-track conference like a TV broadcast: every room is a channel, and you get a personalized program guide with AI match scores, live session cards, countdown timers and a control-room dashboard for organizers to track what&rsquo;s working in real time.</p><p><strong><a target="_blank" href="https://www.hackathonparty.com/hackathons/43/projects/581">FireRaven Conference Hub </a></strong>went after a bigger problem: conference discovery is basically guesswork. Their platform gives speakers portable reputation profiles and lets attendees rate events across meaningful dimensions&mdash;expertise, clarity, practical impact and energy.</p><p><strong><a target="_blank" href="https://www.hackathonparty.com/hackathons/43/projects/573">Nexo</a></strong> kept it focused: tell it your role, goals and interests, and it builds you a focused conference plan with a clear explanation for every recommendation. Sometimes, the best solutions are the simple ones.</p><p><strong><a target="_blank" href="https://www.hackathonparty.com/hackathons/43/projects/580">Stage Deployer</a></strong> changes how you give talks. It lets speakers create small audience apps from a simple text prompt. Just type an idea, wait about two minutes, then share a QR code for the audience to scan. The interaction results appear live on the big screen as charts, rankings or word clouds.</p><p>We highly encourage you to <a target="_blank" href="https://www.hackathonparty.com/hackathons/43/projects">check out all the submissions in the project gallery </a>and see the incredible variety of things people built with Telerik and Kendo UI components from Progress Software.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/img_4975.jpeg?sfvrsn=f5e0c414_2" height="786" style="max-width:100%;height:auto;" title="IMG_4975" width="1179" alt="The Progress team in front of the booth at JS Nation / React SUmmit " sf-size="271594" /></p><p>Amsterdam delivered on all fronts: the venue was great, the crowd was friendly and the whole experience reminded us just how great it is to be able to spend time together with a bunch of folks all excited to geek out about the same stuff.</p><p><em>Huge</em> congrats to all three of our hackathon winning teams, and a massive well done to every single person who submitted. More than 30 projects in a couple of days is no joke! We had a fantastic time, and we&rsquo;re already looking forward to the next one.</p><hr /><h3>Ready to build your next React app?</h3><p><a href="https://www.telerik.com/kendo-react-ui" class="Btn" target="_blank">Explore KendoReact</a></p><img src="https://feeds.telerik.com/link/23056/17369834.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:0d4c4648-b7a2-4e28-9530-a66dd91961b4</id>
    <title type="text">REST to tRPC Migration in NestJS Monorepos</title>
    <summary type="text">Convert a shared monorepo with a NestJS REST API and Next.js to tRPC. Replace NestJS controllers with typed tRPC routers that expose procedures instead of endpoints, updating the frontend to consume those procedures with fully typed hooks and no manual type definitions.</summary>
    <published>2026-06-26T15:33:25Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>Christian Nwamba </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17368674/rest-trpc-migration-nestjs-monorepos"/>
    <content type="text"><![CDATA[<p><span class="featured">Convert a shared monorepo with a NestJS REST API and Next.js to tRPC. Replace NestJS controllers with typed tRPC routers that expose procedures instead of endpoints, updating the frontend to consume those procedures with fully typed hooks and no manual type definitions.</span></p><p>This post covers how to convert a shared monorepo with a <a target="_blank" href="https://nestjs.com/">NestJS</a> REST API and <a target="_blank" href="https://nextjs.org/">Next.js</a> to <a target="_blank" href="https://trpc.io/">tRPC</a>.</p><p>We&rsquo;ll replace NestJS controllers with typed tRPC routers that expose procedures instead of endpoints, and update our frontend to consume those procedures with fully typed hooks and no manual type definitions. By the end, you&rsquo;ll learn how to prevent backend-to-frontend API mismatches at compile time rather than in production.</p><h2 id="prerequisites">Prerequisites</h2><ul><li>Basic knowledge of <a target="_blank" href="https://nestjs.com/">NestJS</a> and <a target="_blank" href="https://www.typescriptlang.org/">TypeScript</a></li><li>Familiarity with REST APIs and how HTTP requests work</li><li>Node.js and pnpm installed</li><li>Basic knowledge of <a target="_blank" href="https://nextjs.org/">Next.js</a></li></ul><h2 id="why-choose-trpc-over-rest">Why Choose tRPC Over REST</h2><p>In a REST setup, you have type safety on the server and on the client, but there&rsquo;s a blind spot at the boundary between them. In a REST API, when the client fetches data, it has to manually define the response type. This is typically done by copying API specifications from the backend. However, because these definitions are copies, if changes are made on the backend without updating the client, no problems are noticed immediately, the builds still pass, but users encounter errors in production.</p><p>tRPC solves this by sharing the router type directly from the server to the client. The client uses the same type definitions as the backend. <a target="_blank" href="https://nestjs-trpc.io/">nestjs-trpc</a> creates the AppRouter type using our <a target="_blank" href="https://zod.dev/">Zod</a> output schemas, and the client imports this type directly.</p><p>If the backend changes, the type changes, and TypeScript immediately flags the call sites.</p><h2 id="project-setup">Project Setup</h2><p>This guide assumes you already have a NestJS REST API and a Next.js app running together in a pnpm monorepo. If you need a starting point, you can <a target="_blank" href="https://github.com/christiannwamba/trpc-starter">clone the starter repo here</a> and run the install command at the root:</p><pre class=" language-shell"><code class="prism  language-shell">pnpm install
</code></pre><p>We will be migrating the NestJS backend from REST to tRPC and updating the frontend to consume it with typed hooks. Here is what the structure will look like at the end:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/project-structure-diagram.png?sfvrsn=ddad2fc6_2" title="Project structure diagram" alt="Project structure diagram" /></p><h2 id="monorepo-foundation">Monorepo Foundation</h2><p>Since you already have a pnpm monorepo set up, there are just two things to add before we start the migration.</p><p>First, add the <code>@api/*</code> path alias to the existing <code>tsconfig.base.json</code> file:</p><pre class=" language-js"><code class="prism  language-js"><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">"baseUrl"</span><span class="token punctuation">:</span> <span class="token string">"."</span><span class="token punctuation">,</span>
    <span class="token string">"paths"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
      <span class="token string">"@api/*"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"apps/server/src/*"</span><span class="token punctuation">]</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>This alias allows the frontend to import types directly from the backend source without having to create and publish a package for sharing types.</p><p>Next, let&rsquo;s add scripts to our root <code>package.json</code> file so we can run commands more conveniently:</p><pre class=" language-js"><code class="prism  language-js"><span class="token punctuation">{</span>
<span class="token string">"scripts"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">"types:generate"</span><span class="token punctuation">:</span> <span class="token string">"pnpm --filter server exec nestjs-trpc generate --entrypoint src/app.module.ts"</span><span class="token punctuation">,</span>
    <span class="token string">"typecheck:web"</span><span class="token punctuation">:</span> <span class="token string">"pnpm --filter web run typecheck"</span><span class="token punctuation">,</span>
    <span class="token string">"verify:types"</span><span class="token punctuation">:</span> <span class="token string">"pnpm run types:generate &amp;&amp; pnpm run typecheck:web"</span><span class="token punctuation">,</span>
    <span class="token string">"dev:server"</span><span class="token punctuation">:</span> <span class="token string">"pnpm --filter server run start:dev"</span><span class="token punctuation">,</span>
    <span class="token string">"dev:web"</span><span class="token punctuation">:</span> <span class="token string">"pnpm --filter web run dev"</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the snippet above, <code>types:generate</code> runs the nestjs-trpc CLI to create the AppRouter type using our backend routers. <code>typecheck:web</code> runs <code>tsc --noEmit</code> on the Next.js app. <code>verify:types</code> combines both of them. We&rsquo;ll use these scripts in the type safety section. <code>dev:server</code> and <code>dev:web</code> are used to start the server and frontend, respectively.</p><h2 id="backend-setup">Backend Setup</h2><p>Run the command below to install the dependencies we&rsquo;ll need for this project:</p><pre class=" language-shell"><code class="prism  language-shell">cd apps/server
pnpm add nestjs-trpc @trpc/server zod superjson express
pnpm add -D @types/express
</code></pre><p>In the snippet above:</p><ul><li><strong>nestjs-trpc:</strong> Integrates tRPC into NestJS&rsquo;s module and DI system</li><li><strong>@trpc/server:</strong> The core tRPC server library</li><li><strong>zod:</strong> Handles input and output schema validation for each procedure</li><li><strong>superjson:</strong> The serializer that handles data types JSON can&rsquo;t handle natively, like dates, maps and sets</li><li><strong>express and @types/express:</strong> Used to properly type the request and response objects from <a target="_blank" href="https://expressjs.com/">Express</a></li></ul><p>Next, update your <code>app.module.ts</code> file with the following:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> Module <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@nestjs/common'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> TRPCModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'nestjs-trpc'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> superjson <span class="token keyword">from</span> <span class="token string">'superjson'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> AppContext <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./trpc/app.context'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> ProtectedMiddleware <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./trpc/protected.middleware'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> UsersModule <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./users/users.module'</span><span class="token punctuation">;</span>

@<span class="token function">Module</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  imports<span class="token punctuation">:</span> <span class="token punctuation">[</span>
    TRPCModule<span class="token punctuation">.</span><span class="token function">forRoot</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      context<span class="token punctuation">:</span> AppContext<span class="token punctuation">,</span>
      transformer<span class="token punctuation">:</span> superjson<span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    UsersModule<span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">,</span>
  providers<span class="token punctuation">:</span> <span class="token punctuation">[</span>AppContext<span class="token punctuation">,</span> ProtectedMiddleware<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">AppModule</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</code></pre><p>In the code snippet above, <code>TRPCModule.forRoot</code> wires tRPC into NestJS. The <code>context</code> option points to the class responsible for building the per-request context object, which is <code>AppContext</code> (we&rsquo;ll build this in the next section). The transformer option handles serialization: <code>superjson</code> extends JSON to correctly handle dates, maps, sets and undefined values across the wire. <code>UsersModule</code> is imported just as it would be in a REST app, we&rsquo;re still using the standard NestJS module structure.</p><p>Next, let&rsquo;s create a <code>src/trpc/app.context.ts</code> file and add the following to it:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> Injectable <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@nestjs/common'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">{</span> CreateExpressContextOptions <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@trpc/server/adapters/express'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">type</span> AppRequestContext <span class="token operator">=</span> <span class="token punctuation">{</span>
  req<span class="token punctuation">:</span> CreateExpressContextOptions<span class="token punctuation">[</span><span class="token string">'req'</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
  res<span class="token punctuation">:</span> CreateExpressContextOptions<span class="token punctuation">[</span><span class="token string">'res'</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">Injectable</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">AppContext</span> <span class="token punctuation">{</span>
  <span class="token keyword">async</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    req<span class="token punctuation">,</span>
    res<span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">:</span> CreateExpressContextOptions<span class="token punctuation">)</span><span class="token punctuation">:</span> Promise<span class="token operator">&lt;</span>AppRequestContext<span class="token operator">&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span> req<span class="token punctuation">,</span> res <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>While in REST NestJS the request is readily available via the <code>@Req decorator</code> or <code>guards</code>, in tRPC, context is the mechanism that carries per-request data, the raw request, the current user or anything a procedure might need. Procedures are individual callable endpoints defined inside a router (e.g., getUser and createUser in UsersRouter).</p><p><code>AppRequestContext</code> defines the shape of the context object that each procedure will receive. It has req and res, which are typed using <code>CreateExpressContextOptions</code>, which is a type that gives us the raw Express <code>req</code> and <code>res</code> objects from the incoming HTTP request.</p><p>The <code>create</code> method is called automatically by <code>nestjs-trpc</code> on every incoming request, and its return value becomes the <code>context</code> object. Notice it&rsquo;s only returning <code>req</code> and <code>res</code>. This is intentional, because context isn&rsquo;t where we guard the endpoints, but where we make the request data available to the middleware that will do the guarding. We&rsquo;ll show how this works in <code>ProtectedMiddleware</code>.</p><p>The flow is: <code>AppContext</code> creates the context, <code>ProtectedMiddleware</code> inspects it, and <code>procedures</code> run if the middleware allows them through.</p><h2 id="creating-our-router">Creating Our Router</h2><p>Next, a new file s<code>rc/users/users.schema.ts</code> and add the following to it:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> z <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'zod'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> userNameSchema <span class="token operator">=</span> z<span class="token punctuation">.</span><span class="token keyword">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> getUserOutputSchema <span class="token operator">=</span> z<span class="token punctuation">.</span><span class="token function">object</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  id<span class="token punctuation">:</span> z<span class="token punctuation">.</span><span class="token keyword">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  name<span class="token punctuation">:</span> userNameSchema<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> createUserOutputSchema <span class="token operator">=</span> z<span class="token punctuation">.</span><span class="token function">object</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  id<span class="token punctuation">:</span> z<span class="token punctuation">.</span><span class="token keyword">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  username<span class="token punctuation">:</span> z<span class="token punctuation">.</span><span class="token keyword">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  email<span class="token punctuation">:</span> z<span class="token punctuation">.</span><span class="token keyword">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">type</span> GetUserOutput <span class="token operator">=</span> z<span class="token punctuation">.</span>infer<span class="token operator">&lt;</span><span class="token keyword">typeof</span> getUserOutputSchema<span class="token operator">&gt;</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">type</span> CreateUserOutput <span class="token operator">=</span> z<span class="token punctuation">.</span>infer<span class="token operator">&lt;</span><span class="token keyword">typeof</span> createUserOutputSchema<span class="token operator">&gt;</span><span class="token punctuation">;</span>
</code></pre><p>These schemas serve two purposes: runtime validation via Zod and type generation via the nestjs-trpc CLI. The output schemas are what the frontend&rsquo;s AppRouter types will be derived from after we run <code>types:generate</code>.</p><p>Next, update the <code>src/users/users.service.ts</code> file with the following:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> Injectable <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@nestjs/common'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">{</span> CreateUserOutput<span class="token punctuation">,</span> GetUserOutput <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./users.schema'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">type</span> CreateUserInput <span class="token operator">=</span> <span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span> email<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">Injectable</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">UsersService</span> <span class="token punctuation">{</span>
  <span class="token function">findById</span><span class="token punctuation">(</span>id<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">)</span><span class="token punctuation">:</span> GetUserOutput <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span> id<span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'John Doe'</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  <span class="token function">create</span><span class="token punctuation">(</span>data<span class="token punctuation">:</span> CreateUserInput<span class="token punctuation">)</span><span class="token punctuation">:</span> CreateUserOutput <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token string">'1'</span><span class="token punctuation">,</span> <span class="token operator">...</span>data <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Our user service remains relatively unchanged. The business logic layer doesn&rsquo;t care whether it&rsquo;s being called by a REST controller or a tRPC router. We keep the same layered architecture we&rsquo;re used to in NestJS.</p><p>Create <code>src/users/users.router.ts</code> file and add the following to it:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> Router<span class="token punctuation">,</span> Query<span class="token punctuation">,</span> Mutation<span class="token punctuation">,</span> Input<span class="token punctuation">,</span> UseMiddlewares <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'nestjs-trpc'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> z <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'zod'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> ProtectedMiddleware <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../trpc/protected.middleware'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> UsersService <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./users.service'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> createUserOutputSchema<span class="token punctuation">,</span> getUserOutputSchema <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./users.schema'</span><span class="token punctuation">;</span>

@<span class="token function">Router</span><span class="token punctuation">(</span><span class="token punctuation">{</span> alias<span class="token punctuation">:</span> <span class="token string">'users'</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">UsersRouter</span> <span class="token punctuation">{</span>
  <span class="token keyword">constructor</span><span class="token punctuation">(</span><span class="token keyword">private</span> readonly usersService<span class="token punctuation">:</span> UsersService<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

  @<span class="token function">Query</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    input<span class="token punctuation">:</span> z<span class="token punctuation">.</span><span class="token function">object</span><span class="token punctuation">(</span><span class="token punctuation">{</span> id<span class="token punctuation">:</span> z<span class="token punctuation">.</span><span class="token keyword">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    output<span class="token punctuation">:</span> getUserOutputSchema<span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token function">getUser</span><span class="token punctuation">(</span>@<span class="token function">Input</span><span class="token punctuation">(</span><span class="token string">'id'</span><span class="token punctuation">)</span> id<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>usersService<span class="token punctuation">.</span><span class="token function">findById</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>

  @<span class="token function">Mutation</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    input<span class="token punctuation">:</span> z<span class="token punctuation">.</span><span class="token function">object</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      username<span class="token punctuation">:</span> z<span class="token punctuation">.</span><span class="token keyword">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">min</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      email<span class="token punctuation">:</span> z<span class="token punctuation">.</span><span class="token keyword">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">email</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>
    output<span class="token punctuation">:</span> createUserOutputSchema<span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
  @<span class="token function">UseMiddlewares</span><span class="token punctuation">(</span>ProtectedMiddleware<span class="token punctuation">)</span>
  <span class="token function">createUser</span><span class="token punctuation">(</span>@<span class="token function">Input</span><span class="token punctuation">(</span><span class="token punctuation">)</span> data<span class="token punctuation">:</span> <span class="token punctuation">{</span> username<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span> email<span class="token punctuation">:</span> <span class="token keyword">string</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>usersService<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In a regular REST controller, we&rsquo;d have <code>@Get('id')</code>. But in tRPC, we have <code>@Query</code>, and <code>@Post()</code> becomes <code>@Mutation()</code>.</p><p>Although the decorators look similar, the difference is in the input and output Zod schemas attached to each procedure. We use the input schema for validation, so the procedure only receives data in the exact shape it expects. The output schema determines the shape that the frontend will expect for the response body. <code>@UseMiddlewares(ProtectedMiddleware)</code> is a per-procedure guard applied to <code>createUser</code>. We&rsquo;ll discuss this next.</p><p>Update your <code>src/users/users.module.ts</code> file to register the router and export the service:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> Module <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@nestjs/common'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> UsersRouter <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./users.router'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> UsersService <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./users.service'</span><span class="token punctuation">;</span>

@<span class="token function">Module</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  providers<span class="token punctuation">:</span> <span class="token punctuation">[</span>UsersRouter<span class="token punctuation">,</span> UsersService<span class="token punctuation">]</span><span class="token punctuation">,</span>
  exports<span class="token punctuation">:</span> <span class="token punctuation">[</span>UsersService<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">UsersModule</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</code></pre><p><code>UsersRouter</code> and <code>UsersService</code> are registered as providers above. <code>nestjs-trpc</code> discovers <code>UsersRouter</code> automatically because it&rsquo;s decorated with <code>@Router</code>. Every module registers its routers in this same way.</p><p>We can now delete <code>users.controller.ts</code>, as it&rsquo;s no longer being referenced.</p><h2 id="protected-procedures">Protected Procedures</h2><p>Just like guards, we can add protection to an individual procedure or to the router as a whole.</p><p>Create a <code>src/trpc/protected.middleware.ts</code> file and add the following to it:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> Injectable <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@nestjs/common'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> TRPCError <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@trpc/server'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">{</span> Request <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'express'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">{</span> MiddlewareOptions<span class="token punctuation">,</span> TRPCMiddleware <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'nestjs-trpc'</span><span class="token punctuation">;</span>

<span class="token keyword">type</span> AuthUser <span class="token operator">=</span> <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token keyword">string</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">parseBearerToken</span><span class="token punctuation">(</span>header<span class="token punctuation">:</span> <span class="token keyword">string</span> <span class="token operator">|</span> undefined<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">string</span> <span class="token operator">|</span> <span class="token keyword">null</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>header<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">startsWith</span><span class="token punctuation">(</span><span class="token string">'Bearer '</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> token <span class="token operator">=</span> header<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token string">'Bearer '</span><span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> token<span class="token punctuation">.</span>length <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token operator">?</span> token <span class="token punctuation">:</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

@<span class="token function">Injectable</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">ProtectedMiddleware</span> <span class="token keyword">implements</span> <span class="token class-name">TRPCMiddleware</span> <span class="token punctuation">{</span>
  <span class="token keyword">async</span> <span class="token function">use</span><span class="token punctuation">(</span>opts<span class="token punctuation">:</span> MiddlewareOptions<span class="token operator">&lt;</span>object<span class="token punctuation">,</span> Record<span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token punctuation">,</span> unknown<span class="token operator">&gt;&gt;</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> <span class="token punctuation">{</span> ctx<span class="token punctuation">,</span> next <span class="token punctuation">}</span> <span class="token operator">=</span> opts<span class="token punctuation">;</span>
    <span class="token keyword">const</span> req <span class="token operator">=</span> <span class="token punctuation">(</span>ctx <span class="token keyword">as</span> <span class="token punctuation">{</span> req<span class="token operator">?</span><span class="token punctuation">:</span> Request <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span>req<span class="token punctuation">;</span>

    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>req<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">TRPCError</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        code<span class="token punctuation">:</span> <span class="token string">'INTERNAL_SERVER_ERROR'</span><span class="token punctuation">,</span>
        message<span class="token punctuation">:</span> <span class="token string">'Request missing from context.'</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">const</span> token <span class="token operator">=</span> <span class="token function">parseBearerToken</span><span class="token punctuation">(</span>req<span class="token punctuation">.</span>headers<span class="token punctuation">.</span>authorization<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>token <span class="token operator">!==</span> <span class="token string">'demo-token'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">TRPCError</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        code<span class="token punctuation">:</span> <span class="token string">'UNAUTHORIZED'</span><span class="token punctuation">,</span>
        message<span class="token punctuation">:</span> <span class="token string">'Authentication required.'</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span>

    <span class="token keyword">const</span> user<span class="token punctuation">:</span> AuthUser <span class="token operator">=</span> <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token string">'1'</span> <span class="token punctuation">}</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">{</span> ctx<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token operator">...</span>ctx<span class="token punctuation">,</span> user <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, <code>ProtectedMiddleware</code> is a class that implements <code>TRPCMiddleware</code>, and gives it a <code>use</code> method that runs automatically before any procedure it&rsquo;s applied to.</p><p><code>MiddlewareOptions</code> gives us <code>ctx</code> (the context object from AppContext) and <code>next</code> (which we call to pass control to the procedure). <code>parseBearerToken</code> pulls the token from the Authorization header. If the token check fails, a <code>TRPCError</code> with code <code>UNAUTHORIZED</code> is thrown and the procedure doesn&rsquo;t run. If it passes, we call <code>next()</code> with an enriched context that now includes the user.</p><p>That user object is now accessible inside the procedure through <code>ctx</code>, which is the same concept as <code>req.user</code> in a NestJS Guard.</p><h2 id="generating-the-approuter">Generating the AppRouter</h2><p>With our router and output schemas in place, run the CLI generator from the monorepo root:</p><pre class=" language-shell"><code class="prism  language-shell">npm run types:generate
</code></pre><p>This produces <code>apps/server/src/@generated/server.ts</code>, a file that contains the <code>AppRouter</code> type derived from our routers and Zod schemas. Do not manually edit this file, as it will be overwritten on every generation.</p><p>Next, create <code>src/trpc/schema.ts</code> to use as the stable re-export path for clients:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">export</span> <span class="token keyword">type</span> <span class="token punctuation">{</span> AppRouter <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../@generated/server'</span><span class="token punctuation">;</span>
</code></pre><h2 id="frontend-setup">Frontend Setup</h2><p>Let&rsquo;s install the dependencies we&rsquo;ll need for this project:</p><pre class=" language-shell"><code class="prism  language-shell">cd apps/web
pnpm add @trpc/client @trpc/server @trpc/react-query @tanstack/react-query superjson
</code></pre><p>Now, update your <code>tsconfig.json</code> file to add the <code>@api/*</code> path alias pointing to the server source, extend the base config, and set baseUrl to <code>.</code> so the path aliases resolve correctly. Merge this with your existing <code>tsconfig.json</code> instead of replacing it:</p><pre class=" language-js"><code class="prism  language-js"><span class="token punctuation">{</span>
  <span class="token string">"extends"</span><span class="token punctuation">:</span> <span class="token string">"../../tsconfig.base.json"</span><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">"baseUrl"</span><span class="token punctuation">:</span> <span class="token string">"."</span><span class="token punctuation">,</span>
    <span class="token string">"paths"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
      <span class="token string">"@/*"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"./src/*"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
      <span class="token string">"@api/*"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"../server/src/*"</span><span class="token punctuation">]</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>Next, create a <code>src/utils/trpc.ts</code> file and add the following to it:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token string">'use client'</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> createTRPCReact <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@trpc/react-query'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">{</span> AppRouter <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@api/trpc/schema'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> trpc <span class="token operator">=</span> createTRPCReact<span class="token operator">&lt;</span>AppRouter<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>In the code above, the <code>AppRouter</code> type import from <code>@api/trpc/schema</code> connects the frontend to the backend&rsquo;s type definitions. Every hook we call using trpc will have its input and output types pulled directly from the backend router.</p><p>Next, create a <code>src/app/providers.tsx</code> file and add the following to it:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token string">'use client'</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> useState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> QueryClient<span class="token punctuation">,</span> QueryClientProvider <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@tanstack/react-query'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> httpBatchLink <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@trpc/client'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> superjson <span class="token keyword">from</span> <span class="token string">'superjson'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> trpc <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@/utils/trpc'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">Providers</span><span class="token punctuation">(</span><span class="token punctuation">{</span> children <span class="token punctuation">}</span><span class="token punctuation">:</span> <span class="token punctuation">{</span> children<span class="token punctuation">:</span> React<span class="token punctuation">.</span>ReactNode <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>queryClient<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</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">new</span> <span class="token class-name">QueryClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>trpcClient<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
    trpc<span class="token punctuation">.</span><span class="token function">createClient</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
      links<span class="token punctuation">:</span> <span class="token punctuation">[</span>
        <span class="token function">httpBatchLink</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
          url<span class="token punctuation">:</span> <span class="token string">'http://localhost:3000/trpc'</span><span class="token punctuation">,</span>
          transformer<span class="token punctuation">:</span> superjson<span class="token punctuation">,</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      <span class="token punctuation">]</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  <span class="token punctuation">)</span><span class="token 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>trpc.Provider</span> <span class="token attr-name">client</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>trpcClient<span class="token punctuation">}</span></span> <span class="token attr-name">queryClient</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>queryClient<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>QueryClientProvider</span> <span class="token attr-name">client</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>queryClient<span class="token punctuation">}</span></span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>children<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>QueryClientProvider</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>trpc.Provider</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>In the code above, <code>Providers</code> sets up a React Query <code>QueryClient</code> for caching and a tRPC client configured with <code>httpBatchLink</code>, which points to the NestJS server and uses <code>superjson</code> for serialization. This matches the transformer we set on the backend.</p><p>Both clients are initialized with <code>useState</code> so they&rsquo;re created once per render tree. To make tRPC hooks available throughout our app, we wrap the root layout with <code>Providers</code>.</p><p>Update the <code>src/app/layout.tsx</code> to wrap children with Providers:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token keyword">import</span> type <span class="token punctuation">{</span> Metadata <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'next'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> Providers <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'./providers'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> metadata<span class="token punctuation">:</span> Metadata <span class="token operator">=</span> <span class="token punctuation">{</span>
  title<span class="token punctuation">:</span> <span class="token string">'NestJS tRPC Migration'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">RootLayout</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  children<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
  children<span class="token punctuation">:</span> React<span class="token punctuation">.</span>ReactNode<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>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>en<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>body</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Providers</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>children<span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Providers</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Let&rsquo;s now update the <code>src/app/page.tsx</code> to consume the tRPC query:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token string">'use client'</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> trpc <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@/utils/trpc'</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Home</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> data<span class="token punctuation">,</span> isLoading <span class="token punctuation">}</span> <span class="token operator">=</span> trpc<span class="token punctuation">.</span>users<span class="token punctuation">.</span>getUser<span class="token punctuation">.</span><span class="token function">useQuery</span><span class="token punctuation">(</span><span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token string">'1'</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>isLoading<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>Loading<span class="token operator">...</span><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 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">style</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> padding<span class="token punctuation">:</span> <span class="token string">'2rem'</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>h1</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>data<span class="token operator">?</span><span class="token punctuation">.</span>name <span class="token operator">?</span><span class="token operator">?</span> <span class="token string">''</span><span class="token punctuation">}</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Compared to REST, there&rsquo;s no manual <code>User</code> type definition and no risk of backend mismatches. If the <code>name</code> field doesn&rsquo;t exist on the output schema, TypeScript tells us immediately.</p><h2 id="proving-end-to-end-type-safety">Proving End-To-End Type Safety</h2><p>To test, we&rsquo;ll make a change to the backend definition and see if TypeScript flags the call site. In your <code>users.schema.ts</code> file, change <code>userNameSchema</code> from a plain string to a structured object:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token comment">// Before</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> userNameSchema <span class="token operator">=</span> z<span class="token punctuation">.</span><span class="token keyword">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment">// After</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> userNameSchema <span class="token operator">=</span> z<span class="token punctuation">.</span><span class="token function">object</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  firstName<span class="token punctuation">:</span> z<span class="token punctuation">.</span><span class="token keyword">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  lastName<span class="token punctuation">:</span> z<span class="token punctuation">.</span><span class="token keyword">string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Then, from the monorepo root, regenerate and typecheck:</p><pre class=" language-shell"><code class="prism  language-shell">npm run types:generate
npm run typecheck:web
</code></pre><p>Without touching the <code>page.tsx</code>, TypeScript will error. <code>data?.name</code> in <code>page.tsx</code> is no longer a string, it&rsquo;s <code>{ firstName: string; lastName: string }</code>, and TypeScript will flag every call site that references the old shape.</p><h2 id="conclusion">Conclusion</h2><p>In this post, we migrated a NestJS REST API to tRPC. Controllers were replaced with typed routers, we handled auth with middleware, and wired up our Next.js frontend to share types directly with the backend. With this setup, we&rsquo;ve learned how to reliably prevent backend-to-frontend API mismatches.</p><p>Possible next steps you can take include adding more routers, chaining <code>verify:types</code> with <code>nest build</code> and <code>next build</code> into a single pre-merge script, or using tRPC subscriptions.</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">Deploying Containerized NestJS Applications on GCP Using Cloud Run</h4></div><div class="col-8"><p class="u-fs16 u-mb0">While there are numerous ways to deploy an app to Cloud Run, see <a target="_blank" href="https://www.telerik.com/blogs/deploying-containerized-nestjs-applications-gcp-using-cloud-run">how to containerize and deploy a NestJS API</a>. We will use a few products on GCP, such as Buildpacks and Artifact Registry, to build and deploy our image, and then finally deploy it to Cloud Run.</p></div></div></aside><img src="https://feeds.telerik.com/link/23056/17368674.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-07-05T18:13:58Z</updated>
    <author>
      <name>Kathryn Grayson Nanz </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17362885/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/23056/17362885.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-07-05T18:13:58Z</updated>
    <author>
      <name>Hassan Djirdeh </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17359602/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/23056/17359602.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:a21016d2-1ad2-4a02-bae7-eef7529852c5</id>
    <title type="text">What’s Next for React in 2026</title>
    <summary type="text">The State of React survey reveals developer insights into the patterns and tools they use and how their opinions about React are shifting.</summary>
    <published>2026-04-20T16:37:09Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>Hassan Djirdeh </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17322393/whats-next-react-2026"/>
    <content type="text"><![CDATA[<p><span class="featured">The State of React survey reveals developer insights into the patterns and tools they use and how their opinions about React are shifting.</span></p><p>The <a target="_blank" href="https://2025.stateofreact.com/">State of React 2025</a> survey results paint an interesting picture of where React stands as it heads into 2026. <a target="_blank" href="https://www.telerik.com/blogs/whats-new-react-19">React 19</a> adoption is well underway, but stability hasn&rsquo;t translated into consensus about what comes next.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-02/whats-next-for-react-state-of-react-25-survey.png?sfvrsn=952363dc_2" alt="State of React 2025" /></p><p>What the survey shows is that some of React&rsquo;s biggest bets are paying off while others are still finding their footing. The patterns we&rsquo;re building with, the tools we&rsquo;re reaching for and the way we think about React architecture are all shifting. In this article, we&rsquo;ll look at what the data actually says and what it means for the year ahead.</p><h2 id="react-server-components">React Server Components</h2><p>One of the most talked about additions to React in the last couple of years has been <a target="_blank" href="https://react.dev/reference/rsc/server-components">React Server Components (RSC)</a>. Server Components are components that run on the server and let us keep server-only logic, data access and sensitive code out of the client bundle. Alongside that, <a target="_blank" href="https://react.dev/reference/rsc/server-functions">Server Functions</a> let client-side code invoke server-side logic through a framework-managed interface, without hand-rolling traditional API endpoints for every interaction.</p><blockquote><p>For a great read on Server Components, check out <a target="_blank" href="https://www.telerik.com/blogs/current-state-react-server-components-guide-perplexed">The Current State of React Server Components: A Guide for the Perplexed</a>.</p></blockquote><p>Some have said that Server Components were to be the foundation of React&rsquo;s next evolution toward a more complete full-stack framework. The <a target="_blank" href="https://2025.stateofreact.com/en-US/features/#all_features">survey data</a>, however, is more nuanced.</p><p>About 45% of respondents have used Server Components, and among those who have, only about a third report a positive experience. Server Functions tell a similar story, with roughly 37% adoption and 33% positive sentiment among users. In both cases, only a small fraction of the overall community has used these features <em>and</em> come away with a positive sentiment.</p><p>Contrast that with <a target="_blank" href="https://react.dev/reference/react/Suspense">Suspense</a>, React&rsquo;s mechanism for declaratively handling loading states while waiting for asynchronous data or code. <a target="_blank" href="https://2025.stateofreact.com/en-US/features/#new_apis_ratios">Suspense has the highest adoption rate among new features and boasts strong satisfaction numbers</a>.</p><p>It&rsquo;s a useful comparison because it shows that the React community isn&rsquo;t resistant to new patterns: when a new API solves a clear problem with a reasonable developer experience, adoption follows. With that said, Suspense is a smaller, more contained feature that&rsquo;s easier to introduce into existing applications, while Server Components require a more fundamental shift in how we think about our application architecture.</p><p>The architecture data reinforces this picture. <a target="_blank" href="https://2025.stateofreact.com/en-US/usage/#js_app_patterns">When asked which rendering patterns they&rsquo;ve used</a>, most teams still rely on the tried-and-true: Single-Page Applications lead the way at 84%, followed by Server-Side Rendering (61%) and Static Site Generation (44%). Newer approaches like partial hydration (25%), streaming SSR (18%) and islands architecture (14%) are gaining traction, but they&rsquo;re far from mainstream.</p><p>None of this means Server Components don&rsquo;t matter. Architecturally, the ability to move rendering logic to the server, reduce client-side JavaScript and simplify data fetching is significant. But the developer experience may just need more time to mature and catch up to the architectural promise. For most teams, the pragmatic move is to adopt RSC incrementally and where it makes sense, rather than treating it as a mandate to rewrite everything.</p><h2 id="what-developers-are-curious-about">What Developers Are Curious About</h2><p>The survey also gives us a sense of where developer curiosity is headed. The <a target="_blank" href="https://2025.stateofreact.com/en-US/features/#reading_list">reading list</a>, the section of the survey that lets respondents flag topics they want to learn more about, is a useful signal here.</p><p><a href="https://react.dev/reference/react/ViewTransition">ViewTransition</a>, a React API for coordinating animated transitions between UI states, ranks near the top. So does <a target="_blank" href="https://react.dev/reference/react/Activity">Activity</a>, which lets us hide and show parts of our UI while preserving their internal state and DOM. Both are currently only available in React&rsquo;s Canary channel, but they point to a future where React handles more of the UX polish that we currently rely on third-party libraries for.</p><p>What&rsquo;s worth noting is the general pattern across the survey data. The features gaining the most positive attention tend to be the ones that solve focused problems without requiring a wholesale rethink of how we build applications. Developers are drawn to APIs that slot into their existing workflows and make specific things easier, whether that&rsquo;s handling loading states with Suspense, coordinating transitions with <code>&lt;ViewTransition&gt;</code> or managing background rendering with <code>&lt;Activity&gt;</code>.</p><h2 id="the-ui-component-library-landscape">The UI Component Library Landscape</h2><p>The <a target="_blank" href="https://2025.stateofreact.com/en-US/libraries/component-libraries/#component_libraries_cardinalities">survey data around UI component libraries</a> also tells an interesting story. The average respondent has used 2.3 UI libraries and a significant proportion don&rsquo;t use any component library at all. As the survey itself notes, this suggests the space isn&rsquo;t quite settled yet, and that there&rsquo;s still room for new entrants to make their mark.</p><p>What this tells us is that developers are still actively evaluating their options. Even the most widely used libraries in the survey sit around <a target="_blank" href="https://2025.stateofreact.com/en-US/libraries/component-libraries/">50-57% usage</a>, and the libraries with the highest satisfaction rates aren&rsquo;t always the ones with the broadest adoption. The needs are clear: production-quality components, built-in accessibility, consistent theming, TypeScript support and, increasingly, integration with AI-powered development workflows.</p><h3 id="kendoreact">KendoReact</h3><p>For teams building enterprise applications, the choice of component library has long-term implications. It affects how quickly we can ship features, how accessible our applications are out of the box and how well our UI scales across complex use cases like data grids, schedulers and form-heavy workflows.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-02/whats-next-for-react-kendoreact-home.png?sfvrsn=9f2d81b7_2" alt="KendoReact website: Master the Art of React UI" /></p><p>Progress <a target="_blank" href="https://www.telerik.com/kendo-react-ui">KendoReact</a> is one library worth looking at in this context. It provides <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components">120+ production-ready components</a> with built-in accessibility, deep theming support through <a target="_blank" href="https://www.telerik.com/themebuilder">ThemeBuilder</a> and recent investments in AI-powered developer tooling including an <a target="_blank" href="https://www.telerik.com/react-mcp-servers">MCP server and AI coding assistant</a>. For teams evaluating their UI toolkit for the year ahead, it&rsquo;s a library that&rsquo;s actively investing in the same directions the ecosystem is moving.</p><h2 id="ai-as-an-accelerator">AI as an Accelerator</h2><p>It would be impossible to write about React development in 2026 without mentioning AI. However, the important thing to keep in mind is that AI is changing <strong>how</strong> we write React code, not <strong>what</strong> we build with it.</p><p>The AI tooling landscape has shifted significantly over the last couple of years. AI-native editors like <a target="_blank" href="https://cursor.com/">Cursor</a> and <a target="_blank" href="https://code.claude.com/docs/en/overview">Claude Code</a> understand our entire codebase and can generate components that match our existing patterns and conventions. <a target="_blank" href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol (MCP)</a> integrations give AI assistants real-time access to component library documentation, so the code they generate actually uses the right props and follows current best practices.</p><p>These capabilities make us faster by reducing the time we spend on boilerplate and letting us iterate more quickly. However, they don&rsquo;t replace the architectural decisions we need to make: when to adopt Server Components, how to structure our state management and which rendering patterns fit our use case. AI accelerates the execution of those decisions, not the decisions themselves.</p><blockquote><p>For a deeper dive into how AI is reshaping day-to-day React workflows, from code generation to theming to agentic development, check out <a target="_blank" href="https://www.telerik.com/blogs/ai-productivity-react-developers-2026">AI Productivity for React Developers in 2026</a>.</p></blockquote><h2 id="what-this-means-for-2026">What This Means for 2026</h2><p>The data we surveyed today gives us a clear picture of where React is today and where it could be heading in 2026. React is stable, widely adopted and evolving, but the community&rsquo;s appetite for significant change is measured.</p><ul><li><strong>Server Components represent React&rsquo;s most ambitious shift</strong>, but mainstream acceptance will take time. For most teams, the winning approach is incremental adoption, where it solves real problems.</li><li><strong>Developer experience still wins.</strong> The features seeing the strongest adoption and interest (Suspense, ViewTransition, Activity) solve focused problems without demanding that we rebuild our mental model of React.</li><li><strong>The component library landscape remains unsettled.</strong> Teams need libraries that invest in accessibility, developer experience and integrate well with modern tooling, including AI assistants.</li><li><strong>AI is making us more productive at the execution layer</strong>, but strategic decisions about architecture and user experience still require human judgment.</li></ul><p>Looking ahead, the most successful React teams in 2026 will stay pragmatic: adopting new patterns when they solve real problems, choosing stable tooling and using AI to accelerate delivery without losing sight of fundamentals. React&rsquo;s ecosystem is mature enough that we can be selective about what we adopt and when.</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">AI Productivity for React Developers in 2026</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Developers are not blindly handing work over to AI, but treating it as a strategic assistant. <a target="_blank" href="https://www.telerik.com/blogs/ai-productivity-react-developers-2026">Here&rsquo;s what this looks like for React devs in 2026.</a></p></div></div></aside><img src="https://feeds.telerik.com/link/23056/17322393.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:156049da-af6d-45bc-9ca5-40a634a963f3</id>
    <title type="text">AI Productivity for React Developers in 2026</title>
    <summary type="text">Developers are not blindly handing work over to AI, but treating it as a strategic assistant. Here’s what this looks like for React devs in 2026.</summary>
    <published>2026-04-13T16:03:30Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>Hassan Djirdeh </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17318469/ai-productivity-react-developers-2026"/>
    <content type="text"><![CDATA[<p><span class="featured">Developers are not blindly handing work over to AI, but treating it as a strategic assistant. Here&rsquo;s what this looks like for React devs in 2026.</span></p><p>The 2025 Stack Overflow Developer Survey confirmed what most of us already know from experience: <a target="_blank" href="https://survey.stackoverflow.co/2025/ai/#1-ai-tools-in-the-development-process">84% of developers are now using or planning to use AI tools in their development process.</a> What&rsquo;s more revealing is how developers are still being cautious in their use of these tools. Only about <a target="_blank" href="https://survey.stackoverflow.co/2025/ai/#2-accuracy-of-ai-tools">one-third say they trust AI outputs completely, while nearly half question their accuracy</a>.</p><p>This cautious adoption tells us something important. We&rsquo;re not blindly handing our work over to AI. We&rsquo;re learning to use these tools strategically, treating them as assistants rather than replacements. For React developers specifically, this means adapting how we approach building interfaces, managing component libraries and iterating on features.</p><p>If you&rsquo;ve been building React applications for a while, you&rsquo;ve probably experienced this shift firsthand. Maybe you&rsquo;re using <a target="_blank" href="https://github.com/features/copilot">GitHub Copilot</a> to autocomplete component logic, or <a target="_blank" href="https://cursor.com/">Cursor</a> to refactor entire feature branches. Perhaps you&rsquo;re generating design themes with <a target="_blank" href="https://www.telerik.com/themebuilder">Progress ThemeBuilder</a>, or using <a target="_blank" href="https://claude.ai/">Claude</a> to debug complex state management issues. These aren&rsquo;t experimental workflows anymore; they&rsquo;re part of how we work.</p><p>In this article, we&rsquo;ll explore what this actually looks like in practice for React developers in 2026.</p><h2 id="coding-itself-has-changed">Coding Itself Has Changed</h2><p>There&rsquo;s a shift in how we write code, and it&rsquo;s not what most people expected (don&rsquo;t worry, we&rsquo;re not being replaced by AI). We&rsquo;re spending less time on the mechanical act of typing and more time on the creative work of guiding and refining.</p><p><a target="_blank" href="https://tinyclouds.org/">Ryan Dahl</a>, the creator of <a target="_blank" href="https://nodejs.org/en">Node.js</a> and <a target="_blank" href="https://deno.com/">Deno</a>, aptly put it in a recent social media post: <em>&ldquo;This has been said a thousand times before, but allow me to add my own voice: the era of humans writing code is over. Disturbing for those of us who identify as SWEs, but no less true. That&rsquo;s not to say SWEs don&rsquo;t have work to do, but writing syntax directly is not it.&rdquo;</em></p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-02/ai-productivity-for-react-ryan-dahl-x-post.png?sfvrsn=6d572b2c_2" alt="" /></p><p>For React developers, this means our day-to-day work looks different. Instead of spending an hour carefully typing out a form component with all its validation logic, we might spend 10 minutes describing what we need and then another 20 minutes reviewing and adjusting the generated code. The result? The same quality component in a fraction of the time.</p><p>The iteration cycles have also become faster. When we can generate and refine code quickly, we&rsquo;re more willing to try multiple approaches. Need to see how our data grid looks with server-side pagination versus client-side? Generate both implementations and compare them. Want to experiment with different state management patterns? We can prototype each one in minutes rather than hours.</p><p>We still need to understand React&rsquo;s rendering model, still need to know when to use <code>useCallback</code> versus <code>useMemo</code>, still need to architect our component hierarchy thoughtfully. However, we don&rsquo;t need to manually type out every property definition or write boilerplate code repeatedly.</p><h2 id="continuous-context-and-ai-native-editors">Continuous Context and AI-Native Editors</h2><p>One of the significant improvements in AI-assisted development is <strong>persistent context</strong>. Modern AI coding assistants understand our entire codebase, not just the file we&rsquo;re currently editing.</p><p>Traditional autocomplete tools could only see a few lines around our cursor. They might suggest completing a function name, but they had no idea what that function should actually do or how it fit into our broader application architecture. AI-native editors and tools like <a target="_blank" href="https://cursor.com/">Cursor</a>, <a target="_blank" href="https://code.claude.com/docs/en/overview">Claude Code</a> and others have changed this completely.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-02/ai-productivity-for-react-cursor.jpg?sfvrsn=e557aff2_2" alt="" /></p><p>These tools help maintain an understanding of our project structure, our component patterns, our naming conventions and even our coding style. When we ask them to create a new feature, they don&rsquo;t just generate generic React code; they generate code that looks like it belongs in our codebase because they&rsquo;ve learned from the rest of our application.</p><p>Let&rsquo;s say we&rsquo;re building a <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/sample-applications/admin-dashboard">React dashboard</a> UI that needs to fetch user data and display it in a <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid">table</a>. With traditional tools, we&rsquo;d manually wire up the API call, handle loading states, manage errors and set up the table structure.</p><p>With an AI assistant that understands our codebase, we can describe what we need, and it&rsquo;ll generate code that uses our existing API client, follows our established error handling patterns and implements the table using whatever libraries we&rsquo;re already using in our app.</p><p>Working with these AI assistants feels less like using a tool and more like pair programming with someone who already knows our codebase inside and out.</p><h2 id="planning-and-agentic-workflows">Planning and Agentic Workflows</h2><p>Writing individual functions or components is one thing, but modern AI tools are increasingly capable of handling multi-step workflows that require planning and coordination. This is where agentic AI comes in: systems that can break down complex tasks into smaller steps and execute them sequentially or in parallel.</p><p>For React developers, this means we can hand off entire features to an AI agent rather than just asking for help with specific files. Want to migrate from <a target="_blank" href="https://redux.js.org/">Redux</a> to <a target="_blank" href="https://github.com/pmndrs/zustand">Zustand</a>? An AI agent can analyze our current state management setup, create a migration plan, and then systematically work through each component to update the implementation.</p><p>For example, we might tell an agent: <em>&ldquo;Build a column chart that shows quarterly sales by region, with drill-down functionality to reveal monthly data when a quarter is clicked.&rdquo;</em></p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-02/kendoreact-chart-mcp.gif?sfvrsn=86c7ffc8_1" alt="" /></p><p>The agent can generate the chart component, configure the data bindings, create any necessary helper functions for aggregation and even add default styling or tooltips.</p><p>What&rsquo;s powerful is that these agents can handle refactors and migrations that would be tedious to do manually. Need to rename a prop across 50 components? Want to update all your class components to functional components with hooks? Planning to migrate from JavaScript to TypeScript? These are exactly the kinds of repetitive-but-important tasks that agents excel at.</p><h2 id="ui-generation-design-and-theming-with-ai">UI Generation, Design and Theming with AI</h2><p>Building user interfaces has always involved a fair amount of repetitive work. Creating layouts, styling components and maintaining design consistency across an application. These are all necessary tasks, but they&rsquo;re not where some developers want to spend their creative energy.</p><p>With AI, layout generation has become surprisingly sophisticated. Tools like <a target="_blank" href="https://v0.dev/">v0</a> can take a description or even a screenshot of a design and generate React components that implement that layout. The code they produce isn&rsquo;t just a rough approximation either, it&rsquo;s good-quality JSX with proper semantic HTML, accessibility attributes and responsive styling.</p><p>For React developers working with component libraries like <a target="_blank" href="https://www.telerik.com/kendo-react-ui">Progress KendoReact</a>, this gets even more powerful as long as the agent has the appropriate context about the library and components. Instead of generating generic HTML and CSS, these tools can generate code using the actual components from our library. Need a data grid with sorting, filtering and pagination? Describe it, and the AI will scaffold it using the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid">React Grid</a> component we&rsquo;re already using elsewhere in our app: with all the right props, event handlers and styling.</p><p><a target="_blank" href="https://www.telerik.com/themebuilder">Progress ThemeBuilder</a> has taken this a step further with AI-powered theme generation. Instead of manually configuring dozens of color variables and spacing values, we can describe the aesthetic we&rsquo;re going for in plain English: <em>&ldquo;Create a clean, modern analytics theme with a cool blue-gray palette that feels data-driven and professional, suitable for a B2B software dashboard.&rdquo;</em> The AI generates a complete design system that applies consistently across all our components.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-02/theme-builder-modern-analytics-theme.png?sfvrsn=8a4ac335_2" alt="" /></p><p>What makes this valuable is that the generated themes aren&rsquo;t just random color combinations. The AI understands design principles like color theory, contrast ratios for accessibility and how different UI states should relate visually. When it generates a theme, it&rsquo;s creating a system where primary buttons, hover states, focus indicators and disabled states all work together. ThemeBuilder also provides component-level AI theming, which means we can refine specific components without having to regenerate our entire theme.</p><p>For developers who aren&rsquo;t designers (which is most of us), this removes a huge source of friction. We can focus on building functionality while still delivering applications that look professional and cohesive.</p><h2 id="model-context-protocol-and-integration">Model Context Protocol and Integration</h2><p>The tools we&rsquo;ve talked about so far are impressive, but they have a limitation: they only know what&rsquo;s in our codebase and what they learned during training. They don&rsquo;t know about our specific component library&rsquo;s latest features, our company&rsquo;s internal design system, or the proprietary APIs we&rsquo;re building against.</p><p>This is where <a target="_blank" href="https://modelcontextprotocol.io/docs/getting-started/intro">Model Context Protocol (MCP)</a> becomes important. Think of MCP as a standardized way for AI models to connect to external knowledge sources and tools. Instead of the AI being limited to its training data, it can reach out to documentation, component libraries, databases and other resources to get current, specific information about the tools we&rsquo;re using.</p><p>For React developers, this means our AI assistant can understand the actual API of the components we&rsquo;re working with, not just generic React patterns. When we&rsquo;re using the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid">KendoReact Grid</a> component, an MCP-enabled assistant can query the real documentation to understand what props are available, what patterns are recommended and what changed in the latest version.</p><p>The <a target="_blank" href="https://www.npmjs.com/package/@progress/kendo-react-mcp">@progress/kendo-react-mcp</a> server implements this for KendoReact. When we install it and configure it with our editor, the AI assistant gains access to comprehensive knowledge about KendoReact components. It&rsquo;s not guessing at how to use the Grid, or the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/scheduler">Scheduler</a>, or the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/dateinputs/datepicker">DatePicker</a>. It&rsquo;s referencing the actual documentation and best practices.</p><p>Here&rsquo;s an example of generating a KendoReact Grid from scratch with the help of the <code>@progress/kendo-react-mcp</code> tool.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-02/kendoreact-grid-mcp.gif?sfvrsn=98fc03b_2" alt="" /></p><p>This can make a large difference in practice. Without MCP, we might ask an AI to create a KendoReact Grid component and get something that looks right but uses outdated props or misses important configuration options. With MCP, the AI generates code that follows current best practices and uses the component exactly as it was designed to be used.</p><h2 id="quality-review-and-governance">Quality, Review and Governance</h2><p>As AI-generated code becomes more prevalent in React applications, questions about quality and review processes naturally arise. The good news is that AI isn&rsquo;t just generating code; it&rsquo;s starting to get better at reviewing and improving it.</p><p>Modern AI tools can perform code reviews that go beyond syntax checking. They understand architectural patterns, can spot potential performance issues and flag accessibility concerns. When we commit changes, these tools can analyze the diff and provide feedback similar to what a developer might offer in a pull request review.</p><p>For React applications specifically, this means AI can catch issues like unnecessary re-renders, missing dependency arrays in hooks, improper state management patterns or components that should be memoized but aren&rsquo;t. It can also verify that our components follow established patterns in our codebase and flag when new code deviates from those conventions.</p><p>AI-generated code can greatly accelerate development and improve consistency, but <strong>it&rsquo;s not a replacement for human judgment</strong>. Even the more sophisticated tools benefit from a steward: a developer who reviews, guides and validates the AI&rsquo;s output. Humans remain essential for making nuanced decisions, verifying alignment with project goals and upholding quality standards that AI alone can&rsquo;t fully guarantee. In other words, AI can handle the heavy (repetitive) lifting, but the final responsibility still rests with us.</p><h2 id="what-this-means-for-day-to-day-react-work">What This Means for Day-to-Day React Work</h2><p>What does all of this actually look like in practice? How has the daily experience of building React applications changed? Here are some takeaways:</p><ul><li><strong>Faster iteration</strong>: Features that used to take days can now be scaffolded in hours. AI handles repetitive boilerplate, letting us focus on logic, architecture and user experience.</li><li><strong>Less boilerplate, more creativity</strong>: Forms, API integrations, responsive layouts and other common patterns can be generated automatically, freeing us to solve higher-level problems.</li><li><strong>Higher-level decisions matter more</strong>: Understanding React fundamentals like component composition, state management, performance optimization and accessibility is more important than ever because we&rsquo;re guiding and reviewing AI outputs rather than typing everything ourselves.</li></ul><p>In short, React development in 2026 is faster, more iterative and more creative. The mechanical work of writing boilerplate and translating designs into components is largely automated, but the critical thinking and stewardship still require skilled developers.</p><h2 id="ready-to-explore-how-ai-can-enhance-your-react-development">Ready to Explore How AI Can Enhance Your React Development?</h2><ul><li>Give the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/ai-components#kendoreact-ai-components-and-features">KendoReact AI Components and Features</a> a try.</li><li>Watch demos of <a target="_blank" href="https://www.telerik.com/themebuilder">AI-powered theme generation in ThemeBuilder</a> to see how design ideation can be automated.</li><li>Try the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/ai-tools/ai-assistant/getting-started">KendoReact MCP Server</a> to give your AI assistant deep knowledge of KendoReact components.</li><li>Check out the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/ai-tools/ai-assistant/prompt-library">KendoReact Prompt Library</a> for ready-made prompts that target common development scenarios.</li></ul><img src="https://feeds.telerik.com/link/23056/17318469.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:bd4d6447-7473-40eb-b916-7931c305c4b7</id>
    <title type="text">Simplified Authentication with Better Auth</title>
    <summary type="text">We’ll build a complete email and password authentication system with session management to see how Better Auth works. Users will be able to sign up, log in and stay authenticated across page refreshes. We’ll use Next.js, Drizzle ORM and SQLite.</summary>
    <published>2026-04-09T17:26:49Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>Christian Nwamba </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17316561/simplified-authentication-better-auth"/>
    <content type="text"><![CDATA[<p><span class="featured">We&rsquo;ll build a complete email and password authentication system with session management to see how Better Auth works. Users will be able to sign up, log in and stay authenticated across page refreshes. We&rsquo;ll use Next.js, Drizzle ORM and SQLite.</span></p><p>Authentication should be straightforward, but in practice, it takes time. You&rsquo;re dealing with sessions, cookies, password hashing, OAuth redirects and email verification, and each piece has its own quirks and scattered documentation. The whole time, you may also be asking yourself if you&rsquo;re doing this securely. One mistake exposes your users.</p><p>Most existing tools don&rsquo;t help much. <a target="_blank" href="https://next-auth.js.org/">NextAuth</a> is solid if you&rsquo;re using Next.js, but switching frameworks means starting over. Building from scratch gives you control at the cost of maintaining every security detail yourself. <a target="_blank" href="https://clerk.com/">Clerk</a> and <a target="_blank" href="https://auth0.com/">Auth0</a> work well until you reach their pricing tiers or require a feature they don&rsquo;t support. <a target="_blank" href="https://www.passportjs.org/">Passport.js</a> works, but it hasn&rsquo;t aged well&mdash;there&rsquo;s callback hell and endless boilerplate.</p><p><a target="_blank" href="https://www.better-auth.com/">Better Auth</a> takes a different approach. It&rsquo;s lightweight, works with any framework (<a target="_blank" href="https://nextjs.org/">Next.js</a>, <a target="_blank" href="https://remix.run/">Remix</a>, <a target="_blank" href="https://svelte.dev/">Svelte</a>, etc.), and comes with <a target="_blank" href="https://www.typescriptlang.org/">TypeScript</a> baked in. You get OAuth, 2FA and password resets out of the box. You can also customize when you need to, or just use the defaults.</p><p>In this guide, we&rsquo;ll build a complete email and password authentication system with session management to see how Better Auth works. Users will be able to sign up, log in and stay authenticated across page refreshes. We&rsquo;ll use Next.js, Drizzle ORM and SQLite.</p><h2 id="prerequisites">Prerequisites</h2><p>To follow along with this guide, you&rsquo;ll need:</p><ul><li>A decent understanding of JavaScript/TypeScript</li><li>Basic familiarity with React and Next.js</li><li>A database ready (<a target="_blank" href="https://www.postgresql.org/">PostgreSQL</a>, <a target="_blank" href="https://www.mysql.com/">MySQL</a> or <a target="_blank" href="https://sqlite.org/">SQLite</a>)</li></ul><h2 id="what-is-better-auth">What Is Better Auth?</h2><p>Better Auth is a TypeScript-first authentication library that avoids the usual trade-offs like vendor lock-in, expensive monthly fees or complex configurations. It is framework-agnostic, so you can use it with Next.js today and switch to Remix or SvelteKit tomorrow without rewriting your auth logic. It&rsquo;s not a managed service, so there&rsquo;s no per-user pricing or vendor lock-in.</p><p>What makes it different? It&rsquo;s database-agnostic, meaning you can use PostgreSQL, MySQL, SQLite or MongoDB. Better Auth adapts through adapters like Prisma, Drizzle and Mongoose. It&rsquo;s type-safe by default, built in TypeScript from the ground up, so your IDE knows what methods exist, what data comes back, and catches errors before you run your code. It also has security measures built in by default, with proper password hashing, HttpOnly cookies, CSRF protection and secure session management. The defaults follow best practices.</p><p>Better Auth supports OAuth providers (Google, GitHub, etc.), magic links, two-factor authentication, passkeys, even enterprise SSO, and more than what we&rsquo;ll cover in this article. We&rsquo;re focusing on the fundamentals so you understand how the system works. Once you grasp the basics, adding these features is straightforward through Better Auth&rsquo;s plugin system.</p><h2 id="project-setup">Project Setup</h2><p>Let&rsquo;s start by creating a new Next.js project with TypeScript and <a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a>. Run the following command in your terminal:</p><pre class=" language-shell"><code class="prism  language-shell">npx create-next-app@latest better-auth --ts --tailwind --eslint --app
</code></pre><p>This creates a Next.js project with TypeScript, Tailwind CSS, ESLint and App Router (the modern Next.js routing system) configured.</p><p>Run this command to navigate into the project:</p><pre class=" language-shell"><code class="prism  language-shell">cd better-auth
</code></pre><p>Run the following command to install Better Auth and its dependencies:</p><pre class=" language-shell"><code class="prism  language-shell">npm install better-auth drizzle-orm better-sqlite3 
npm install -D drizzle-kit @types/better-sqlite3
</code></pre><h2 id="database-setup">Database Setup</h2><p>Before configuring Better Auth, we need to prepare our database. We&rsquo;ll break this down into three distinct files to keep things organized:</p><ul><li><strong>Define the tables</strong>: Create the tables Better Auth needs (users, sessions, accounts, verification)</li><li><strong>Database initialization</strong>: Set up the SQLite connection and wrap it with Drizzle for type-safe queries</li><li><strong>Configure auth</strong>: Finally, we connect our database to Better Auth using the Drizzle adapter</li></ul><h3 id="define-the-tables">Define the Tables</h3><p>We need to explicitly define the tables Better Auth expects. We will add all of these to a single file. Open your <code>lib/auth-schema.ts</code> file and add the following to it:</p><p><strong>The User Table</strong><br />This stores basic user information.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token comment">//lib/auth-schema.ts</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> sqliteTable<span class="token punctuation">,</span> text<span class="token punctuation">,</span> integer <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"drizzle-orm/sqlite-core"</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> user <span class="token operator">=</span> <span class="token function">sqliteTable</span><span class="token punctuation">(</span><span class="token string">"user"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  id<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"id"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">primaryKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  name<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"name"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  email<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"email"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unique</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  emailVerified<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"emailVerified"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"boolean"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  image<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"image"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  createdAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"createdAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  updatedAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"updatedAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</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 <code>email</code> field is unique, meaning no two users can share the same email. Better Auth requires these specific fields for its internal functionality.</p><p>Notice we mark <code>name</code> and <code>email</code> as <code>notNull()</code> here. This is important because Better Auth infers its types from this schema. By making them required in the database, TypeScript will automatically force us to provide them in the sign-up form later.</p><p><strong>The Session Table</strong><br />This manages active login sessions.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token comment">//lib/auth-schema.ts</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> session <span class="token operator">=</span> <span class="token function">sqliteTable</span><span class="token punctuation">(</span><span class="token string">"session"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  id<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"id"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">primaryKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  expiresAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"expiresAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  token<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"token"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unique</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  createdAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"createdAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  updatedAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"updatedAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  ipAddress<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"ipAddress"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  userAgent<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"userAgent"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  userId<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"userId"</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">references</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> user<span class="token punctuation">.</span>id<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>Each session has a unique token and links to a user via <code>userId</code>. The <code>expiresAt</code> timestamp determines when the session becomes invalid.</p><p><strong>The Account Table</strong><br />This handles OAuth providers and stores authentication credentials.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token comment">//lib/auth-schema.ts</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> account <span class="token operator">=</span> <span class="token function">sqliteTable</span><span class="token punctuation">(</span><span class="token string">"account"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  id<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"id"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">primaryKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  accountId<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"accountId"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  providerId<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"providerId"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  userId<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"userId"</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
    <span class="token punctuation">.</span><span class="token function">references</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> user<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">,</span>
  accessToken<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"accessToken"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  refreshToken<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"refreshToken"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  idToken<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"idToken"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  accessTokenExpiresAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"accessTokenExpiresAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  refreshTokenExpiresAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"refreshTokenExpiresAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
    mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  scope<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"scope"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  password<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"password"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  createdAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"createdAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  updatedAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"updatedAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</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 account table stores the user&rsquo;s credentials, including the password. This separation keeps the architecture flexible, allowing you to link other providers like GitHub or Google to the same user identity.</p><p><strong>Verification Table</strong><br />This stores temporary code for email verification and password resets.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">export</span> <span class="token keyword">const</span> verification <span class="token operator">=</span> <span class="token function">sqliteTable</span><span class="token punctuation">(</span><span class="token string">"verification"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span>
  id<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"id"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">primaryKey</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  identifier<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"identifier"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  value<span class="token punctuation">:</span> <span class="token function">text</span><span class="token punctuation">(</span><span class="token string">"value"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  expiresAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"expiresAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  createdAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"createdAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  updatedAt<span class="token punctuation">:</span> <span class="token function">integer</span><span class="token punctuation">(</span><span class="token string">"updatedAt"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> mode<span class="token punctuation">:</span> <span class="token string">"timestamp"</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>This table acts as a temporary vault for security tokens. When the system sends an email to verify a user, the unique code is stored here to ensure the link is valid and hasn&rsquo;t expired when clicked.</p><h3 id="database-initialization">Database Initialization</h3><p>We need a running database connection. Let&rsquo;s create a file called <code>lib/db.ts</code>. This is where we initialize SQLite and wrap it in Drizzle so we can use it everywhere else. We wrap it because we want to write TypeScript, not SQL strings. By passing the connection to Drizzle, you get to query your database using typed methods.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token comment">// lib/db.ts </span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> drizzle <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"drizzle-orm/better-sqlite3"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> Database <span class="token keyword">from</span> <span class="token string">"better-sqlite3"</span><span class="token punctuation">;</span>

<span class="token comment">// This creates a local 'sqlite.db' file if it doesn't exist</span>
<span class="token keyword">const</span> sqlite <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Database</span><span class="token punctuation">(</span><span class="token string">"sqlite.db"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 

<span class="token keyword">export</span> <span class="token keyword">const</span> db <span class="token operator">=</span> <span class="token function">drizzle</span><span class="token punctuation">(</span>sqlite<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>We need to do this first so we can import <code>db</code> into our auth configuration without TypeScript yelling at us.</p><h3 id="configure-auth">Configure Auth</h3><p>Now we can write the auth config. This file is where we tell Better Auth about our database and configure how users will authenticate.</p><p>Create a <code>lib/auth.ts</code> file and add the following to it:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token comment">//lib/auth.ts</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> betterAuth <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"better-auth"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> drizzleAdapter <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"better-auth/adapters/drizzle"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> db <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./db"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> schema <span class="token keyword">from</span> <span class="token string">"./auth-schema"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> auth <span class="token operator">=</span> <span class="token function">betterAuth</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  database<span class="token punctuation">:</span> <span class="token function">drizzleAdapter</span><span class="token punctuation">(</span>db<span class="token punctuation">,</span> <span class="token punctuation">{</span>
    provider<span class="token punctuation">:</span> <span class="token string">"sqlite"</span><span class="token punctuation">,</span>
    schema<span class="token punctuation">:</span> schema<span class="token punctuation">,</span> <span class="token comment">// passing the schema here</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
  emailAndPassword<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    enabled<span class="token punctuation">:</span> <span class="token keyword">true</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>In the code above, we import the database connection <code>db</code> and <code>schema</code> we created, then pass them to Better Auth through the drizzleAdapter. This adapter translates Better Auth operations into Drizzle queries so it can read and write user data.</p><p>The <code>provider: "sqlite"</code> tells Better Auth we&rsquo;re using SQLite. If we were using something else like PostgreSQL, we would change it to whatever we&rsquo;re using.</p><p>It is important to note that the <code>emailAndPassword: {enabled: true}</code> option activates email and password authentication. Better Auth will generate the signup and signin endpoints we need.</p><h2 id="syncing-database">Syncing Database</h2><p>Now that we have three core files, we need to create the database file. Currently, <code>sqlite.db</code> doesn&rsquo;t exist.</p><h3 id="drizzle-configuration">Drizzle Configuration</h3><p>Create a file named <code>drizzle.config.ts</code> at the your root of your project and add the following to it:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token comment">//drizzle.config.ts</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> defineConfig <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"drizzle-kit"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token function">defineConfig</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  schema<span class="token punctuation">:</span> <span class="token string">"./lib/auth-schema.ts"</span><span class="token punctuation">,</span> <span class="token comment">// where our tables will be defined</span>
  dialect<span class="token punctuation">:</span> <span class="token string">"sqlite"</span><span class="token punctuation">,</span>
  dbCredentials<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    url<span class="token punctuation">:</span> <span class="token string">"sqlite.db"</span><span class="token punctuation">,</span> <span class="token comment">// This is the name of the file it will create</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 tell Drizzle where to find our schema and what database to use. The config points to our schema file <code>./lib/auth-schema.ts</code> and specifies SQLite as the database type. The url is the filename Drizzle will create and in this case, it&rsquo;s <code>sqlite.db</code>.</p><p>Now, run this command in your terminal to sync your code with the database:</p><pre class=" language-shell"><code class="prism  language-shell">npx drizzle-kit push
</code></pre><p>If everything is set up correctly, you should see a success message. Drizzle just created a local <code>sqlite.db</code> file in your project root with all your user, session and account tables.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/drizzle-configuration.png?sfvrsn=8da70ecc_2" title="Drizzle configurations" alt="Drizzle configuration" /></p><h2 id="api-route">API Route</h2><p>Now we need to set up an API route so our frontend can communicate with Better Auth. Create a file named <code>app/api/auth/[...all]/route.ts</code>. This will be the API route that handles all auth requests:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token comment">// app/api/auth/[...all]/route.ts</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> auth <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@/lib/auth"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> toNextJsHandler <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"better-auth/next-js"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token punctuation">{</span> GET<span class="token punctuation">,</span> POST <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">toNextJsHandler</span><span class="token punctuation">(</span>auth<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><blockquote><p>If your project uses <code>src/</code>, this goes in <code>src/api/auth/[...all]/route.ts</code>.</p></blockquote><h2 id="auth-client">Auth Client</h2><p>Finally, for our setup, we need a way for our frontend to talk to the backend without writing messy fetch calls.</p><p>Create <code>lib/auth-client.ts</code> file and add the following to it:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> createAuthClient <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"better-auth/react"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> authClient <span class="token operator">=</span> <span class="token function">createAuthClient</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  baseURL<span class="token punctuation">:</span> <span class="token string">"http://localhost:3000"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>This small utility will give us type-safe methods for signing in, signing up and checking sessions.</p><h2 id="email-and-password-authentication">Email and Password Authentication</h2><p>Now let&rsquo;s build the authentication forms. We&rsquo;ll create two components: one for sign-up and one for sign-in.</p><h3 id="sign-up-form">Sign-up Form</h3><p>Create a file named <code>components/sign-up.tsx</code> and add the following to it:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token comment">// components/sign-up.tsx</span>
<span class="token string">"use client"</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> useState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"react"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> authClient <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@/lib/auth-client"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> useRouter <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"next/navigation"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">SignUp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>email<span class="token punctuation">,</span> setEmail<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>password<span class="token punctuation">,</span> setPassword<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>name<span class="token punctuation">,</span> setName<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>isLoading<span class="token punctuation">,</span> setIsLoading<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">useRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> signUp <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">await</span> authClient<span class="token punctuation">.</span>signUp<span class="token punctuation">.</span><span class="token function">email</span><span class="token punctuation">(</span>
      <span class="token punctuation">{</span>
        email<span class="token punctuation">,</span>
        password<span class="token punctuation">,</span>
        name<span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span>
        onRequest<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
          <span class="token function">setIsLoading</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        onSuccess<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>
          router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token string">"/dashboard"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        onError<span class="token punctuation">:</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
          <span class="token function">alert</span><span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>error<span class="token punctuation">.</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token function">setIsLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">return</span> <span class="token punctuation">(</span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col gap-4 w-full max-w-md mx-auto mt-10 border border-gray-200 p-6 rounded-lg shadow-sm bg-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>h1</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-xl font-bold text-gray-900<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Create Account<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col gap-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-sm font-medium text-gray-700<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Name<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>input</span>
        <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span>
        <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>name<span class="token punctuation">}</span></span>
        <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setName</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span></span>
        <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>John Doe<span class="token punctuation">"</span></span>
        <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>border border-gray-300 p-2 rounded focus:outline-none focus:ring-2 focus:ring-black text-black bg-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>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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col gap-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-sm font-medium text-gray-700<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Email<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>input</span>
        <span class="token attr-name">type</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 attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>email<span class="token punctuation">}</span></span>
        <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setEmail</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span></span>
        <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>user@example.com<span class="token punctuation">"</span></span>
        <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>border border-gray-300 p-2 rounded focus:outline-none focus:ring-2 focus:ring-black text-black bg-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>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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col gap-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-sm font-medium text-gray-700<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Password<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>input</span>
        <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>password<span class="token punctuation">"</span></span>
        <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>password<span class="token punctuation">}</span></span>
        <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setPassword</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span></span>
        <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;<span class="token punctuation">"</span></span>
        <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>border border-gray-300 p-2 rounded focus:outline-none focus:ring-2 focus:ring-black text-black bg-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>div</span><span class="token punctuation">&gt;</span></span>

    <span class="token operator">&lt;</span>button
      onClick<span class="token operator">=</span><span class="token punctuation">{</span>signUp<span class="token punctuation">}</span>
      disabled<span class="token operator">=</span><span class="token punctuation">{</span>isLoading<span class="token punctuation">}</span>
      className<span class="token operator">=</span><span class="token punctuation">{</span><span class="token template-string"><span class="token string">`mt-2 p-2 rounded text-white font-medium transition-colors </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>
        isLoading
          <span class="token operator">?</span> <span class="token string">"bg-gray-400 cursor-not-allowed"</span>
          <span class="token punctuation">:</span> <span class="token string">"bg-black hover:bg-gray-800"</span>
      <span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">}</span>
    <span class="token operator">&gt;</span>
      <span class="token punctuation">{</span>isLoading <span class="token operator">?</span> <span class="token string">"Creating an account..."</span> <span class="token punctuation">:</span> <span class="token string">"Sign Up"</span><span class="token punctuation">}</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
<span class="token punctuation">)</span><span class="token punctuation">;</span> 
</code></pre><p>This component handles the entire sign-up flow in a single file. If you look closer, you&rsquo;ll see that the heavy lifting is done by <code>authClient.signUp.email()</code>. This function is type-safe and accepts two distinct arguments (the payload and the event handlers):</p><ul><li><strong>The payload</strong>: This is the actual data we&rsquo;re sending. In this case, email, password and name. Since our database schema requires these fields, the client verifies we actually provide them. If we miss a required field, TypeScript will flag it instantly.</li><li><strong>Event handlers</strong>: The second argument is an object that controls the request lifecycle, from the moment we click Sign Up to when we get a result. This is one of the perks of Better Auth because it replaces try/catch blocks with clean event hooks. All we have to do is define what happens at each stage: <code>onRequest</code>, <code>onSuccess</code> and <code>onError</code>, which catches errors and alerts the user in this case.</li></ul><h2 id="sign-in-form">Sign-in Form</h2><p>With our current setup, users can only register. We need to log them in if they&rsquo;re existing users.</p><p>Create a file named <code>components/sign-in.tsx</code> and add the following to it:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token comment">//components/sign-in.tsx</span>
<span class="token string">"use client"</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> useState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"react"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> authClient <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@/lib/auth-client"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> useRouter <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"next/navigation"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">SignIn</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>email<span class="token punctuation">,</span> setEmail<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>password<span class="token punctuation">,</span> setPassword<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>isLoading<span class="token punctuation">,</span> setIsLoading<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">useRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> signIn <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">await</span> authClient<span class="token punctuation">.</span>signIn<span class="token punctuation">.</span><span class="token function">email</span><span class="token punctuation">(</span>
      <span class="token punctuation">{</span>
        email<span class="token punctuation">,</span>
        password<span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">{</span>
        onRequest<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
          <span class="token function">setIsLoading</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        onSuccess<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
          <span class="token function">setIsLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token string">"/dashboard"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
        onError<span class="token punctuation">:</span> <span class="token punctuation">(</span>ctx<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
          <span class="token function">setIsLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
          <span class="token function">alert</span><span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>error<span class="token punctuation">.</span>message<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>

  <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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col gap-4 w-full max-w-md mx-auto mt-10 border border-gray-200 p-6 rounded-lg shadow-sm bg-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>h1</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-xl font-bold text-gray-900<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Sign In<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>

      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col gap-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-sm font-medium text-gray-700<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Email<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>input</span>
          <span class="token attr-name">type</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 attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>email<span class="token punctuation">}</span></span>
          <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setEmail</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span></span>
          <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>user@example.com<span class="token punctuation">"</span></span>
          <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>border border-gray-300 p-2 rounded focus:outline-none focus:ring-2 focus:ring-black text-black bg-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>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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col gap-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-sm font-medium text-gray-700<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Password<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>input</span>
          <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>password<span class="token punctuation">"</span></span>
          <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>password<span class="token punctuation">}</span></span>
          <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setPassword</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span></span>
          <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>&bull;&bull;&bull;&bull;&bull;&bull;&bull;&bull;<span class="token punctuation">"</span></span>
          <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>border border-gray-300 p-2 rounded focus:outline-none focus:ring-2 focus:ring-black text-black bg-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>div</span><span class="token punctuation">&gt;</span></span>

      <span class="token operator">&lt;</span>button
        onClick<span class="token operator">=</span><span class="token punctuation">{</span>signIn<span class="token punctuation">}</span>
        disabled<span class="token operator">=</span><span class="token punctuation">{</span>isLoading<span class="token punctuation">}</span>
        className<span class="token operator">=</span><span class="token punctuation">{</span><span class="token template-string"><span class="token string">`mt-2 p-2 rounded text-white font-medium transition-colors </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>
          isLoading
            <span class="token operator">?</span> <span class="token string">"bg-gray-400 cursor-not-allowed"</span>
            <span class="token punctuation">:</span> <span class="token string">"bg-black hover:bg-gray-800"</span>
        <span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">}</span>
      <span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>isLoading <span class="token operator">?</span> <span class="token string">"Loading..."</span> <span class="token punctuation">:</span> <span class="token string">"Sign In"</span><span class="token punctuation">}</span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>This component is nearly identical to the sign-up form with one key difference: we only need email and password since we&rsquo;re verifying an existing user, not creating a new one.</p><p>When a user submits the form, Better Auth verifies the credentials against our database. If they match, it automatically creates a session and sets a secure HttpOnly cookie. This cookie persists the login state, so users stay logged in even after refreshing the page.</p><p>Remember when we enabled <code>emailAndPassword: { enabled: true }</code> in the <code>lib/auth.ts</code> file? Better Auth read that configuration and automatically generated this method for us.</p><p>Now let&rsquo;s create dedicated pages for these forms.</p><h2 id="creating-authentication-pages">Creating Authentication Pages</h2><p>Instead of embedding forms on the homepage, we&rsquo;ll create dedicated routes for sign-in, sign-up and a dashboard to verify successful login.</p><h3 id="sign-up-page">Sign-up Page</h3><p>Create a component called <code>app/signup/page.tsx</code> and add the following to it:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token comment">//app/signup/page.tsx</span>
<span class="token keyword">import</span> SignUp <span class="token keyword">from</span> <span class="token string">"@/components/sign-up"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> Link <span class="token keyword">from</span> <span class="token string">"next/link"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">SignUpPage</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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col items-center justify-center min-h-screen bg-gray-50<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>SignUp</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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mt-6 text-center text-sm text-gray-600<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        Already have an account<span class="token operator">?</span><span class="token punctuation">{</span><span class="token string">" "</span><span class="token punctuation">}</span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Link</span>
          <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/signin<span class="token punctuation">"</span></span>
          <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-blue-600 font-medium hover:underline<span class="token punctuation">"</span></span>
        <span class="token punctuation">&gt;</span></span>
          Sign In
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Link</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 punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>This page wraps the <code>&lt;SignUp /&gt;</code> component and adds a <code>link</code> to sign in.</p><h3 id="sign-in-page">Sign-in Page</h3><p>Create a component called <code>app/signin/page.tsx</code> and add the following to it:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token comment">//app/signin/page.tsx</span>
<span class="token keyword">import</span> SignIn <span class="token keyword">from</span> <span class="token string">"@/components/sign-in"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> Link <span class="token keyword">from</span> <span class="token string">"next/link"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">SignInPage</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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col items-center justify-center min-h-screen bg-gray-50<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>SignIn</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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mt-6 text-center text-sm text-gray-600<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        New <span class="token class-name">here</span><span class="token operator">?</span><span class="token punctuation">{</span><span class="token string">" "</span><span class="token punctuation">}</span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Link</span>
          <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>/signup<span class="token punctuation">"</span></span>
          <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-blue-600 font-medium hover:underline<span class="token punctuation">"</span></span>
        <span class="token punctuation">&gt;</span></span>
          Create an account
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>Link</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 punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>This page wraps the <code>&lt;SignIn /&gt;</code> component and adds a link to Sign Up.</p><h3 id="dashboard-page">Dashboard Page</h3><p>We need a destination for users after they log in. For now, let&rsquo;s create a simple static page named <code>app/dashboard/page.tsx</code> and add the following to it:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token comment">//app/dashboard/page.tsx</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Dashboard</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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col items-center justify-center min-h-screen bg-white text-black<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-3xl font-bold<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Dashboard<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>mt-4 text-gray-600<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>You are successfully logged <span class="token keyword">in</span><span class="token operator">!</span><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>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Now you can start your server by running <code>npm run dev</code> and then open <a target="_blank" href="http://localhost:3000">http://localhost:3000</a>.</p><p>You should see a landing page with options to Sign In or Create Account. Follow these steps to test the authentication:</p><ul><li>Click &ldquo;Create Account&rdquo; and create a new user. If it works, you should be redirected to the dashboard.</li><li>To test the login form, you don&rsquo;t need to open another window. Just hit the back button in your browser to return to the landing page. Click &ldquo;Sign In&rdquo; this time, enter the email and password you just used to sign up, and watch it redirect you back to the dashboard.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/sign-up-and-redirect.gif?sfvrsn=eb3db7f9_2" title="Testing the Sign Up and Redirect flow" alt="Testing the Sign Up and Redirect flow" /></p><h2 id="accessing-user-sessions">Accessing User Sessions</h2><p>Right now, our dashboard just shows static text. We need to make it smart so it displays the actual user&rsquo;s name and email.</p><p>To do this, we use the <code>useSession</code> hook from the <code>auth-client</code>. This hook gives us real-time access to the user&rsquo;s data.</p><p>Update your <code>app/dashboard/page.tsx</code> file with the following:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token comment">//app/dashboard/page.tsx</span>
<span class="token string">"use client"</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> authClient <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@/lib/auth-client"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> useRouter <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"next/navigation"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Dashboard</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">useRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> data<span class="token punctuation">:</span> session<span class="token punctuation">,</span> isPending <span class="token punctuation">}</span> <span class="token operator">=</span> authClient<span class="token punctuation">.</span><span class="token function">useSession</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>isPending<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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex min-h-screen items-center justify-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>p</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-gray-500<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Loading<span class="token operator">...</span><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>
    <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>session<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token string">"/signin"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
  <span class="token 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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col items-center justify-center min-h-screen gap-4 bg-white text-black<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-2xl font-bold<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Welcome back<span class="token punctuation">,</span> <span class="token punctuation">{</span>session<span class="token punctuation">.</span>user<span class="token punctuation">.</span>name<span class="token punctuation">}</span><span class="token operator">!</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-gray-600<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        You are logged <span class="token keyword">in</span> <span class="token keyword">as</span><span class="token punctuation">{</span><span class="token string">" "</span><span class="token punctuation">}</span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>font-semibold<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>session<span class="token punctuation">.</span>user<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>span</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>

      <span class="token operator">&lt;</span>button
        onClick<span class="token operator">=</span><span class="token punctuation">{</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
          <span class="token keyword">await</span> authClient<span class="token punctuation">.</span><span class="token function">signOut</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
            fetchOptions<span class="token punctuation">:</span> <span class="token punctuation">{</span>
              onSuccess<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>
                router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token string">"/signin"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
              <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token punctuation">}</span><span class="token punctuation">,</span>
          <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">}</span>
        className<span class="token operator">=</span><span class="token string">"px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 transition"</span>
      <span class="token operator">&gt;</span>
        Sign Out
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>We introduced the <code>useSession()</code> hook. This is the bridge between the frontend and the user&rsquo;s session. It gives us access to the current session and keeps it in sync as authentication changes. It returns two key values:</p><ul><li><strong>data</strong>: The session object containing user information (or null if not logged in).</li><li><strong>isPending</strong>: A boolean indicating whether the session is still loading. With this, we&rsquo;re able to show a loading state so the user doesn&rsquo;t see empty content.</li></ul><p>Finally, the Sign Out button calls <code>authClient.signOut()</code>. This function invalidates the session cookie and uses the <code>onSuccess</code> callback to send the user straight back to the login screen.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/authenticated-user-session.gif?sfvrsn=cf86529c_2" title="Displaying the authenticated user&#39;s session data" alt="Displaying the authenticated user&#39;s session data" /></p><h2 id="session-management-and-protected-routes">Session Management and Protected Routes</h2><p>Now that our app works, let&rsquo;s look at what happens under the hood.</p><p>When a user signs in, Better Auth creates a session and stores it as an HttpOnly cookie. Unlike regular cookies that you can read with <code>document.cookie</code>, these are blocked from frontend JavaScript entirely. Only the browser can send them automatically with each request to the server.</p><p>There are two main ways of securing your pages, and they serve different purposes:</p><ul><li><strong>Client-Side Protection</strong>: This is what we implemented in our <code>app/dashboard/page.tsx</code> earlier. We wait for the session to load in the browser, and, if it&rsquo;s missing, we redirect the user to the &ldquo;Sign In&rdquo; page.</li><li><strong>Server-Side Protection (better for security)</strong>: We check the session on the server before the page renders. If there&rsquo;s no valid session, the request gets blocked immediately, and the sensitive page content is never sent to the browser.</li></ul><p>To implement server-side protection, we use middleware. Middleware is code that runs before a page loads. It sits between the user&rsquo;s request and your page, checking conditions and deciding whether to allow access or redirect elsewhere. In Next.js, it runs on the server, so unauthorized users never even download the page.</p><h2 id="protecting-routes-with-middleware">Protecting Routes with Middleware</h2><p>Create a new file named <code>middleware.ts</code> at the same level as your app folder and add the following to it:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token comment">//middleware.ts</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> betterFetch <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@better-fetch/fetch"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">{</span> Session <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"better-auth/types"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> NextResponse<span class="token punctuation">,</span> <span class="token keyword">type</span> NextRequest <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"next/server"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token function">authMiddleware</span><span class="token punctuation">(</span>request<span class="token punctuation">:</span> NextRequest<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> data<span class="token punctuation">:</span> session <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token keyword">await</span> betterFetch<span class="token operator">&lt;</span>Session<span class="token operator">&gt;</span><span class="token punctuation">(</span>
    <span class="token string">"/api/auth/get-session"</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      baseURL<span class="token punctuation">:</span> request<span class="token punctuation">.</span>nextUrl<span class="token punctuation">.</span>origin<span class="token punctuation">,</span>
      headers<span class="token punctuation">:</span> <span class="token punctuation">{</span>
        cookie<span class="token punctuation">:</span> request<span class="token punctuation">.</span>headers<span class="token punctuation">.</span><span class="token keyword">get</span><span class="token punctuation">(</span><span class="token string">"cookie"</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token string">""</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>session<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> NextResponse<span class="token punctuation">.</span><span class="token function">redirect</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">URL</span><span class="token punctuation">(</span><span class="token string">"/signin"</span><span class="token punctuation">,</span> request<span class="token punctuation">.</span>url<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> NextResponse<span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> config <span class="token operator">=</span> <span class="token punctuation">{</span>
  matcher<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"/dashboard"</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 a nutshell, the config object at the bottom defines the rules of engagement, telling Next.js to strictly apply this security check only to routes starting with <code>/dashboard</code>. When a user attempts to visit the page, the middleware steps in to verify their session first. If the session is missing, it instantly blocks the request and redirects them to the &ldquo;Sign In&rdquo; page, preventing the protected content from reaching the browser.</p><h2 id="refactor-the-dashboard">Refactor the Dashboard</h2><p>Now that the server (middleware) is handling the security, we can simplify our dashboard page. We don&rsquo;t need to redirect from inside the component anymore, but we&rsquo;ll keep the data fetching to show the user&rsquo;s name and email address.</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token comment">//app/dashboard/page.tsx</span>
<span class="token string">"use client"</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> <span class="token punctuation">{</span> authClient <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@/lib/auth-client"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> useRouter <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"next/navigation"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">Dashboard</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> router <span class="token operator">=</span> <span class="token function">useRouter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> data<span class="token punctuation">:</span> session<span class="token punctuation">,</span> isPending <span class="token punctuation">}</span> <span class="token operator">=</span> authClient<span class="token punctuation">.</span><span class="token function">useSession</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>isPending<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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex min-h-screen items-center justify-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>p</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-gray-500<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Loading<span class="token operator">...</span><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>
    <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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex flex-col items-center justify-center min-h-screen 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>h1</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-2xl font-bold<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Dashboard<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h1</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>p-4 border rounded shadow-sm bg-white min-w-[300px]<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-gray-600 mb-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
          Signed <span class="token keyword">in</span> <span class="token keyword">as</span><span class="token punctuation">:</span><span class="token punctuation">{</span><span class="token string">" "</span><span class="token punctuation">}</span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>font-semibold text-black<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token punctuation">{</span>session<span class="token operator">?</span><span class="token punctuation">.</span>user<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>span</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text-xs text-gray-400<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>User ID<span class="token punctuation">:</span> <span class="token punctuation">{</span>session<span class="token operator">?</span><span class="token punctuation">.</span>user<span class="token punctuation">.</span>id<span class="token punctuation">}</span><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>

      <span class="token operator">&lt;</span>button
        onClick<span class="token operator">=</span><span class="token punctuation">{</span><span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
          <span class="token keyword">await</span> authClient<span class="token punctuation">.</span><span class="token function">signOut</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
            fetchOptions<span class="token punctuation">:</span> <span class="token punctuation">{</span>
              onSuccess<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>
                router<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token string">"/signin"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
              <span class="token punctuation">}</span><span class="token punctuation">,</span>
            <span class="token punctuation">}</span><span class="token punctuation">,</span>
          <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">}</span>
        className<span class="token operator">=</span><span class="token string">"px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 transition"</span>
      <span class="token operator">&gt;</span>
        Sign Out
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Now when we sign out from the app and try to manually visit <a target="_blank" href="http://localhost:3000/dashboard">http://localhost:3000/dashboard</a>, we should be instantly redirected back to the sign-in page. The dashboard will never attempt to render because there&rsquo;s no valid session.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/access-the-dashboard.gif?sfvrsn=32489ceb_2" title="Trying to access the dashboard without a session" alt="Trying to access the dashboard without a session" /></p><h2 id="conclusion">Conclusion</h2><p>At the base level, Better Auth handles the heavy lifting for authentication. The good thing is you aren&rsquo;t reinventing the wheel, and because it works with pretty much everything, you don&rsquo;t even have to rewrite your whole auth setup if you switch frameworks down the line. It just works, so you can focus on your app.</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">An Introduction to TanStack DB: A Reactive Data Layer for Modern Web Applications</h4></div><div class="col-8"><p class="u-fs16 u-mb0">In this article, we will build a to-do app to <a target="_blank" href="https://www.telerik.com https://www.telerik.com/blogs/introduction-tanstack-db-reactive-data-layer-modern-web-applications">see TanStack DB in action</a>. We&rsquo;ll cover how to set up collections, what &ldquo;live queries&rdquo; are, and why &ldquo;differential dataflow&rdquo; makes it so fast. We'll also cover when to use TanStack DB and when TanStack Query alone is enough.</p></div></div></aside><img src="https://feeds.telerik.com/link/23056/17316561.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:9bf61f7e-e7a4-44d4-aba4-e8c1fc74615b</id>
    <title type="text">Progress Telerik Agentic UI Generator vs. Syncfusion Agentic UI Builder</title>
    <summary type="text">See how the AI-based UI creation tools from devtools powerhouses Progress and Syncfusion stack up when they go head to head.</summary>
    <published>2026-04-08T16:38:33Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>Hassan Djirdeh </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17315938/progress-telerik-agentic-ui-generator-vs-syncfusion-agentic-ui-builder"/>
    <content type="text"><![CDATA[<p><span class="featured">See how the AI-based UI creation tools from devtools powerhouses Progress and Syncfusion stack up when they go head to head. </span></p><p>AI-powered code generation has become a significant part of how developers build user interfaces. The <a target="_blank" href="https://survey.stackoverflow.co/2025/ai/#1-ai-tools-in-the-development-process">2025 Stack Overflow Developer Survey</a> found that 84% of developers are now using or planning to use AI tools in their development process.</p><p>Original AI adoption has been through tools like code autocompletion and chat-based assistants, but agentic UI tools take things further. Rather than suggesting the next line of code, they can take a natural language prompt and produce working, styled UI code in seconds.</p><p>However, not all AI generation tools are created equal. The quality of the output, how reliably it builds on the first attempt, how fast it runs and how well it handles things like accessibility and theming can vary quite a bit between tools. These differences matter because a tool that requires a large number of follow-up prompts to correct errors or produce code that doesn&rsquo;t match the original requirements isn&rsquo;t truly saving us time.</p><p>In this article, we&rsquo;ll look at a head-to-head comparison between two agentic UI generation tools: the <strong>Progress Telerik Agentic UI Generator</strong> which includes the <a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/ai/agentic-ui-generator/getting-started">Blazor Agentic UI Generator for .NET/Blazor</a> and the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/ai-tools/agentic-ui-generator/getting-started">Kendo UI Generator for Angular and React</a>, and the <a target="_blank" href="https://www.syncfusion.com/explore/agentic-ui-builder/"><strong>Syncfusion Agentic UI Builder</strong></a>. We&rsquo;ll walk through the results of extensive testing across multiple frameworks, covering output quality, performance, stability, accessibility, theming and more.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/progress-vs-syncfusion.jpg?sfvrsn=d281e72b_2" alt="Progress Telerik vs. Syncfusion" /></p><h2 id="how-the-comparison-was-conducted">How the Comparison Was Conducted</h2><p>To make this comparison as fair and thorough as possible, both tools were tested across multiple frameworks using two evaluation approaches.</p><h3 id="blazor.net">Blazor/.NET</h3><p>On the Blazor/.NET side, 23 prompts were run through the Telerik Agentic UI Generator and the Syncfusion Agentic UI Builder. These prompts ranged from simple single-component requests to complex multi-section layouts like e-commerce catalog pages and CMS admin dashboards. Each prompt&rsquo;s output was rated on a 0 to 7 scale:</p><ul><li><strong>0-1</strong>: Doesn&rsquo;t build or crashes with no UI</li><li><strong>2-3</strong>: Partially renders, major sections broken</li><li><strong>4</strong>: Renders but requires multiple prompts to clear build or runtime errors</li><li><strong>5</strong>: Renders but doesn&rsquo;t match prompt requirements in several areas</li><li><strong>6</strong>: Mostly complete, minor issues</li><li><strong>7</strong>: Fully matches requirements</li></ul><p>The evaluation also tracked how many prompts were needed before the output could successfully build and render, as well as the time (in seconds) from initial prompt submission to output generation.</p><h3 id="angular-and-react">Angular and React</h3><p>For Angular and React, a dedicated evaluation was conducted comparing the Kendo Agentic UI Generator against the Syncfusion Agentic UI Builder. Rather than scoring individual prompts, this evaluation focused on a technical comparison of the tools themselves. By running many prompts and examining the context returned by each tool, the analysis compared how each MCP server helps the AI agent arrive at its output across categories like layout, styling, accessibility, documentation and component handling.</p><h2 id="output-quality-and-reliability">Output Quality and Reliability</h2><p>This is the category where the gap between the two tools becomes immediately clear.</p><p>Across the 23 Blazor prompts, the Telerik Agentic UI Generator consistently produced complete, requirement-matching output on the first attempt. The Syncfusion Agentic UI Builder told a different story. It experienced <strong>five failures</strong> where no usable UI was generated at all, and several other prompts that required follow-up prompts to fix errors before the output could even build. Let&rsquo;s look at a couple of examples.</p><h3 id="failures">Failures</h3><p>The very first prompt asked both tools to build a login screen with email/password validation and an admin dashboard with a sidebar menu, key metrics and recent activity.</p><p><strong>Prompt</strong>: <em>&ldquo;I have created an empty application that now needs a login screen and an admin dashboard. Add a login form with email/password fields and validation. After a successful login, redirect to an admin dashboard page featuring a sidebar menu and a main content area displaying key metrics and recent activity.&rdquo;</em></p><p>The Telerik Generator produced a fully working output on the first attempt.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/blazor-agentic-ui-prompt-1.png?sfvrsn=ead93eba_1" alt="Telerik UI Generator produced dashboard" /></p><p>The Syncfusion Builder returned an error along the lines of: <code>Error. This response is truncated because it is too long. Try rephrasing your question.</code> No UI was generated at all.</p><p>The second prompt told both tools to create a monitoring dashboard with system health KPIs, a log stream panel, charts for API response times and requests per service.</p><p><strong>Prompt</strong>: <em>&ldquo;Create a new page using the existing top navigation and footer. In the middle, add 3 rows with 3 responsive columns each. The top row shows system health KPIs for CPU, memory and error counts. The middle rows include a Log Stream panel, a line Chart of API response times and a bar chart of requests per service. The bottom row contains a Deployment History table, an Alerts panel and a list of open tickets.&rdquo;</em></p><p>The Telerik Generator again produced a fully working output on the first pass.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/blazor-agentic-ui-prompt-2.png?sfvrsn=150ecc0b_1" alt="Telerik Generator produced dashboard with graphs" /></p><p>The Syncfusion Builder hit the same truncation error.</p><p>These are the kinds of prompts that represent real-world use cases. A login screen with a dashboard and a monitoring page with multiple data panels aren&rsquo;t unusual requests.</p><h3 id="errors-requiring-follow-ups">Errors Requiring Follow-ups</h3><p>Not every issue is an error during the generation process. Some prompts produced output that needed additional work to get running.</p><p>We fired a prompt to ask for a product catalog page with a responsive layout, product cards, a filtering toolbar and expandable detail views. The Telerik Generator handled it cleanly on the first attempt. The Syncfusion Builder hit an error about a missing component parameter and needed a second follow-up prompt to fix it.</p><p><strong>Prompt</strong>: <em>&ldquo;Create a product catalog page with a responsive CSS layout. The layout should display product cards. Add a toolbar with filtering options, and expandable detail view for each product that work seamlessly on mobile, tablet, and desktop.&rdquo;</em></p><p>The first output by the Telerik Blazor generator:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/blazor-agentic-ui-prompt-4.png?sfvrsn=5b3d958b_1" alt="Telerik Blazor AI UI Generator produced product catalog" /></p><p>The final output from Syncfusion, after the necessary follow-up prompt:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/syncfusion-agentic-ui-prompt-4.png?sfvrsn=a91f64f0_1" alt="Syncfusion product catalog" /></p><p>The Syncfusion Builder&rsquo;s errors in these cases often involved referencing component parameters that don&rsquo;t exist, which may suggest the tool&rsquo;s AI agent is working with less reliable component information during generation.</p><h3 id="partial-results">Partial Results</h3><p>Not all issues showed up as crashes or errors needing follow-ups. Let&rsquo;s look at an example of a prompt that was made which involved a theming request.</p><p><strong>Prompt</strong>: <em>&ldquo;Generate a custom theme for a corporate blue and green color scheme with high contrast accessibility requirements.&rdquo;</em></p><p>The Telerik Agentic UI Generator created the output matching the requirements.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/blazor-agentic-ui-prompt-13.png?sfvrsn=ed53d11a_1" alt="Telerik Agentic UI generator blue n=and green" /></p><p>The Syncfusion Builder did create a custom theme, but the expected theme wasn&rsquo;t applied to the output.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-04/syncfusion-agentic-ui-prompt-13.png?sfvrsn=eaac1328_1" alt="Syncfusion green and blue missing" /></p><h3 id="the-bigger-picture">The Bigger Picture</h3><p>Even when we set aside the failures and only look at prompts where the Syncfusion Builder produced something usable, the outputs still frequently fell short of the original requirements.</p><p>On the Angular and React side, the findings were consistent. While the final rendered UI sometimes appeared similar at a glance, the most significant differences were in how each tool&rsquo;s MCP server helped the agent arrive at that output. The Kendo Generator&rsquo;s tools consistently delivered more relevant, targeted context to the AI agent, filtering results to what was actually needed for the prompt. The Syncfusion Builder&rsquo;s tools returned broader, less filtered responses, often including full component prop lists and governance rules regardless of what was asked.</p><h2 id="performance">Performance</h2><p>Speed matters when we&rsquo;re iterating on UI designs with AI. Waiting several minutes for a complex layout to generate can significantly slow down the development flow.</p><p>Across the Blazor evaluation, the Telerik Generator was consistently faster. The speed advantage was most noticeable on complex layout prompts. For example, on a complex e-commerce catalog page with filtering, sorting, responsive breakpoints and pagination, the Telerik Generator completed in about 799 seconds while the Syncfusion Builder took 908 seconds.</p><p>There was one outlier where Syncfusion was notably faster (a simpler single-component case), but across the full set of 23 prompts, the speed advantage consistently favored Telerik.</p><h2 id="accessibility">Accessibility</h2><p>Accessibility is often treated as an afterthought, but for enterprise applications, it&rsquo;s a requirement, not a nice-to-have!</p><p>The Kendo Generator includes a dedicated accessibility tool (<a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/ai-tools/agentic-ui-generator/prompt-library#accessibility-assistant">#kendo_accessibility_assistant</a>) that retrieves WCAG 2.2 Level AA guidelines as a mandatory step in the generation process. It also fetches component-specific accessibility requirements for each component used in the output. This leads to every piece of generated UI having accessibility considerations baked in from the start.</p><p>From what we can gather, and at the time of writing, the Syncfusion Builder doesn&rsquo;t have a strong accessibility tool like the <code>#kendo_accessibility_assistant</code>.</p><h2 id="theming-layout-and-documentation">Theming, Layout and Documentation</h2><p>Beyond output quality and speed, the Angular and React evaluation revealed some important architectural differences in how each tool handles theming, layout and documentation.</p><h3 id="theming">Theming</h3><p><strong>Theming</strong> is more than color selection. The Kendo UI Generator produces a complete design system from a single prompt, including colors, typography, spacing scales, border radius, elevation and shadows. The Syncfusion Builder&rsquo;s style tool is limited to component state colors and semantic colors. Typography, spacing and elevation aren&rsquo;t reachable through it, so the generated output may look right in terms of color but lack the design coherence that holds a full application together.</p><h3 id="layout">Layout</h3><p><strong>Layout</strong> is also where a notable dependency difference comes in. The Kendo UI Generator uses a self-contained layout system built on its own CSS utilities, introducing no external dependencies. The Syncfusion Builder relies on <a target="_blank" href="https://tailwindcss.com/">Tailwind CSS</a>, injected into every project, and works from a catalog of 265+ prebuilt blocks. When the desired layout doesn&rsquo;t match a catalog entry, the result approximates what was asked for.</p><p>For teams already using Tailwind, this might feel natural but for projects on a different CSS framework (<a target="_blank" href="https://material.angular.dev/">Angular Material</a>, for example), adding Tailwind on top can introduce style conflicts. This can also be a potential blocker for organizations that restrict third-party dependencies.</p><p>Responsiveness follows a similar split: the Kendo UI Generator has a dedicated phase with explicit breakpoint reasoning, while the Syncfusion Builder inherits responsive behavior from Tailwind&rsquo;s defaults without any first-class responsive logic of its own.</p><h3 id="documentation">Documentation</h3><p><strong>Documentation</strong> plays a bigger role in output quality than it might seem, since it directly shapes how well the AI agent understands the components it&rsquo;s generating. The Kendo UI Generator injects relevant documentation chunks directly into every component tool call, including API references with full type signatures. The Syncfusion Builder treats documentation as a separate tool, triggered only by specific keywords. At the time of the evaluation, results from this tool were inconsistent and the service had been unavailable for over 24 hours for both Angular and React.</p><p>One other important observation from the Angular and React evaluation: the Kendo UI Generator customizes its workflow per prompt using confidence scores, including only the tools and instructions relevant to each request. The Syncfusion Builder runs the same fixed sequence for most prompts regardless of complexity, and its component tools return the full prop list and approximately 250 lines of governance rules on every call. The result is that <strong>the Kendo UI agent operates on targeted, preprocessed context while the Syncfusion agent carries a heavier payload for every request</strong>.</p><h2 id="wrap-up">Wrap-up</h2><p>The comparison across Blazor, Angular and React tells a consistent story with these agentic UI generators. The Progress Telerik Agentic UI Generator outperforms the Syncfusion Agentic UI Builder across output quality, reliability, performance, accessibility, theming, layout and documentation.</p><p>On the Blazor side, the Telerik Generator had zero full failures across 23 prompts while Syncfusion had five, along with a consistent speed advantage on complex prompts and more reliable first-pass output. On the Angular and React side, the advantages show up in the underlying architecture, where the Kendo Generator&rsquo;s tools consistently deliver cleaner, more relevant context to the AI agent.</p><hr /><blockquote><p>Get started with AI-empowered development. The <a target="_blank" href="https://www.telerik.com/mcp-servers">Kendo UI and Telerik AI tools</a> are ready. And they come as part of the free trial of the component libraries.</p><br /><p><a href="https://www.telerik.com/mcp-servers#how-to-try" class="Btn" target="_blank">Try Now</a></p></blockquote><hr /><p>For more information on the Progress Telerik Agentic UI Generator, check out the following resources:</p><ul><li><a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/ai/agentic-ui-generator/getting-started">Telerik Agentic UI Generator for Blazor</a></li><li><a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/ai-tools/agentic-ui-generator/getting-started">Kendo UI Generator for Angular</a></li><li><a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/ai-tools/agentic-ui-generator/getting-started">Kendo UI Generator for React</a></li></ul><p>The full comparison data referenced in this article is available for download:</p><ul><li><a target="_blank" href="https://www.telerik.com/docs/default-source/blogs-docs//syncfusion-builder-vs-telerik-generator-comparison.xlsx">Telerik UI for Blazor Agentic UI Generator vs Syncfusion Agentic UI Builder Comparative Evaluation (XLSX)</a></li><li><a target="_blank" href="https://www.telerik.com/docs/default-source/blogs-docs//syncfusion-ui-builder-vs-kendo-ui-generator.xlsx">Kendo Agentic UI Generator vs Syncfusion Agentic UI Builder (XLSX)</a></li></ul><hr /><h2 id="summary-faqs">Summary FAQs</h2><h3 id="what-do-these-tools-do">What Do These Tools Do?</h3><h4 id="what-telerik-agentic-ui-generator-is">What Telerik Agentic UI Generator Is</h4><p>According to the <a href="https://www.telerik.com/mcp-servers" target="_blank">Progress Telerik website</a>, the Telerik Agentic UI Generator for Telerik and Kendo UI libraries is an intelligent development tool delivered through the Model Context Protocol (MCP) Server that enables UI generation from natural language prompts. Once configured and authenticated, you can use the Agentic UI Generator tool (#telerik_ui_generator) together with the available specialized MCP assistants.</p><h4 id="what-syncfusion-agentic-ui-builder-is">What Syncfusion Agentic UI Builder Is</h4><p>According to the <a target="_blank" href="https://www.syncfusion.com/explore/agentic-ui-builder/">Syncfusion site</a>, Syncfusion&rsquo;s Agentic UI Builder is designed to work inside an IDE to generate UIs, dashboards or pages with Syncfusion components with natural language commands. Syncfusion uses MCP integration to access component APIs, prebuilt UI blocks, styling configurations, icon libraries and code generation.</p><h3 id="why-does-mcp-matter-in-both-products">Why Does MCP Matter in Both Products?</h3><p>Both brands&rsquo; tools use Model Context Protocol (MCP) servers to connect AI applications directly to the software specs for the individual library. This means that the AI is informed on component documentation, best practices, workflows and tooling provided by the UI library, so the AI can create a user interface more in line with how the library was designed to work. It should result in better outputs than if an AI were cobbling together an interface without guidance.</p><p>Learn more about the individual MCP servers:</p><ul><li><a href="https://www.telerik.com/blazor-mcp-servers" target="_blank">Telerik UI for Blazor MCP Servers</a></li><li><a href="https://www.telerik.com/angular-mcp-servers" target="_blank">Kendo UI for Angular MCP Servers</a></li><li><a href="https://www.telerik.com/react-mcp-servers" target="_blank">KendoReact MCP Servers</a></li><li><a target="_blank" href="https://ej2.syncfusion.com/react/documentation/mcp-server/overview">Syncfusion EJ2 React MCP Server</a></li><li><a target="_blank" href="https://ej2.syncfusion.com/angular/documentation/mcp-server/overview">Syncfusion EJ2 Angular MCP Server</a></li><li><a target="_blank" href="https://blazor.syncfusion.com/documentation/mcp-server/overview">Syncfusion Blazor MCP Server</a></li></ul><h3 id="are-prompt-libraries-included">Are Prompt Libraries Included?</h3><h4 id="telerik-and-kendo-ui-generator-prompt-libraries">Telerik and Kendo UI Generator Prompt Libraries</h4><p>Yes! Prompt libraries are included for the Kendo UI and Telerik AI UI Generator tools:</p><ul><li>Blazor: <a target="_blank" href="https://www.telerik.com/blazor-ui/documentation/ai/agentic-ui-generator/prompt-library">https://www.telerik.com/blazor-ui/documentation/ai/agentic-ui-generator/prompt-library</a></li><li>React: <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/ai-tools/agentic-ui-generator/getting-started">https://www.telerik.com/kendo-react-ui/components/ai-tools/agentic-ui-generator/getting-started</a></li><li>Angular: <a target="_blank" href="https://www.telerik.com/kendo-angular-ui/components/ai-tools/agentic-ui-generator/prompt-library">https://www.telerik.com/kendo-angular-ui/components/ai-tools/agentic-ui-generator/prompt-library</a></li></ul><h4 id="syncfusion-agentic-ui-builder-prompt-libraries">Syncfusion Agentic UI Builder Prompt Libraries</h4><p>Yes! Prompt libraries are included for the Syncfusion Agentic UI Builder tools:</p><ul><li>Blazor: <a target="_blank" href="https://blazor.syncfusion.com/documentation/mcp-server/agentic-ui-builder/prompt-library">https://blazor.syncfusion.com/documentation/mcp-server/agentic-ui-builder/prompt-library</a></li><li>React: <a target="_blank" href="https://ej2.syncfusion.com/react/documentation/mcp-server/agentic-ui-builder/prompt-library">https://ej2.syncfusion.com/react/documentation/mcp-server/agentic-ui-builder/prompt-library</a></li><li>Angular: <a target="_blank" href="https://ej2.syncfusion.com/angular/documentation/mcp-server/agentic-ui-builder/prompt-library">https://ej2.syncfusion.com/angular/documentation/mcp-server/agentic-ui-builder/prompt-library</a></li></ul><img src="https://feeds.telerik.com/link/23056/17315938.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:4733c0a3-bf88-4efb-8d20-246db8da5688</id>
    <title type="text">The Great React Grid-Off: KendoReact vs. MUI vs. AG Grid</title>
    <summary type="text">Curious how the best React grids on the market compete head-to-head? KendoReact vs. MUI vs. AG Grid—let’s see it!</summary>
    <published>2026-03-19T20:15:24Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>Kathryn Grayson Nanz </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17302239/great-react-grid-off-kendoreact-vs-mui-vs-ag-grid"/>
    <content type="text"><![CDATA[<p><span class="featured">Curious how the best React grids on the market compete head-to-head? KendoReact vs. MUI vs. AG Grid&mdash;let&rsquo;s see it!</span></p><p>If you&rsquo;re looking for an enterprise-level <a href="https://www.telerik.com/kendo-react-ui/components/grid" target="_blank">data grid for your React</a> application, there&rsquo;s no shortage of options to consider. When you&rsquo;re making a comparison, many aspects will be subjective; those things will ultimately come down to which approach makes the most sense to you as a developer and what you think will be easiest for your existing team to adopt.</p><p>However, there are also performance-related metrics that are objective, which we can test directly and draw clear conclusions around. In the hopes of making that task slightly easier for you, we ran the tests ourselves and would like to share the results.</p><p>This benchmark compares the rendering and virtualization performance of three popular React data grids: Progress KendoReact, MUI and AG Grid. The goal is to provide objective, repeatable performance data across common real-world scenarios&mdash;from small datasets to large, virtualized grids with up to 1 million rows. We wanted to mirror production-like situations as closely as possible, rather than simply testing demo scenarios that aren&rsquo;t commonly reflected in real applications.</p><h2 id="sneak-peek-at-the-results">Sneak Peek at the Results</h2><p>For those of you who are just eager to see our conclusions, below is a summary of our results. For those interested in diving deeper into the methodology and details, read on!</p><ul><li>For basic grids (&le;5k rows, no virtualization), MUI is faster.</li><li>When paging is enabled, the KendoReact consistently leads.</li><li>If virtualization is needed (10k&ndash;1M rows), KendoReact is significantly faster, with performance gaps widening as datasets grow.</li><li>In situations with row <em>and</em> column virtualization, MUI performs well on smaller datasets, while KendoReact scales better at higher volumes.</li><li>If you&rsquo;re working with very large datasets, the best choice is the KendoReact Data Grid.</li></ul><h2 id="how-we-measure-performance">How We Measure Performance</h2><p>Before we look at the numbers, let&rsquo;s take a second to discuss how we calculated them.</p><p>We run automated performance tests twice each week (for our own visibility) and track the results to a set of internal dashboards. This is for our own learning&mdash;we want to keep an eye on our grid and how it performs in real-world scenarios, as compared to our competitors. All our tests are designed to be repeatable and to reflect realistic application usage.</p><p>We run our tests via GitHub Actions, using Selenium WebDriver and a custom browser automation in a headless Chromium browser. The tests use realistic, production-like data generated with the Faker.js library (ours is for a &ldquo;warehouse,&rdquo; with columns for ID, name, price, start date, units in stock, discontinued status and a description). For tests requiring wider datasets, additional column content is dynamically generated, with each cell containing realistic five-character alphanumeric data. Each scenario is executed eight times, and we average the results for consistency.</p><p>The testing framework uses high-precision millisecond timing, moving through the following steps:</p><ol><li><strong>Pre-render State:</strong> To begin, the Grid component is <em>not</em> initially loaded on the page.</li><li><strong>Data Generation:</strong> Test data is generated and prepared in memory before timing begins.</li><li><strong>Timer Start:</strong> A precise timestamp is captured using <code>Date.now()</code> immediately before grid initialization.</li><li><strong>Grid Load:</strong> The Grid component is rendered with a full dataset.</li><li><strong>Completion Detection:</strong> Automated polling (every 10ms) detects when the last table cell becomes visible in the DOM using XPath selectors (<code>td[contains(@class,'k-table-td')]</code>).</li><li><strong>Timer Stop:</strong> A timestamp is captured when the target cell is detected.</li><li><strong>Result:</strong> The rendering time in milliseconds is logged.</li></ol><h2 id="what-gets-measured">What Gets Measured</h2><p>We look at our KendoReact Data Grid (Premium) as compared to AG Grid (Community Edition) and MUI Grid (Material-UI Data Grid) in terms of pure component rendering time, DOM manipulation performance, virtual scrolling efficiency and template rendering overhead. We do not measure network latency, API calls or data generation time.</p><p>For all the grids, we test in four different configurations:</p><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 <tr><th style="width:25%;padding:5px;"><strong>Scenario</strong></th><th style="width:25%;padding:5px;"><strong>Rows</strong></th><th style="width:25%;padding:5px;"><strong>Columns</strong></th><th style="width:25%;padding:5px;"><strong>Features</strong></th></tr></thead><tbody><tr><td style="width:25%;padding:5px;"><strong>Basic Grid (no virtualization)</strong></td><td style="width:25%;padding:5px;">5,000</td><td style="width:25%;padding:5px;">20</td><td style="width:25%;padding:5px;">Sorting and Filtering</td></tr><tr><td style="width:25%;padding:5px;"><strong>Paged Grid (no virtualization)</strong></td><td style="width:25%;padding:5px;">5,000</td><td style="width:25%;padding:5px;">20</td><td style="width:25%;padding:5px;">Sorting, Filtering, Paging</td></tr><tr><td style="width:25%;padding:5px;"><strong>Row Virtualization</strong></td><td style="width:25%;padding:5px;">1,000,000</td><td style="width:25%;padding:5px;">10</td><td style="width:25%;padding:5px;">Sorting, Filtering, Row Virtualization</td></tr><tr><td style="width:25%;padding:5px;"><strong>Row + Column Virtualization</strong></td><td style="width:25%;padding:5px;">100,000</td><td style="width:25%;padding:5px;">100</td><td style="width:25%;padding:5px;">Sorting, Filtering, Dual-Axis Virtualization</td></tr></tbody></table><br /><h2 id="the-results-in-detail">The Results in Detail</h2><p>Now that the scope and scale of the tests have been established, let&rsquo;s look at the results. This particular benchmark compared KendoReact v13.0.0 against AG Grid Community v34.3.1 and MUI Grid v8.11.0. <strong>Below are the results from this specific test, but it&rsquo;s worth noting that we repeat these tests regularly and the results are consistently similar.</strong></p><h3 id="grid-basic-tests">Grid Basic Tests</h3><h4 id="rows-x-10-cols">100 Rows x 10 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><style>table,
 th,
        td {
            border: 1px;
            border-color: #bdbdba;
            border-style: dotted;
            border-collapse: collapse;
            margin-right: auto;
            text-align: left;
        }
    </style>
 <thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>MUI</td><td>671.00ms</td></tr><tr><td>KendoReact</td><td>709.00ms</td></tr><tr><td>AG Grid</td><td>841.25ms</td></tr></tbody></table><br /><h4 id="rows-x-100-cols">100 Rows x 100 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>MUI</td><td>1762.88ms</td></tr><tr><td>AG Grid</td><td>1817ms</td></tr><tr><td>KendoReact</td><td>1998.00ms</td></tr></tbody></table><br /><h4 id="rows-x-10-cols-1">1,000 Rows x 10 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>AG Grid</td><td>2133.63ms</td></tr><tr><td>MUI</td><td>2364.25ms</td></tr><tr><td>KendoReact</td><td>2475.88ms</td></tr></tbody></table><br /><h4 id="rows-x-20-cols">5,000 Rows x 20 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>MUI</td><td>104004.25ms</td></tr><tr><td>KendoReact</td><td>14718.25ms</td></tr><tr><td>AG Grid</td><td>16494.63ms</td></tr></tbody></table><br /><h3 id="grid-paging-tests">Grid Paging Tests</h3><h4 id="rows-x-10-cols-2">100 Rows x 10 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>KendoReact</td><td>332.25ms</td></tr><tr><td>MUI</td><td>413.88ms</td></tr><tr><td>AG Grid</td><td>459.50ms</td></tr></tbody></table><br /><h4 id="rows-x-100-cols-1">100 Rows x 100 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>KendoReact</td><td>824.13ms</td></tr><tr><td>AG Grid</td><td>846.00ms</td></tr><tr><td>MUI</td><td>988.88ms</td></tr></tbody></table><br /><h4 id="rows-x-10-cols-3">1,000 Rows x 10 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>KendoReact</td><td>335.63ms</td></tr><tr><td>MUI</td><td>424.38ms</td></tr><tr><td>AG Grid</td><td>448.63ms</td></tr></tbody></table><br /><h4 id="rows-x-20-cols-1">5,000 Rows x 20 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>KendoReact</td><td>374.88ms</td></tr><tr><td>MUI</td><td>462.38ms</td></tr><tr><td>AG Grid</td><td>494.75ms</td></tr></tbody></table><br /><h3 id="column-virtualization-tests">Column Virtualization Tests</h3><h4 id="rows-x-100-cols-2">10,000 Rows x 100 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>MUI</td><td>489.13ms</td></tr><tr><td>AG Grid</td><td>495.88ms</td></tr><tr><td>KendoReact</td><td>763.25ms</td></tr></tbody></table><br /><h4 id="rows-x-100-cols-3">100,000 Rows x 100 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>KendoReact</td><td>621.25ms</td></tr><tr><td>AG Grid</td><td>847.75ms</td></tr><tr><td>MUI</td><td>1079.88ms</td></tr></tbody></table><br /><h3 id="dual-axis-virtualization-tests">Dual-Axis Virtualization Tests</h3><h4 id="rows-x-10-cols-4">10,000 Rows x 10 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>KendoReact</td><td>319.25ms</td></tr><tr><td>MUI</td><td>419.50ms</td></tr><tr><td>AG Grid</td><td>601.25ms</td></tr></tbody></table><br /><h4 id="rows-x-10-cols-5">100,000 Rows x 10 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>KendoReact</td><td>350.75ms</td></tr><tr><td>MUI</td><td>705.38ms</td></tr><tr><td>AG Grid</td><td>798.75ms</td></tr></tbody></table><br /><h4 id="rows-x-10-cols-6">1,000,000 Rows x 10 Cols</h4><table cellspacing="5" style="text-align:left;margin-right:auto;"><thead><style>table,
 th,
            td {
                border: 1px;
                border-color: #bdbdba;
                border-style: dotted;
                border-collapse: collapse;
                margin-right: auto;
                text-align: left;
            }
        </style>
 </thead><thead><tr><th>Vendor</th><th>Performance Time</th></tr></thead><tbody><tr style="width:50%;background-color:#ffff35;"><td>KendoReact</td><td>826.63ms</td></tr><tr><td>MUI</td><td>5608.38ms</td></tr><tr><td>AG Grid</td><td>5974.25ms</td></tr></tbody></table><br /><h3 id="basic-grid">Basic Grid</h3><p>For the Basic Grid, MUI came out on top&mdash;although, it&rsquo;s worth remembering that in all these situations, we&rsquo;re talking about a matter of milliseconds (note that I&rsquo;ve converted the results into seconds in these summaries, since that&rsquo;s a bit easier to mentally parse). Still, they consistently clocked in faster than both KendoReact and AG Grid.</p><p>For a 100x10 grid, MUI (0.67s) was faster than KendoReact (0.71s) and AG Grid (0.85s). Even at 5,000x20, MUI (10.4s) still kept the lead over both KendoReact (14.7s) and AG Grid (16.5s).</p><p><strong>For small datasets without virtualization, MUI Grid shows slightly faster initial render times, though all three grids perform within the same order of magnitude.</strong></p><h3 id="paging">Paging</h3><p>The situation changes once other, more complicated factors are included. When we enable paging, we see KendoReact move into the lead.</p><p>In a 100x10 grid, KendoReact (0.33s) is faster than MUI (0.41s) and AG Grid (0.46s). Scaled up to a 5,000x20 grid, KendoReact (0.38s) still has the edge on MUI (0.46s), which in turn beats out AG Grid (0.50s).</p><p><strong>Once paging is enabled, the KendoReact Grid consistently outperforms both MUI Grid and AG Grid across all tested dataset sizes.</strong></p><h3 id="row-virtualization">Row Virtualization</h3><p>Similarly, KendoReact remains on top for row virtualization. For a 10,000x10 grid, KendoReact (0.32s) is more performant than both MUI (0.42s) and AG Grid (0.60s).</p><p>Push that to 1,000,000x10, and we start to see a truly significant difference: KendoReact (0.83s) is significantly faster than MUI (5.6s) and AG Grid (6s). Obviously, that&rsquo;s a testing scenario that pushes the boundaries of capability, but it demonstrates the kind of compounding performance costs that developers need to be aware of when dealing with larger datasets.</p><p><strong>As dataset size increases, performance gaps widen significantly. At 1,000,000 rows, KendoReact renders in under 1 second, while MUI Grid and AG Grid require more than 5 seconds.</strong></p><h3 id="row--column-virtualization">Row + Column Virtualization</h3><p>MUI and KendoReact split the victory on Row + Column Virtualization.</p><p>When we tested with a smaller dataset, MUI takes the lead: at 10,000x100, MUI (0.49s) is faster than AG Grid (0.5s), which is faster than KendoReact (0.77s).</p><p>However, KendoReact takes the crown when tested with the larger dataset: at 100,000x100, KendoReact (0.62s) beats AG Grid (0.85s) and MUI (1.1s).</p><p><strong>KendoReact scales more predictably as both row and column counts grow, avoiding compounding render costs.</strong></p><h2 id="which-grid-is-best-for-your-team">Which Grid Is Best for Your Team?</h2><p>There&rsquo;s a long-running joke: no matter what they&rsquo;re asked, a senior developer never says &ldquo;yes&rdquo; or &ldquo;no&rdquo; They always say: &ldquo;It depends.&rdquo;</p><p>I must be showing my true colors here, because when faced with the question of which grid to pick, I find myself saying &hellip; well, it depends!</p><p><strong>You might prefer MUI Grid if:</strong></p><ul><li>Your datasets are small</li><li>You prioritize Material UI consistency</li><li>You rarely rely on virtualization</li></ul><p><strong>You might prefer KendoReact Grid if:</strong></p><ul><li>Your app handles large or growing datasets</li><li>Virtualization and performance are top priorities</li><li>You need predictable enterprise-grade performance at scale</li></ul><p>If you just need basic grid functionality, all the grids we looked at here were honestly pretty evenly matched in terms of performance.</p><p>In this case, as a tech lead, I would default to other determining factors to help tip the scales. What other components are included in the library that could be leveraged in our app? How does it meet the needs of our users who rely on accessibility functionality? How customizable is it? How easy is it to integrate AI-related functionality? How often is it updated, and what are the available customer support offerings?</p><p>How you weigh and compare the answers to these questions will ultimately come down to what factors are most important for both the userbase of your specific application and the needs of your specific team.</p><p>However, if you&rsquo;re dealing with truly large datasets and virtualization is a high priority, then KendoReact is the frontrunner. And the larger the dataset gets, the greater the disparity between KendoReact and the competitors.</p><p>If your application needs to handle truly large amounts of data, there&rsquo;s one grid that&rsquo;s simply better equipped to do so: the KendoReact Data Grid.</p><h2 id="try-it-yourself">Try It Yourself!</h2><p>Of course, we highly encourage you to try this experiment for yourself! That&rsquo;s why we shared our methodology: this is meant to be repeatable and standardized. If you run these tests and see different results, please let us know.</p><p>You can even try the full KendoReact library free for 30 days:</p><p><a target="_blank" href="https://www.telerik.com/try/kendo-react-ui" class="Btn">Try KendoReact</a></p><img src="https://feeds.telerik.com/link/23056/17302239.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:8fed6f1e-c388-43cd-bda2-ec55eced7d62</id>
    <title type="text">An Introduction to TanStack DB: A Reactive Data Layer for Modern Web Applications</title>
    <summary type="text">In this article, we will build a to-do app to see TanStack DB in action. We’ll cover how to set up collections, what “live queries” are, and why “differential dataflow” makes it so fast. We'll also cover when to use TanStack DB and when TanStack Query alone is enough.</summary>
    <published>2026-03-03T16:00:04Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>Christian Nwamba </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17288834/introduction-tanstack-db-reactive-data-layer-modern-web-applications"/>
    <content type="text"><![CDATA[<p><span class="featured">In this article, we will build a to-do app to see TanStack DB in action. We&rsquo;ll cover how to set up collections, what &ldquo;live queries&rdquo; are, and why &ldquo;differential dataflow&rdquo; makes it so fast. We'll also cover when to use TanStack DB and when TanStack Query alone is enough.</span></p><p><a target="_blank" href="https://tanstack.com/query/latest">TanStack Query</a> is a popular library for developers to manage server data in <a target="_blank" href="https://react.dev/">React</a> applications. While it excels at fetching and caching data, it has one fundamental limitation: it treats all data as independent key-value pairs.</p><p>For example, TanStack Query doesn&rsquo;t understand that your <code>['todos']</code> data is related to your <code>['categories']</code> data. When you need to show &ldquo;all todos in the Work category,&rdquo; you&rsquo;re forced to write messy, manual <code>.filter()</code> and <code>.find()</code> logic and cross-reference data inside your components.</p><p>This is the exact problem <a target="_blank" href="https://tanstack.com/db/latest">TanStack DB</a> solves. It&rsquo;s not a replacement for TanStack Query; it&rsquo;s a new layer that enhances it. TanStack Query still handles all server communication (fetching, caching, revalidation), but it feeds that data into a high-performance, local, queryable database with proper relationships.</p><p>Instead of filtering arrays manually, your components query the local database using a structured query. The result is cleaner components, better performance and automatic reactivity when data changes.</p><p>In this article, we&rsquo;ll build a to-do app to see it in action. We&rsquo;ll see how to set up the collection, what &ldquo;live queries&rdquo; are, and why &ldquo;differential dataflow&rdquo; makes it so fast. We&rsquo;ll also cover when you should actually reach for this and when you&rsquo;re better off just sticking with plain TanStack Query.</p><h2 id="evolution-of-data-fetching-in-react">Evolution of Data Fetching in React</h2><p>To understand why TanStack DB is a big deal, it helps to look at different ways developers have tried to handle data fetching in React.</p><h3 id="the-useeffecthook">The useEffectHook</h3><p>If you&rsquo;ve been building React for a while, you&rsquo;ve seen <code>useEffect</code> used for data fetching everywhere. A typical pattern looks like this:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">function</span> <span class="token function">TodoList</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> setTodos<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>loading<span class="token punctuation">,</span> setLoading<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>error<span class="token punctuation">,</span> setError<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</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 function">useEffect</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">'/api/todos'</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>res <span class="token operator">=&gt;</span> res<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>data <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token function">setTodos</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token keyword">catch</span><span class="token punctuation">(</span>err <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token function">setError</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token function">setLoading</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">if</span> <span class="token punctuation">(</span>loading<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span>Loading<span class="token operator">...</span><span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span>Error<span class="token operator">!</span><span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span><span class="token punctuation">{</span><span class="token comment">/* render todos */</span><span class="token punctuation">}</span><span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>This pattern was everywhere, and it worked, but it had serious problems. Every component mount triggered a data fetch since there was no caching. You had to manually track loading and error states every single time. Once you fetched the data, it just sat there getting stale with no background updates, and if two components needed the same data, they&rsquo;d fetch it separately.</p><h2 id="tanstack-query-react-query">Tanstack Query (React Query)</h2><p>TanStack Query solved these problems by abstracting away the complexity of data fetching.</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">function</span> <span class="token function">TodoList</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> data<span class="token punctuation">:</span> todos<span class="token punctuation">,</span> isLoading<span class="token punctuation">,</span> error <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">'todos'</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
    queryFn<span class="token punctuation">:</span> fetchTodos
  <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>isLoading<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span>Loading<span class="token operator">...</span><span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span><span class="token punctuation">;</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span>Error<span class="token operator">!</span><span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span><span class="token punctuation">{</span><span class="token comment">/* render todos */</span><span class="token punctuation">}</span><span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>The code snippet above is way cleaner, right? TanStack Query came with powerful features like automatic caching that lets you fetch once and use everywhere, background refetching that keeps data fresh automatically, built-in loading and error states, request deduplication so multiple components only fetch once, and stale-while-revalidate patterns. TanStack Query basically solved the &ldquo;fetching from APIs&rdquo; problem.</p><p>Despite all these perks, there&rsquo;s still a problem. The thing with TanStack Query is that it&rsquo;s great at getting data, but once that data arrives, it treats everything as separate entities&mdash;no relationships.</p><p>Imagine we&rsquo;re building a to-do app with categories. We fetch todos, we fetch categories. Both queries work perfectly. But what if we want to show &ldquo;all uncompleted Work todos,&rdquo; how do we do it?</p><p>We&rsquo;d end up writing something like this:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> <span class="token punctuation">{</span> data<span class="token punctuation">:</span> todos <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><span class="token string">'todos'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> fetchTodos<span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token keyword">const</span> <span class="token punctuation">{</span> data<span class="token punctuation">:</span> categories <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><span class="token string">'categories'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> fetchCategories<span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token keyword">const</span> workCategory <span class="token operator">=</span> categories<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span>c <span class="token operator">=&gt;</span> c<span class="token punctuation">.</span>name <span class="token operator">===</span> <span class="token string">'Work'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token keyword">const</span> uncompletedWorkTodos <span class="token operator">=</span> todos <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>t <span class="token operator">=&gt;</span> t<span class="token punctuation">.</span>categoryId <span class="token operator">===</span> workCategory<span class="token operator">?</span><span class="token punctuation">.</span>id<span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>t <span class="token operator">=&gt;</span> <span class="token operator">!</span>t<span class="token punctuation">.</span>completed<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>This works. But notice what&rsquo;s happening? We&rsquo;re manually filtering and joining data, and code like this runs on every render. With 10 todos? It feels seamless. But scale that to a thousand todos, and we&rsquo;ll start feeling the lag, especially if we&rsquo;re not careful with memoization. Our app gets sluggish.</p><p>The issue here is that the todos cache and the categories cache don&rsquo;t know about each other. They&rsquo;re completely isolated. If we want to connect them, we&rsquo;d have to do that manually every single time.</p><h2 id="project-setup">Project Setup</h2><p>We&rsquo;re going to set up a simple to-do app from scratch. The goal is to build two lists, &ldquo;Pending&rdquo; and &ldquo;Completed,&rdquo; and watch items move between them instantly. No manual state management, no <code>useMemo</code> hooks for filtering, just a reactive, local-first database.</p><p>We&rsquo;ll use <a target="_blank" href="https://vite.dev/">Vite</a>. It&rsquo;s the fastest way to get a React + <a target="_blank" href="https://www.typescriptlang.org/">TypeScript</a> project running.</p><p>Open your terminal and run the following command:</p><pre class=" language-shell"><code class="prism  language-shell">npm create vite@latest tanstack-db-demo -- --template react-ts
</code></pre><p>Once that&rsquo;s finished, move into the new directory and install the default packages:</p><pre class=" language-shell"><code class="prism  language-shell">cd tanstack-db-demo
npm install
</code></pre><p>With the basics installed, let&rsquo;s add our two core libraries. We&rsquo;ll install <code>react-query</code> (the &ldquo;delivery service&rdquo;) and <code>react-db</code> (our new &ldquo;smart warehouse&rdquo;).</p><p>Next, we&rsquo;ll install TanStack Query to handle server syncing and TanStack DB to store that data in a queryable local database.</p><pre class=" language-shell"><code class="prism  language-shell">npm install @tanstack/react-query @tanstack/db
</code></pre><h3 id="setting-up-the-file-structure">Setting up the File Structure</h3><p>Let&rsquo;s clean up the <code>src</code> folder. You can delete the <code>App.css</code> file and the <code>assets</code> folder that Vite gives you. We&rsquo;ll replace them with our own <code>index.css</code> and two new files: <code>types.ts</code> and <code>db.ts</code>.</p><p>When you&rsquo;re done, your <code>src</code> folder should look like this:</p><pre class=" language-js"><code class="prism  language-js">src<span class="token operator">/</span>
── components<span class="token operator">/</span>   <span class="token punctuation">(</span>We'll add <span class="token keyword">this</span> later<span class="token punctuation">)</span>
── App<span class="token punctuation">.</span><span class="token function">tsx</span>       <span class="token punctuation">(</span>Already there<span class="token punctuation">,</span> we'll edit it<span class="token punctuation">)</span>
── db<span class="token punctuation">.</span><span class="token function">ts</span>         <span class="token punctuation">(</span>New <span class="token class-name">file</span><span class="token punctuation">)</span>
── index<span class="token punctuation">.</span><span class="token function">css</span>     <span class="token punctuation">(</span>Already there<span class="token punctuation">,</span> we'll edit it<span class="token punctuation">)</span>
── main<span class="token punctuation">.</span><span class="token function">tsx</span>      <span class="token punctuation">(</span>Already there<span class="token punctuation">,</span> we'll edit it<span class="token punctuation">)</span>
── types<span class="token punctuation">.</span><span class="token function">ts</span>      <span class="token punctuation">(</span>New <span class="token class-name">file</span><span class="token punctuation">)</span>
</code></pre><h2 id="defining-the-local-collection">Defining the Local Collection</h2><p>With our project set up, let&rsquo;s get to the core of our app&rsquo;s data logic. We&rsquo;ll do this in a new file, <code>src/db.ts</code>. It&rsquo;s important to understand that TanStack DB is built to work with TanStack Query, not replace it.</p><p>This <code>db.ts</code> file will set up both the <strong>QueryClient</strong>, which is the standard engine from TanStack Query, and the main cache that all our data will flow through, and the <strong>Collection</strong>, which is the new part from TanStack DB&mdash;our actual, reactive table that will hold the todos.</p><p>We&rsquo;ll start with <code>localOnlyCollectionOptions</code> to build a simple, in-memory collection first. This lets us build our UI without worrying about a server yet. Go ahead and add the following to your <code>src/db.ts</code> file:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> QueryClient <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@tanstack/react-query"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>
  createCollection<span class="token punctuation">,</span>
  localOnlyCollectionOptions<span class="token punctuation">,</span>
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@tanstack/react-db"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">{</span> Todo <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./types"</span><span class="token punctuation">;</span> 
<span class="token keyword">export</span> <span class="token keyword">const</span> queryClient <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">QueryClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> initialTodos<span class="token punctuation">:</span> Todo<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> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> text<span class="token punctuation">:</span> <span class="token string">"Learn TanStack DB"</span><span class="token punctuation">,</span> completed<span class="token punctuation">:</span> <span class="token keyword">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> text<span class="token punctuation">:</span> <span class="token string">"Write the blog post"</span><span class="token punctuation">,</span> completed<span class="token punctuation">:</span> <span class="token keyword">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span> text<span class="token punctuation">:</span> <span class="token string">'Show "live queries"'</span><span class="token punctuation">,</span> completed<span class="token punctuation">:</span> <span class="token keyword">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> todoCollection <span class="token operator">=</span> createCollection<span class="token operator">&lt;</span>Todo<span class="token punctuation">,</span> <span class="token keyword">number</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>
  <span class="token function">localOnlyCollectionOptions</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    id<span class="token punctuation">:</span> <span class="token string">"todos"</span><span class="token punctuation">,</span>
    getKey<span class="token punctuation">:</span> <span class="token punctuation">(</span>todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> todo<span class="token punctuation">.</span>id<span class="token punctuation">,</span>
    initialData<span class="token punctuation">:</span> initialTodos<span class="token punctuation">,</span> <span class="token comment">// Load the dummy data on start</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The code snippet above is pretty straightforward. Let&rsquo;s break it down and be clear on what each part does:</p><ul><li><strong>queryClient</strong>: This is just the standard QueryClient from <code>@tanstack/react-query</code>. We create one and export it because our whole app needs to share a single cache.</li><li><strong>initialTodos</strong>: This is just some dummy data, so our app doesn&rsquo;t start empty.</li><li><strong>todoCollection</strong>: This is the core of our setup. We&rsquo;re defining our &ldquo;table.&rdquo;</li><li><strong>localOnlyCollectionOptions</strong>: This is our starting point. It&rsquo;s a helper that tells TanStack DB to be a local-only database. It won&rsquo;t try to fetch anything or talk to a server. It&rsquo;s the perfect way to build out our UI before we&rsquo;ve written an API.</li><li><strong>getKey</strong>: This is important. It&rsquo;s the primary key for our &ldquo;table,&rdquo; telling TanStack DB how to find any specific to-do. So we&rsquo;ll just use <code>todo.id</code>.</li></ul><p>Now, we have a fully typed, in-memory database populated with initial data, all in one file. We just need to tell our React app to actually use this setup. We&rsquo;ll do that in the next step by wiring up our queryClient in <code>main.tsx</code> file.</p><h2 id="configuring-the-app">Configuring the App</h2><p>We have our <code>queryClient</code> and <code>todoCollection</code> defined in the <code>src/db.ts</code> file . But the thing is, our React app doesn&rsquo;t automatically know they exist. They&rsquo;re just isolated pieces of code right now.</p><p>To make everything work together, we need to connect that <code>queryClient</code> to our component tree. This isn&rsquo;t a TanStack DB-specific thing; it&rsquo;s how TanStack Query works, and we&rsquo;ll need a <code>QueryClientProvider</code> at the root. This provider uses React Context under the hood to pass the client instance down, making sure all your hooks (useQuery, useMutation and eventually useLiveQuery) are all talking to the same central cache.</p><p>It&rsquo;s a straightforward setup in our main entry file, <code>src/main.tsx</code>. We just need to do two things:</p><ul><li>Import the <code>queryClient</code> that we already exported from <code>./db</code></li><li>Wrap our main <code>&lt;App /&gt;</code> component with <code>&lt;QueryClientProvider&gt;</code>, passing that imported <code>queryClient</code> to its client prop</li></ul><p>Here&rsquo;s what that looks like in the code:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> StrictMode <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"react"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> createRoot <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"react-dom/client"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> QueryClientProvider <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@tanstack/react-query"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token string">"./index.css"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> App <span class="token keyword">from</span> <span class="token string">"./App.tsx"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> queryClient <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./db"</span><span class="token punctuation">;</span>
<span class="token function">createRoot</span><span class="token punctuation">(</span>document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"root"</span><span class="token punctuation">)</span><span class="token operator">!</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">render</span><span class="token punctuation">(</span>
  <span class="token operator">&lt;</span>StrictMode<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span>QueryClientProvider client<span class="token operator">=</span><span class="token punctuation">{</span>queryClient<span class="token punctuation">}</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>App <span class="token operator">/</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>QueryClientProvider<span class="token operator">&gt;</span>
  <span class="token operator">&lt;</span><span class="token operator">/</span>StrictMode<span class="token operator">&gt;</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>With that provider set up, our app is ready. The <code>queryClient</code> is available everywhere, which means TanStack DB can now hook into it. All the groundwork is done.</p><p>Next, let&rsquo;s build the UI in <code>App.tsx</code> file and see how <code>useLiveQuery</code> pulls our data into the components.</p><h2 id="building-the-ui-with-live-queries">Building the UI with Live Queries</h2><p>We have our setup at this point. Our <code>queryClient</code> is provided, and our <code>todoCollection</code> is ready, so the next thing is to pull our data into the UI.</p><p>This is where <code>useLiveQuery</code> comes in. It&rsquo;s a hook TanStack DB gives us to read from our database, and it&rsquo;s the core of what makes this whole setup &ldquo;reactive.&rdquo; In this context, &ldquo;reactive&rdquo; means our UI will update immediately and automatically whenever our data changes, without us having to force a re-render manually.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/ui-update-immediately.png?sfvrsn=1ddd99e6_2" title="UI update immediately data changes, without  forcing a re-render manually" alt="UI update immediately data changes, without  forcing a re-render manually" /></p><p>If we were doing this the old way, we would fetch data, dump it into our <code>useState</code> hook, and then if we added a new todo, we would have to call <code>setTodos([...todos, newTodo])</code> just to see the change.</p><p>But with <code>useLiveQuery</code>, we don&rsquo;t need to do that. Our component subscribes to the database, and when we call <code>todoCollection.insert()</code>, the <code>useLiveQuery</code> sees the change, retrieves the new data, and our component re-renders.</p><p>Let&rsquo;s look at <code>useLiveQuery</code> as subscribing to your database. What happens behind the scenes is this: when your component mounts, useLiveQuery subscribes to the database. It runs your query and gives you the current data, then keeps watching the database for any changes. When data changes (insert, update, delete), it automatically reruns your query. Your component receives the fresh data and re-renders, without you having to manage any state manually.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/components-update-automatically.png?sfvrsn=37043fd8_2" title="When database changes both components update automatically" alt="When database changes both components update automatically" /></p><p>Let&rsquo;s build our UI. We&rsquo;ll start by creating two simple components: <code>TodoForm</code> and <code>TodoList</code>.</p><h2 id="creating-components">Creating Components</h2><p>Run the command below in your terminal to create a new <code>src/components</code> folder:</p><pre class=" language-shell"><code class="prism  language-shell">mkdir src/components
</code></pre><p>We&rsquo;ll put our new components here. These are presentational components&mdash;they just take props and render UI. They have no knowledge of TanStack DB.</p><p>Create a <code>src/components/TodoForm.tsx</code> file and add the following to it:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token keyword">import</span> <span class="token punctuation">{</span> useState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"react"</span><span class="token punctuation">;</span>
type TodoFormProps <span class="token operator">=</span> <span class="token punctuation">{</span>
  onAdd<span class="token punctuation">:</span> <span class="token punctuation">(</span>text<span class="token punctuation">:</span> string<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">TodoForm</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> onAdd <span class="token punctuation">}</span><span class="token punctuation">:</span> TodoFormProps<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>newTodoText<span class="token punctuation">,</span> setNewTodoText<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token function-variable function">handleSubmit</span> <span class="token operator">=</span> <span class="token punctuation">(</span>e<span class="token punctuation">:</span> React<span class="token punctuation">.</span>FormEvent<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    e<span class="token punctuation">.</span><span class="token function">preventDefault</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>newTodoText<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>
    <span class="token function">onAdd</span><span class="token punctuation">(</span>newTodoText<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">setNewTodoText</span><span class="token punctuation">(</span><span class="token string">""</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>form</span> <span class="token attr-name">onSubmit</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>handleSubmit<span class="token punctuation">}</span></span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>todo-form<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>input</span>
        <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span>
        <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>newTodoText<span class="token punctuation">}</span></span>
        <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setNewTodoText</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span></span>
        <span class="token attr-name">placeholder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>What needs to be done?<span class="token punctuation">"</span></span>
        <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>todo-input<span class="token punctuation">"</span></span>
      <span class="token punctuation">/&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>submit<span class="token punctuation">"</span></span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>todo-add-button<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        Add
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>form</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>This is a controlled component that manages the input field&rsquo;s value in its own state. When submitted, it passes the text up to the parent component via the <code>onAdd</code> prop.</p><p>Create a <code>src/components/TodoList.tsx</code> file and add the following to it:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token keyword">import</span> type <span class="token punctuation">{</span> Todo <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"../types"</span><span class="token punctuation">;</span>
type TodoListProps <span class="token operator">=</span> <span class="token punctuation">{</span>
title<span class="token punctuation">:</span> string<span class="token punctuation">;</span>
todos<span class="token punctuation">:</span> Todo<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
onToggle<span class="token punctuation">:</span> <span class="token punctuation">(</span>todo<span class="token punctuation">:</span> Todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
onDelete<span class="token punctuation">:</span> <span class="token punctuation">(</span>todo<span class="token punctuation">:</span> Todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> <span class="token function-variable function">TodoList</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>
title<span class="token punctuation">,</span>
todos<span class="token punctuation">,</span>
onToggle<span class="token punctuation">,</span>
onDelete<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">:</span> TodoListProps<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>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>todo-list-container<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>h2</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>todo-list-title<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token punctuation">{</span>title<span class="token punctuation">}</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>todos<span class="token punctuation">.</span>length<span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h2</span><span class="token punctuation">&gt;</span></span>
    <span class="token punctuation">{</span>todos<span class="token punctuation">.</span>length <span class="token operator">===</span> <span class="token number">0</span> <span class="token operator">?</span> <span class="token punctuation">(</span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>todo-item-placeholder<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Nothing here<span class="token operator">!</span><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 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>ul</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>todo-list<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token punctuation">{</span>todos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
          <span class="token keyword">const</span> checkboxId <span class="token operator">=</span> <span class="token template-string"><span class="token string">`todo-checkbox-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>todo<span class="token punctuation">.</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">;</span>
          <span class="token keyword">return</span> <span class="token punctuation">(</span>
            <span class="token operator">&lt;</span>li
              key<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<span class="token punctuation">}</span>
              className<span class="token operator">=</span><span class="token punctuation">{</span><span class="token template-string"><span class="token string">`todo-item </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>todo<span class="token punctuation">.</span>completed <span class="token operator">?</span> <span class="token string">"completed"</span> <span class="token punctuation">:</span> <span class="token string">""</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">}</span>
            <span class="token operator">&gt;</span>
              <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span>
                <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>checkbox<span class="token punctuation">"</span></span>
                <span class="token attr-name">id</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>checkboxId<span class="token punctuation">}</span></span>
                <span class="token attr-name">checked</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>completed<span class="token punctuation">}</span></span>
                <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token punctuation">=</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">onToggle</span><span class="token punctuation">(</span>todo<span class="token punctuation">)</span><span class="token punctuation">}</span></span>
                <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>todo-item-checkbox<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">htmlFor</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>checkboxId<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> color<span class="token punctuation">:</span> <span class="token string">"black"</span><span class="token punctuation">,</span> display<span class="token punctuation">:</span> <span class="token string">"inline"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span></span>
              <span class="token punctuation">&gt;</span></span>
                <span class="token punctuation">{</span>todo<span class="token punctuation">.</span>text <span class="token operator">||</span> <span class="token string">"NO TEXT"</span><span class="token punctuation">}</span>
              <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>button</span>
                <span class="token attr-name">onClick</span><span class="token script language-javascript"><span class="token punctuation">=</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">onDelete</span><span class="token punctuation">(</span>todo<span class="token punctuation">)</span><span class="token punctuation">}</span></span>
                <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>todo-delete-button<span class="token punctuation">"</span></span>
              <span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>svg</span>
                  <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span>
                  <span class="token attr-name">fill</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>none<span class="token punctuation">"</span></span>
                  <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>0 0 24 24<span class="token punctuation">"</span></span>
                  <span class="token attr-name">stroke</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>currentColor<span class="token punctuation">"</span></span>
                <span class="token punctuation">&gt;</span></span>
                  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>path</span>
                    <span class="token attr-name">strokeLinecap</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>round<span class="token punctuation">"</span></span>
                    <span class="token attr-name">strokeLinejoin</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>round<span class="token punctuation">"</span></span>
                    <span class="token attr-name">strokeWidth</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token number">2</span><span class="token punctuation">}</span></span>
                    <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16<span class="token punctuation">"</span></span>
                  <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>svg</span><span class="token punctuation">&gt;</span></span>
              <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>li</span><span class="token punctuation">&gt;</span></span>
          <span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</span><span class="token punctuation">&gt;</span></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 punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre><p>This component is only responsible for displaying the list of todos. It receives the todos array as a prop, shows a &ldquo;Nothing here!&rdquo; message if the list is empty, or maps over the array to render each item.</p><h2 id="updating-app.tsx-file">Updating App.tsx File</h2><p>Now let&rsquo;s open <code>src/App.tsx</code> and wire everything together. First, we need to import our new components <code>(TodoForm and TodoList)</code>. Then we&rsquo;ll call <code>useLiveQuery</code> twice&mdash;one query to get all todos where <code>completed</code> is false, and a second query to get all todos where <code>completed</code> is true.</p><p>Next, we&rsquo;ll create our event handlers (<code>handleAddTodo</code>, <code>handleToggleTodo</code>, <code>handleDeleteTodo</code>), which is where we write to the database. Finally, we&rsquo;ll render the components and pass the data and handlers as props.</p><p>Here&rsquo;s what our <code>src/App.tsx</code> file should look like:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token keyword">import</span> <span class="token punctuation">{</span> useMemo <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"react"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> useLiveQuery<span class="token punctuation">,</span> eq <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@tanstack/react-db"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> todoCollection <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./db"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> type <span class="token punctuation">{</span> Todo <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./types"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> TodoForm <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./components/TodoForm"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> TodoList <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./components/TodoList"</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">TodoApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> data<span class="token punctuation">:</span> pendingTodos <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 function">useLiveQuery</span><span class="token punctuation">(</span><span class="token punctuation">(</span>q<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
    q
      <span class="token punctuation">.</span><span class="token keyword">from</span><span class="token punctuation">(</span><span class="token punctuation">{</span> todo<span class="token punctuation">:</span> todoCollection <span class="token punctuation">}</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token function">where</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">{</span> todo <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">eq</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>completed<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">{</span> data<span class="token punctuation">:</span> completedTodos <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 function">useLiveQuery</span><span class="token punctuation">(</span><span class="token punctuation">(</span>q<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
    q
      <span class="token punctuation">.</span><span class="token keyword">from</span><span class="token punctuation">(</span><span class="token punctuation">{</span> todo<span class="token punctuation">:</span> todoCollection <span class="token punctuation">}</span><span class="token punctuation">)</span>
      <span class="token punctuation">.</span><span class="token function">where</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">{</span> todo <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">eq</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>completed<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> sortedPending <span class="token operator">=</span> <span class="token function">useMemo</span><span class="token punctuation">(</span>
    <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token operator">...</span>pendingTodos<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">reverse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">[</span>pendingTodos<span class="token punctuation">]</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> sortedCompleted <span class="token operator">=</span> <span class="token function">useMemo</span><span class="token punctuation">(</span>
    <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">[</span><span class="token operator">...</span>completedTodos<span class="token punctuation">]</span><span class="token punctuation">.</span><span class="token function">reverse</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">[</span>completedTodos<span class="token punctuation">]</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token function-variable function">handleAddTodo</span> <span class="token operator">=</span> <span class="token punctuation">(</span>text<span class="token punctuation">:</span> string<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> newTodo<span class="token punctuation">:</span> Todo <span class="token operator">=</span> <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
      text<span class="token punctuation">,</span>
      completed<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">;</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"APP: Inserting new todo"</span><span class="token punctuation">,</span> newTodo<span class="token punctuation">)</span><span class="token punctuation">;</span>
    todoCollection<span class="token punctuation">.</span><span class="token function">insert</span><span class="token punctuation">(</span>newTodo<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token function-variable function">handleToggleTodo</span> <span class="token operator">=</span> <span class="token punctuation">(</span>todo<span class="token punctuation">:</span> Todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"APP: Toggling todo"</span><span class="token punctuation">,</span> todo<span class="token punctuation">)</span><span class="token punctuation">;</span>
    todoCollection<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<span class="token punctuation">,</span> <span class="token punctuation">(</span>draft<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      draft<span class="token punctuation">.</span>completed <span class="token operator">=</span> <span class="token operator">!</span>draft<span class="token punctuation">.</span>completed<span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token function-variable function">handleDeleteTodo</span> <span class="token operator">=</span> <span class="token punctuation">(</span>todo<span class="token punctuation">:</span> Todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"APP: Deleting todo"</span><span class="token punctuation">,</span> todo<span class="token punctuation">)</span><span class="token punctuation">;</span>
    todoCollection<span class="token punctuation">.</span><span class="token keyword">delete</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>app-container<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h1</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>app-title<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>TanStack DB To<span class="token operator">-</span>Do<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>TodoForm</span> <span class="token attr-name">onAdd</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>handleAddTodo<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>TodoList</span>
        <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Pending<span class="token punctuation">"</span></span>
        <span class="token attr-name">todos</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>sortedPending<span class="token punctuation">}</span></span>
        <span class="token attr-name">onToggle</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>handleToggleTodo<span class="token punctuation">}</span></span>
        <span class="token attr-name">onDelete</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>handleDeleteTodo<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>TodoList</span>
        <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Completed<span class="token punctuation">"</span></span>
        <span class="token attr-name">todos</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>sortedCompleted<span class="token punctuation">}</span></span>
        <span class="token attr-name">onToggle</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>handleToggleTodo<span class="token punctuation">}</span></span>
        <span class="token attr-name">onDelete</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>handleDeleteTodo<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 punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> TodoApp<span class="token punctuation">;</span>
</code></pre><p>Notice the <code>useLiveQuery</code> hooks and what they look like when you&rsquo;re querying a SQL database. If you run <code>npm run dev</code> at this point, you&rsquo;ll have a local-only to-do app.</p><p>The key takeaway here is our <code>App</code> component doesn&rsquo;t manage any state. There are no <code>useState</code> hooks for the to-do lists. It just asks TanStack DB for two different lists (<code>Pending</code> and <code>Completed</code>).</p><p>When you add a new to-do, <code>handleAddTodo</code> calls <code>todoCollection.insert()</code>. The data is added to the local database, and TanStack DB automatically reruns any query affected by this change. The UI updates instantly because we&rsquo;re just writing to a local database. This is a concept called an &ldquo;optimistic update,&rdquo; and we get it for free.</p><p>Right now, this is all happening in the browser. In the next step, we&rsquo;ll connect this to a server.</p><h2 id="syncing-to-a-server">Syncing to a Server</h2><p>Before we update our <code>db.ts</code> file, we need an API to talk to. We don&rsquo;t have a real backend, so we&rsquo;re going to simulate a fake one. This is the best way to see optimistic updates in action because we can add an artificial delay to simulate real network lag.</p><p>Let&rsquo;s create a new file, <code>src/api.ts</code>, that uses localStorage to save todos so they actually persist when we refresh. And we&rsquo;ll add a one-second setTimeout to every request to mimic what it feels like when your app is talking to a real server.</p><p>Add the following to your <code>src/api.ts</code> file:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">{</span> Todo <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./types"</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> TODOS_STORAGE_KEY <span class="token operator">=</span> <span class="token string">"todos"</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token function-variable function">wait</span> <span class="token operator">=</span> <span class="token punctuation">(</span>ms<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span>resolve<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setTimeout</span><span class="token punctuation">(</span>resolve<span class="token punctuation">,</span> ms<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> loadTodos <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">:</span> Todo<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> data <span class="token operator">=</span> localStorage<span class="token punctuation">.</span><span class="token function">getItem</span><span class="token punctuation">(</span>TODOS_STORAGE_KEY<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> data <span class="token operator">?</span> <span class="token punctuation">(</span>JSON<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span> <span class="token keyword">as</span> Todo<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token function-variable function">saveTodos</span> <span class="token operator">=</span> <span class="token punctuation">(</span>todos<span class="token punctuation">:</span> Todo<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>
  localStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span>TODOS_STORAGE_KEY<span class="token punctuation">,</span> JSON<span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>todos<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">export</span> <span class="token keyword">const</span> api <span class="token operator">=</span> <span class="token punctuation">{</span>
  todos<span class="token punctuation">:</span> <span class="token punctuation">{</span>
    getAll<span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">await</span> <span class="token function">wait</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Simulate network delay</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"SERVER: (GET) Fetched all todos"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">return</span> <span class="token function">loadTodos</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>
    create<span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>newTodo<span class="token punctuation">:</span> Todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">await</span> <span class="token function">wait</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">const</span> todos <span class="token operator">=</span> <span class="token function">loadTodos</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">const</span> updatedTodos <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>todos<span class="token punctuation">,</span> newTodo<span class="token punctuation">]</span><span class="token punctuation">;</span>
      <span class="token function">saveTodos</span><span class="token punctuation">(</span>updatedTodos<span class="token punctuation">)</span><span class="token punctuation">;</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"SERVER: (POST) created new todo"</span><span class="token punctuation">,</span> newTodo<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">return</span> newTodo<span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    update<span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>updatedTodo<span class="token punctuation">:</span> Todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">await</span> <span class="token function">wait</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">const</span> todos <span class="token operator">=</span> <span class="token function">loadTodos</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">const</span> updatedTodos <span class="token operator">=</span> todos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
        todo<span class="token punctuation">.</span>id <span class="token operator">===</span> updatedTodo<span class="token punctuation">.</span>id <span class="token operator">?</span> updatedTodo <span class="token punctuation">:</span> todo
      <span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token function">saveTodos</span><span class="token punctuation">(</span>updatedTodos<span class="token punctuation">)</span><span class="token punctuation">;</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"SERVER: (PUT) updated todo"</span><span class="token punctuation">,</span> updatedTodo<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">return</span> updatedTodo<span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token keyword">delete</span><span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span>id<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">await</span> <span class="token function">wait</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">const</span> todos <span class="token operator">=</span> <span class="token function">loadTodos</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">const</span> updatedTodos <span class="token operator">=</span> todos<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span>todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> todo<span class="token punctuation">.</span>id <span class="token operator">!==</span> id<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token function">saveTodos</span><span class="token punctuation">(</span>updatedTodos<span class="token punctuation">)</span><span class="token punctuation">;</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"SERVER: (DELETE) deleted todo"</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 punctuation">{</span> id <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>Now that we have our <code>src/api.ts</code> ready, the next step is to update our <code>db.ts</code> to tell it to use our API. This is where we&rsquo;ll swap the <code>localOnlyCollectionOptions</code> for <code>queryCollectionOptions</code>, just as we discussed earlier.</p><p>At this point, we need to do three things: install the adapter package that connects TanStack Query and TanStack DB, import the <code>queryCollectionOptions</code> from this new package, and replace <code>localOnlyCollectionOptions</code> with <code>queryCollectionOptions</code>.</p><p>Run this install command in your terminal:</p><pre class=" language-shell"><code class="prism  language-shell">npm install @tanstack/query-db-collection
</code></pre><blockquote><p>This package gives us the bridge between TanStack Query (server state) and TanStack DB (local state).</p></blockquote><p>Now replace the entire content of your <code>src/db.ts</code> file with the following:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> QueryClient <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@tanstack/react-query"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> createCollection <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@tanstack/react-db"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> queryCollectionOptions <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@tanstack/query-db-collection"</span><span class="token punctuation">;</span> <span class="token comment">//new import</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> api <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./api"</span><span class="token punctuation">;</span> <span class="token comment">// import simulated API</span>
<span class="token keyword">import</span> <span class="token keyword">type</span> <span class="token punctuation">{</span> Todo <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./types"</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> queryClient <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">QueryClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">//we no longer need the initialTodos. The API will provide that to us</span>
<span class="token comment">// const initialTodos: Todo[] = [</span>
<span class="token comment">//   { id: 1, text: "Learn TanStack DB", completed: true },</span>
<span class="token comment">//   { id: 2, text: "Write the blog post", completed: false },</span>
<span class="token comment">//   { id: 3, text: 'Show "live queries"', completed: false },</span>
<span class="token comment">// ];</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> todoCollection <span class="token operator">=</span> createCollection<span class="token operator">&lt;</span>Todo<span class="token punctuation">,</span> <span class="token keyword">number</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>
  <span class="token function">queryCollectionOptions</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    queryClient<span class="token punctuation">,</span>
    queryKey<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token string">"todos"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
    queryFn<span class="token punctuation">:</span> api<span class="token punctuation">.</span>todos<span class="token punctuation">.</span>getAll<span class="token punctuation">,</span>
    getKey<span class="token punctuation">:</span> <span class="token punctuation">(</span>todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> todo<span class="token punctuation">.</span>id<span class="token punctuation">,</span>
    onInsert<span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> transaction <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"DB: Inserting..."</span><span class="token punctuation">,</span> transaction<span class="token punctuation">.</span>mutations<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>modified<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">await</span> api<span class="token punctuation">.</span>todos<span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>transaction<span class="token punctuation">.</span>mutations<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>modified <span class="token keyword">as</span> Todo<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    onUpdate<span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> transaction <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"DB: Updating..."</span><span class="token punctuation">,</span> transaction<span class="token punctuation">.</span>mutations<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>modified<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">await</span> api<span class="token punctuation">.</span>todos<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>transaction<span class="token punctuation">.</span>mutations<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>modified <span class="token keyword">as</span> Todo<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    onDelete<span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> transaction <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"DB: Deleting..."</span><span class="token punctuation">,</span> transaction<span class="token punctuation">.</span>mutations<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>original<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token keyword">await</span> api<span class="token punctuation">.</span>todos<span class="token punctuation">.</span><span class="token keyword">delete</span><span class="token punctuation">(</span>transaction<span class="token punctuation">.</span>mutations<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">.</span>original<span class="token punctuation">.</span>id<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>Start your app and try adding a new todo or toggling an existing one. The UI updates instantly, right? Now open your browser console and watch the sequence:</p><ul><li><strong>Immediately</strong>: <code>DB: Inserting&hellip;</code> &ndash; our local database write</li><li><strong>One second later</strong>: <code>SERVER: Created new todo...</code> &ndash; server sync</li></ul><p>This is the optimistic update in action, and it&rsquo;s an important concept. The simple idea is we update the UI immediately, before we&rsquo;ve even confirmed the change with the server. Our app is being optimistic that the server request will succeed.</p><p>The user gets an app that feels fast and instant. All the work of saving data happens in the background. The user never sees a loading spinner. They never wait. They just see their change appear immediately.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/optimistic-data-flow.png?sfvrsn=bc957275_2" title="Optimistic data flow" alt="Optimistic data flow" /></p><p>Let&rsquo;s break the above diagram down. It starts with the user clicking a button in the app. The UI sends a write command to the database: <code>todoCollection.insert()</code>. That&rsquo;s all, just write to the local database.</p><p>Now here is where the fun happens. The moment that the local database receives the new data, it does two things at the same time:</p><ul><li>Immediate optimistic update &ndash; The <code>useLiveQuery()</code> hook in our app is always listening. It gets the new data instantly. The app re-renders, and the user sees their new todo appear.</li><li>Background server sync &ndash; At the same time, the local database triggers the server sync. It calls the <code>onInsert</code> function we wrote, which starts the slow one-second request to the server.</li></ul><p>The key takeaway here is that the user&rsquo;s experience is only tied to Step 1, that is, the immediate update. They get instant feedback. All the slow/heavy requests happen in the background.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-03/no-delay.gif?sfvrsn=577752fc_2" title="The UI doesn’t wait for the 1 second delay" alt="The UI doesn’t wait for the 1 second delay" /></p><h2 id="differential-data-flow">Differential Data-Flow</h2><p>We&rsquo;ve talked about live queries and optimistic updates. Now let&rsquo;s look at what makes TanStack DB fast: differential dataflow. Let&rsquo;s break down this concept to understand why TanStack DB feels so smooth.</p><p>In a React app, when data changes, you recalculate everything that depends on it. For instance, say we have 2,000 todos and you mark one as completed. What happens?</p><p>One approach is:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">const</span> pendingTodos <span class="token operator">=</span> todos<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>t <span class="token operator">=&gt;</span> <span class="token operator">!</span>t<span class="token punctuation">.</span>completed<span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token keyword">const</span> completedTodos <span class="token operator">=</span> todos<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>t <span class="token operator">=&gt;</span> t<span class="token punctuation">.</span>completed<span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token keyword">const</span> pendingCount <span class="token operator">=</span> pendingTodos<span class="token punctuation">.</span>length<span class="token punctuation">;</span> 
<span class="token keyword">const</span> completedCount <span class="token operator">=</span> completedTodos<span class="token punctuation">.</span>length<span class="token punctuation">;</span>
</code></pre><p>This is the traditional approach. One thing changes, and you had to loop through 2,000 todos twice, then count them both. With a small number of todos&mdash;let&rsquo;s say 10-20&mdash;this won&rsquo;t be a problem, but with over a thousand, the app might start to feel slow.</p><p>Let&rsquo;s look at what happens under the hood when we have 2,000 todos:</p><ul><li>Loop through 2,000 todos and filter pending todos&mdash;that&rsquo;s 2,000 operations</li><li>Loop through 2,000 todos again to filter completed todos&mdash;another 2,000 operations</li><li>Count pending&mdash;one operation</li><li>Count completed&mdash;one operation</li></ul><p>At the end of the day, we&rsquo;ll have over 4,000 operations just for this simple computation of finding completed tasks.</p><p>On the other hand, with differential dataflow, it just asks: &ldquo;What changed?&rdquo; Instead of recalculating everything, it only updates what was affected.</p><p>Let&rsquo;s look at how differential dataflow handles it:</p><ul><li>First, it detects that todo #101 has changed</li><li>Next, it figures out which queries care:
        <ul><li>Pending list (remove todo #101)</li><li>Completed list (add todo #101)</li><li>Pending count (subtract 1)</li><li>Completed count (add 1)</li></ul></li></ul><p>That&rsquo;s approximately five operations.</p><p>Notice the difference: the first approach loops through everything. Differential dataflow only cares about what changed. One todo changed, so it does the work for one todo. The rest of the data remains untouched.</p><h2 id="when-to-use-tanstack-db">When to Use TanStack DB</h2><p>TanStack DB makes the most sense when you&rsquo;re dealing with related data. If you have todos connected to categories, posts linked to authors or products with reviews, it makes working with those relationships way easier than manually joining arrays.</p><p>TanStack DB is also a great fit if you need to run complex queries on the client side or if you&rsquo;re building an app that needs real-time UI updates. If you&rsquo;re building an offline-first app where users need to work without an internet connection and sync when they&rsquo;re back online, TanStack DB is built to handle that.</p><p>On the other hand, if your data is simple, maybe you&rsquo;re just fetching a list or a single blog post, TanStack Query alone is fine. There are no relationships to handle, so TanStack DB isn&rsquo;t worth it.</p><p>And if your data rarely changes and you&rsquo;re OK with manual refetching, live queries are probably overkill. TanStack DB isn&rsquo;t for every app, but when you need it, it does a very good job.</p><h2 id="conclusion">Conclusion</h2><p>So that&rsquo;s TanStack DB. A reactive database that lives in your browser gives you relationships between your data, updates your UI instantly with live queries, and stays fast even with thousands of items&mdash;all thanks to differential dataflow.</p><p>It&rsquo;s important to note that it&rsquo;s not replacing TanStack Query. It works alongside it. TanStack Query handles your server state: fetching from APIs, caching and keeping things in sync. TanStack DB handles your client state: storing that data locally and letting you query it like a real database. Together, they give you the best of both worlds&mdash;server syncing and fast local queries.</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">Caching with TanStack Query</h4></div><div class="col-8"><p class="u-fs16 u-mb0"><a target="_blank" href="https://www.telerik.com/blogs/caching-tanstack-query">TanStack Query caching</a> creates an instantaneous feeling user experience. Combined with the KendoReact Loader component, we have a polished React app handling even complex server state smoothly.</p></div></div></aside><img src="https://feeds.telerik.com/link/23056/17288834.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:ca3a3fbd-f3ef-420c-8945-38ae0e265bde</id>
    <title type="text">React Basics: Memoization in React</title>
    <summary type="text">Don’t let re-rendering hamstring your React app: memoize! Learn when and how to memorize to improve the performance of your React app.</summary>
    <published>2026-02-02T17:34:37Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>David Adeneye Abiodun </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17268597/react-basics-memoization-react"/>
    <content type="text"><![CDATA[<p><span class="featured">Don&rsquo;t let re-rendering hamstring your React app: memoize! Learn when and how to memorize to improve the performance of your React app.</span></p><p>If you&rsquo;ve ever worked on a React app that started out snappy and slowly turned sluggish as it grew, you&rsquo;ve probably run into one of React&rsquo;s classic performance puzzles&mdash;re-renders.</p><p>You click a button, update a state and suddenly half your component tree decides to re-render itself. Now, the UI works fine, but something feels off. Things start to lag. And before long, you&rsquo;re Googling &ldquo;why is my React component re-rendering&rdquo; for the 10th time that week.</p><p>That&rsquo;s where memoization steps in. It&rsquo;s one of those optimization tools that, once you understand it, gives you control over when your components and calculations actually update, instead of letting React do it blindly.</p><p>But if you&rsquo;ve ever tried to optimize performance in a large React app, you know how easy it is to overdo it. Suddenly, your code is buried under layers of memo hooks, dependency arrays and &ldquo;why isn&rsquo;t this memoized&rdquo; comments. Performance tuning starts to feel like superstition; sometimes it helps, sometimes it hurts, and often it just adds noise.</p><p>In this deep dive, we will unpack how memoization really works in React and how the landscape is changing with React 19&rsquo;s compiler, which is rewriting the rules entirely. By the end, you&rsquo;ll not only understand memoization, but you&rsquo;ll also know when to stop worrying about it and finally how React 19 is making manual memoization almost obsolete.</p><h2 id="why-memoization-matters">Why Memoization Matters</h2><p>React&rsquo;s rendering model is simple but aggressive. Whenever a parent component re-renders, its children will usually re-render too, even if their props haven&rsquo;t changed. Most of the time, this is fine. But when your components grow complex or involve expensive computations (such as filtering, sorting or formatting data), unnecessary renders start to accumulate.</p><p>Let&rsquo;s say you have a large list of users, and every time you type in a search box, the entire list recalculates and re-renders. Even if only one prop changed, React doesn&rsquo;t know whether the results are the same, so it plays it safe and re-renders everything.</p><p>Memoization helps fix that by letting React remember what didn&rsquo;t change.</p><h2 id="what-is-memoization-exactly">What Is Memoization, Exactly?</h2><p>Memoization is just a fancy term for caching the result of a function so you don&rsquo;t recompute it unnecessarily.</p><p>Here&rsquo;s a plain JavaScript example to show what it means:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">function</span> <span class="token function">slowSquare</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Computing...'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> n <span class="token operator">*</span> n<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token function">slowSquare</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// "Computing..." &rarr; 16</span>
<span class="token function">slowSquare</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// "Computing..." &rarr; 16 again</span>
</code></pre><p>Every call recalculates. Now let&rsquo;s memoize it:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> cache <span class="token operator">=</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">memoizedSquare</span><span class="token punctuation">(</span>n<span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>cache<span class="token punctuation">[</span>n<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token keyword">return</span> cache<span class="token punctuation">[</span>n<span class="token punctuation">]</span><span class="token punctuation">;</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Computing...'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  cache<span class="token punctuation">[</span>n<span class="token punctuation">]</span> <span class="token operator">=</span> n <span class="token operator">*</span> n<span class="token punctuation">;</span>
  <span class="token keyword">return</span> cache<span class="token punctuation">[</span>n<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token function">memoizedSquare</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// "Computing..." &rarr; 16</span>
<span class="token function">memoizedSquare</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// (no log) &rarr; 16 from cache</span>
</code></pre><p>The second time, it skips the heavy work because the inputs haven&rsquo;t changed.</p><p>That&rsquo;s all memoization is. It&rsquo;s a performance optimization that remembers past results based on inputs.</p><p>React applies this same concept to rendering and state updates. React&rsquo;s job is to re-render your UI when state or props change. But sometimes, it&rsquo;s re-rendering too much. So React gives us three tools to take control of this:</p><ul><li><code>React.memo</code> &ndash; memoizes components</li><li><code>useMemo</code> &ndash; memoizes values</li><li><code>useCallback</code> &ndash; memoizes functions</li></ul><p>Each one targets a different kind of unnecessary work. Let&rsquo;s go through them one by one.</p><h2 id="react.memo-memoizing-components">React.memo: Memoizing Components</h2><p>When a parent component re-renders, all its children re-render by default, even if their props are identical. <code>React.memo</code> changes that.</p><p>It wraps a functional component and tells React:</p><blockquote><p>&ldquo;If the props are the same as last time, skip re-rendering.&rdquo;</p></blockquote><p>Here&rsquo;s an example:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> TodoCard <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">memo</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token function">TodoCard</span><span class="token punctuation">(</span><span class="token punctuation">{</span> name <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Rendering:'</span><span class="token punctuation">,</span> name<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span><span class="token punctuation">{</span>name<span class="token punctuation">}</span><span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>If you render multiple <code>TodoCard</code> components inside a parent that re-renders often, you&rsquo;ll notice only the ones with changed props actually re-render.</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">function</span> <span class="token function">TodoList</span><span class="token punctuation">(</span><span class="token punctuation">{</span> todos <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 operator">&lt;</span>div<span class="token operator">&gt;</span>
      <span class="token punctuation">{</span>todos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>todo <span class="token operator">=&gt;</span> <span class="token punctuation">(</span>
        <span class="token operator">&lt;</span>TodoCard key<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<span class="token punctuation">}</span> name<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>name<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
      <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>If the todos array reference stays the same, <code>React.memo</code> will prevent redundant renders.</p><h3 id="shallow-comparison-caveat">Shallow Comparison Caveat</h3><p><code>React.memo</code> uses a shallow comparison for props. That means if you pass a new object or array each time (even with the same contents), React will still think it changed:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TodoCard</span> <span class="token attr-name">todo</span><span class="token attr-value"><span class="token punctuation">=</span>{{</span> <span class="token attr-name"><span class="token namespace">name:</span></span> <span class="token attr-name">&ldquo;Write</span> <span class="token attr-name">React</span> <span class="token attr-name">Article&rdquo;</span> <span class="token attr-name">}}</span> <span class="token punctuation">/&gt;</span></span> // new object every render
</code></pre><p>In that case, memoization won&rsquo;t help unless you also memoize the object reference with <code>useMemo</code> or stabilize it in another way.</p><h2 id="usememo-memoizing-expensive-calculations">useMemo: Memoizing Expensive Calculations</h2><p>Sometimes, the performance hit doesn&rsquo;t come from re-renders&mdash;it comes from recalculations inside the render function. That&rsquo;s what <code>useMemo</code> is for.</p><p><code>useMemo</code> lets you <strong>cache a computed value</strong> between renders, only recomputing when its dependencies change.</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> cachedValue <span class="token operator">=</span> <span class="token function">useMemo</span><span class="token punctuation">(</span>calculateValue<span class="token punctuation">,</span> dependencies<span class="token punctuation">)</span>
</code></pre><p>To cache a calculation between re-renders, wrap it in a <code>useMemo</code> call at the top level of your component:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> useMemo <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span>
<span class="token keyword">function</span> <span class="token function">TodoApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
&hellip;
<span class="token keyword">const</span> filteredTodos <span class="token operator">=</span> <span class="token function">useMemo</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>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Filtering todos..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> todos<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>todo <span class="token operator">=&gt;</span>
      todo<span class="token punctuation">.</span>text<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>search<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> search<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token operator">...</span>
<span class="token punctuation">}</span>

</code></pre><p>When using <code>useMemo</code>, you need to pass two things:</p><ol><li>A <code>calculation function</code> takes no arguments, like <code>() =&gt;</code>, and returns what you wanted to calculate.</li><li>A <code>list of dependencies</code> including every value within your component that&rsquo;s used inside your calculation.</li></ol><p>On the initial render, React runs that calculation and stores the result.</p><p>On every subsequent render, React compares the current dependencies to the ones from the previous render (using <code>Object.is</code> for comparison).</p><ul><li>If none of the dependencies have changed, React simply returns the previously cached value&mdash;it doesn&rsquo;t rerun the calculation.</li><li>If at least one dependency has changed, React re-executes the function, updates the stored result and returns the new value.</li></ul><p>In short, <code>useMemo</code> remembers the result of a computation between renders and only recalculates it when one of its dependencies changes. It&rsquo;s React&rsquo;s way of saying, &ldquo;I&rsquo;ve already done this work. Unless something important changed, let&rsquo;s not do it again.&rdquo;</p><h3>Example: Optimizing a Filtered Todo List with useMemo</h3><p>Let&rsquo;s look at a simple example where <code>useMemo</code> actually makes a difference&mdash;a Todo list with a search filter.</p><p>Without <code>useMemo</code>, every time the component re-renders (even when unrelated state changes), your filter logic will run again. That&rsquo;s fine for small data, but as your list grows, it can start to slow things down unnecessarily.</p><p>Here&rsquo;s the straightforward version first:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">function</span> <span class="token function">TodoApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>search<span class="token punctuation">,</span> setSearch<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> setTodos<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">"Todo 1"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">"Todo 2"</span><span class="token punctuation">,</span> done<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> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">"Todo 3"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> filteredTodos <span class="token operator">=</span> todos<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>todo <span class="token operator">=&gt;</span>
    todo<span class="token punctuation">.</span>text<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>search<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>
    <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>input
        type<span class="token operator">=</span><span class="token string">"text"</span>
        placeholder<span class="token operator">=</span><span class="token string">"Search todos..."</span>
        value<span class="token operator">=</span><span class="token punctuation">{</span>search<span class="token punctuation">}</span>
        onChange<span class="token operator">=</span><span class="token punctuation">{</span>e <span class="token operator">=&gt;</span> <span class="token function">setSearch</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span>
      <span class="token operator">/</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>ul<span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>filteredTodos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>todo <span class="token operator">=&gt;</span> <span class="token punctuation">(</span>
          <span class="token operator">&lt;</span>li key<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<span class="token punctuation">}</span><span class="token operator">&gt;</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>text<span class="token punctuation">}</span><span class="token operator">&lt;</span><span class="token operator">/</span>li<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">&lt;</span><span class="token operator">/</span>ul<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>This works, but notice what happens when you type in the search box or add a new todo: React reruns the entire component, including the <code>todos.filter()</code> call.</p><p>Now imagine you have hundreds or thousands of todos, or that the filter operation involves heavier logic. You don&rsquo;t want to run that unnecessarily on every render.</p><p>Here&rsquo;s where <code>useMemo</code> helps:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">function</span> <span class="token function">TodoApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>search<span class="token punctuation">,</span> setSearch<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> setTodos<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
    <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> text<span class="token punctuation">:</span> <span class="token string">"Buy groceries"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> text<span class="token punctuation">:</span> <span class="token string">"Read a book"</span><span class="token punctuation">,</span> done<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> id<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span> text<span class="token punctuation">:</span> <span class="token string">"Go for a walk"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> filteredTodos <span class="token operator">=</span> <span class="token function">useMemo</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>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Filtering todos..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">return</span> todos<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span>todo <span class="token operator">=&gt;</span>
      todo<span class="token punctuation">.</span>text<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">includes</span><span class="token punctuation">(</span>search<span class="token punctuation">.</span><span class="token function">toLowerCase</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> search<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 operator">&lt;</span>div<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>input
        type<span class="token operator">=</span><span class="token string">"text"</span>
        placeholder<span class="token operator">=</span><span class="token string">"Search todos..."</span>
        value<span class="token operator">=</span><span class="token punctuation">{</span>search<span class="token punctuation">}</span>
        onChange<span class="token operator">=</span><span class="token punctuation">{</span>e <span class="token operator">=&gt;</span> <span class="token function">setSearch</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span>
      <span class="token operator">/</span><span class="token operator">&gt;</span>

      <span class="token operator">&lt;</span>ul<span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>filteredTodos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>todo <span class="token operator">=&gt;</span> <span class="token punctuation">(</span>
          <span class="token operator">&lt;</span>li key<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<span class="token punctuation">}</span><span class="token operator">&gt;</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>text<span class="token punctuation">}</span><span class="token operator">&lt;</span><span class="token operator">/</span>li<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">&lt;</span><span class="token operator">/</span>ul<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Now, the filter only runs when either <code>todos</code> or <code>search</code> changes. If you trigger a re-render for any other reason (say, toggling a modal or updating unrelated state higher up in the tree), React will reuse the cached filtered list from the previous render.</p><p><code>useMemo</code> shines in situations like this when you&rsquo;re doing expensive or repetitive calculations during render that don&rsquo;t need to run every single time. It&rsquo;s not about squeezing out microseconds; it&rsquo;s about keeping your renders predictable and efficient as your app scales.</p><p>In this todo example, the difference is subtle, but in a real-world app where filtering, sorting or formatting can get complex, memoization can save a noticeable amount of work.</p><h3>useCallback: Memoizing Functions for Stability</h3><p>If <code>useMemo</code> helps React remember values, then `useCallback helps React remember functions.</p><p>At first,this might sound unnecessary&mdash;after all, functions are cheap to create, right? But in React, passing functions down as props can sometimes cause subtle and frustrating re-renders that you don&rsquo;t expect.</p><p>The <code>useCallback()</code> hook is one of React&rsquo;s built-in tools for optimizing re-renders. Its job is simple but powerful&mdash;it lets React remember your function definitions between renders, so they don&rsquo;t get recreated every single time your component reruns.</p><p>In short, it memoizes the function itself.</p><p>Syntax:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> memoizedFunction <span class="token operator">=</span> <span class="token function">useCallback</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// Your logic here</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span>dependency1<span class="token punctuation">,</span> dependency2<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>
</code></pre><p>The <code>useCallback()</code> hook takes two arguments:</p><ol><li><strong>A function to memoize:</strong> This is the function you want React to remember. It can take any arguments and return any value.</li><li><strong>A dependency array:</strong> This lists every reactive value (state, props or variables) used inside that function. React will only recreate the function if one of those dependencies changes between renders.</li></ol><p>Here&rsquo;s what happens under the hood:</p><ul><li>On the first render, React creates and returns the function.</li><li>On later renders, React checks if any dependencies have changed.</li></ul><p>This simple mechanism keeps the function reference stable across renders, which is exactly what you want when passing callbacks to memoized child components.</p><p>Remember: Since <code>useCallback</code> is a hook, it must follow the rules of hooks. Call it only at the top level of a React component or custom hook, never inside loops, conditionals or nested functions.</p><h3>Practical Example: Preventing Unnecessary Re-renders</h3><p>Let&rsquo;s bring this to life with a practical example.</p><p>Suppose you&rsquo;re building a simple todo app that displays a list of tasks, with the ability to mark each one as done or undone.</p><p>Here&rsquo;s the initial setup:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">import</span> React<span class="token punctuation">,</span> <span class="token punctuation">{</span> useState <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"react"</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> TodoItem <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">memo</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">{</span> todo<span class="token punctuation">,</span> onChange <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`Rendering </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>todo<span class="token punctuation">.</span>name<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 keyword">return</span> <span class="token punctuation">(</span>
    <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>span style<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> textDecoration<span class="token punctuation">:</span> todo<span class="token punctuation">.</span>done <span class="token operator">?</span> <span class="token string">"line-through"</span> <span class="token punctuation">:</span> <span class="token string">"none"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>todo<span class="token punctuation">.</span>name<span class="token punctuation">}</span>
      <span class="token operator">&lt;</span><span class="token operator">/</span>span<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>button onClick<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">onChange</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>todo<span class="token punctuation">.</span>done <span class="token operator">?</span> <span class="token string">"Undone"</span> <span class="token punctuation">:</span> <span class="token string">"Done"</span><span class="token punctuation">}</span>
      <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> demoTodos <span class="token operator">=</span> <span class="token punctuation">[</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">"Todo 1"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">"Todo 2"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">"Todo 3"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">"Todo 4"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">TodoList</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> setTodos<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span>demoTodos<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">function</span> <span class="token function">toggleTodo</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">setTodos</span><span class="token punctuation">(</span>prevTodos <span class="token operator">=&gt;</span>
      prevTodos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>todo <span class="token operator">=&gt;</span>
        todo<span class="token punctuation">.</span>id <span class="token operator">===</span> id <span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token operator">...</span>todo<span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token operator">!</span>todo<span class="token punctuation">.</span>done <span class="token punctuation">}</span> <span class="token punctuation">:</span> todo
      <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 operator">&lt;</span>div<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>h2<span class="token operator">&gt;</span>Today&rsquo;s Todos<span class="token operator">&lt;</span><span class="token operator">/</span>h2<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>ul<span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>todos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>todo <span class="token operator">=&gt;</span> <span class="token punctuation">(</span>
          <span class="token operator">&lt;</span>li key<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<span class="token punctuation">}</span><span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>TodoItem todo<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">}</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span>toggleTodo<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
          <span class="token operator">&lt;</span><span class="token operator">/</span>li<span class="token operator">&gt;</span>
        <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
      <span class="token operator">&lt;</span><span class="token operator">/</span>ul<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> TodoList<span class="token punctuation">;</span>
</code></pre><p>The <code>TodoItem</code> component is wrapped in <code>React.memo</code>, meaning it shouldn&rsquo;t re-render unless its props change. But if you try this out, you&rsquo;ll notice something strange&mdash;clicking the button causes every TodoItem to re-render, not just the one you toggled:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-01/react-todo-demo.gif?sfvrsn=5d7b5624_2" alt="Demo 1" /></p><h3 id="why">Why?</h3><p>Because each time <code>TodoList</code> re-renders, React creates a brand-new <code>toggleTodo</code> function. From React&rsquo;s perspective, that means the <code>onChange</code> prop on every child is now different, even if the logic is the same, so <code>React.memo</code> decides to re-render everything.</p><p>That&rsquo;s exactly the kind of subtle inefficiency <code>useCallback</code> fixes.</p><h3 id="fixing-it-with-usecallback">Fixing it with useCallback</h3><p>Let&rsquo;s wrap the <code>toggleTodo</code> function in <code>useCallback</code> to make its reference stable:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">import</span> React<span class="token punctuation">,</span> <span class="token punctuation">{</span> useState<span class="token punctuation">,</span> useCallback <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"react"</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> TodoItem <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">memo</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">{</span> todo<span class="token punctuation">,</span> onChange <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`Rendering </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>todo<span class="token punctuation">.</span>name<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 keyword">return</span> <span class="token punctuation">(</span>
    <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>span style<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> textDecoration<span class="token punctuation">:</span> todo<span class="token punctuation">.</span>done <span class="token operator">?</span> <span class="token string">"line-through"</span> <span class="token punctuation">:</span> <span class="token string">"none"</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>todo<span class="token punctuation">.</span>name<span class="token punctuation">}</span>
      <span class="token operator">&lt;</span><span class="token operator">/</span>span<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>button onClick<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">onChange</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">}</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>todo<span class="token punctuation">.</span>done <span class="token operator">?</span> <span class="token string">"Undone"</span> <span class="token punctuation">:</span> <span class="token string">"Done"</span><span class="token punctuation">}</span>
      <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> demoTodos <span class="token operator">=</span> <span class="token punctuation">[</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">"Todo 1"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">"Todo 2"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">"Todo 3"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">"Todo 4"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token function-variable function">TodoList</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> setTodos<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span>demoTodos<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> toggleTodo <span class="token operator">=</span> <span class="token function">useCallback</span><span class="token punctuation">(</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">setTodos</span><span class="token punctuation">(</span>prevTodos <span class="token operator">=&gt;</span>
      prevTodos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>todo <span class="token operator">=&gt;</span>
        todo<span class="token punctuation">.</span>id <span class="token operator">===</span> id <span class="token operator">?</span> <span class="token punctuation">{</span> <span class="token operator">...</span>todo<span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token operator">!</span>todo<span class="token punctuation">.</span>done <span class="token punctuation">}</span> <span class="token punctuation">:</span> todo
      <span class="token punctuation">)</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> <span class="token punctuation">(</span>
    <span class="token operator">&lt;</span>div<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>h2<span class="token operator">&gt;</span>Today&rsquo;s Todos<span class="token operator">&lt;</span><span class="token operator">/</span>h2<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>ul<span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>todos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span>todo <span class="token operator">=&gt;</span> <span class="token punctuation">(</span>
          <span class="token operator">&lt;</span>li key<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<span class="token punctuation">}</span><span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>TodoItem todo<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">}</span> onChange<span class="token operator">=</span><span class="token punctuation">{</span>toggleTodo<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
          <span class="token operator">&lt;</span><span class="token operator">/</span>li<span class="token operator">&gt;</span>
        <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
      <span class="token operator">&lt;</span><span class="token operator">/</span>ul<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> TodoList<span class="token punctuation">;</span>
</code></pre><p>Now, the <code>toggleTodo</code> function keeps the same reference between renders because its dependency array (<code>[]</code>) is empty. React creates it once and reuses it as long as the component stays mounted.</p><p>Since <code>TodoItem</code> receives a stable <code>onChange</code> prop, <code>React.memo</code> can finally do its job&mdash;only the toggled TodoItem re-renders, not the entire list.</p><p>In a small Todo app, this might feel like a micro-optimization. But in a real app, think of dozens of components, large lists or complex trees. Stabilizing your callbacks can prevent entire subtrees from re-rendering unnecessarily.</p><p>That&rsquo;s not just about performance; it&rsquo;s about keeping your app predictable, scalable and smooth.</p><p>Here&rsquo;s the <a target="_blank" href="https://codesandbox.io/p/sandbox/usecallback-3dppld">CodeSandbox demo</a> if you want to play around with it. Try adding features like add, delete or edit, and see how <code>useCallback</code> helps control re-renders as your app grows.</p><h2 id="when-and-when-not-to--memoize">When (and When Not) to Memoize</h2><p>Memoization is powerful, but it&rsquo;s not free. Every memoized value or component adds a bit of overhead. React needs to compare dependencies, store results and decide whether to reuse or recompute. Overusing it can actually make things slower or harder to read.</p><h3>Use Memoization When:</h3><ul><li>A component re-renders often with identical props.</li><li>You&rsquo;re doing expensive computations (sorting, filtering, formatting).</li><li>You&rsquo;re passing callbacks to deeply nested children wrapped in <code>React.memo</code></li></ul><h3>Avoid Memoization When:</h3><ul><li>The component is small and cheap to render.</li><li>Props change frequently anyway.</li><li>You&rsquo;re just memoizing out of habit.</li></ul><p>In short: <strong>Measure first, optimize second.</strong> Don&rsquo;t sprinkle memoization everywhere &ldquo;just in case.&rdquo;</p><h2 id="react-19-rethinking-memoization">React 19: Rethinking Memoization</h2><p>Here&rsquo;s where things get interesting. React 19 introduces several architectural improvements that make manual memoization far less critical than before. Under the hood, React&rsquo;s new compiler can automatically memoize components and hooks at build time&mdash;analyzing your component&rsquo;s dependencies and optimizing re-renders intelligently.</p><p>That means in most cases, you no longer need to reach for <code>React.memo</code>, <code>useMemo</code> or <code>useCallback</code> unless you&rsquo;re dealing with very specific edge cases.</p><p>Here&rsquo;s what changes:</p><ul><li><strong>Automatic memoization:</strong> The React compiler now detects pure components and automatically memoizes them, skipping re-renders when inputs haven&rsquo;t changed.</li><li><strong>Smarter dependency tracking:</strong> Instead of relying on manual dependency arrays, React can track dependencies automatically, reducing human error and cleanup code.</li><li><strong>Simplified mental model:</strong> You can focus on writing clean, declarative code, and React handles the optimization behind the scenes.</li></ul><p>In practice, this means your React 19 codebase becomes simpler and less noisy. Hooks like <code>useCallback</code> and <code>useMemo</code> are still available for advanced control, but they&rsquo;re no longer the first thing you reach for&mdash;React just <em>does the right thing</em> by default.</p><h2 id="final-thoughts">Final Thoughts</h2><p>Memoization in React is all about efficiency, caching results or function references to avoid redundant work.</p><p>Before React 19, we relied heavily on <code>React.memo</code>, <code>useMemo</code> and <code>useCallback</code> to manage this manually. But with React 19&rsquo;s new compiler, much of that optimization happens automatically.</p><p>Still, knowing how memoization works gives you the mental model to understand what React is doing behind the scenes and when to step in yourself. Because even in the new world of auto-optimization, the best React developers understand the &ldquo;why,&rdquo; not just the &ldquo;what.&rdquo;</p><h3 id="further-reading">Further Reading</h3><ul><li><a target="_blank" href="https://www.telerik.com/blogs/react-basics-how-use-react-usecallback-effectively">React Basics: How to Use React useCallback Effectively</a></li><li><a target="_blank" href="https://www.telerik.com/blogs/learn-how-usememo-hook-once-all">Learn How to Use the useMemo Hook Once and for All</a></li><li><a target="_blank" href="https://www.telerik.com/blogs/how-optimize-code-react-compiler-beta">How to Optimize Your Code with React Compiler (Still in Beta)</a></li><li><a target="_blank" href="https://react.dev/reference/react/memo">React memo</a></li></ul><img src="https://feeds.telerik.com/link/23056/17268597.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:ee9c0569-ac10-432d-b5d9-c3543cd39ca7</id>
    <title type="text">Exploring the AI-Powered KendoReact Smart Grid</title>
    <summary type="text">The KendoReact SmartGrid brings AI prompting capabilities to your favorite enterprise-grade component.v</summary>
    <published>2026-01-21T17:56:47Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>Hassan Djirdeh </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17259438/exploring-ai-powered-kendoreact-smart-grid"/>
    <content type="text"><![CDATA[<p><span class="featured">The KendoReact SmartGrid brings AI prompting capabilities to your favorite enterprise-grade component.</span></p><p>Working with large datasets in web applications can involve repetitive tasks, such as manually configuring filters, setting sorting rules or grouping data in multiple ways to find the information you need. While traditional data grids (like the Progress <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid">KendoReact Data Grid</a>) provide the tools to accomplish these operations, they require users to understand the grid&rsquo;s interface and manually configure each step of each operation.</p><p>What if our data grid could understand natural language commands? What if we could ask it to &ldquo;show only failed transactions over $500&rdquo; or &ldquo;group customers by region and sort by revenue&rdquo;? This is exactly what the new <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid/smart">KendoReact SmartGrid</a> offers.</p><h2 id="kendoreact-smartgrid">KendoReact SmartGrid</h2><p>The KendoReact SmartGrid enhances the traditional KendoReact DataGrid with AI-powered capabilities that enable users to interact with data using natural language. Instead of clicking through menus and manually configuring filters, users can describe what they want to see, and the AI assistant translates those requests into grid operations.</p><p>The SmartGrid maintains all the powerful features of the standard grid (sorting, filtering, grouping, etc.) while adding an intelligent layer that interprets user intent and automatically applies the appropriate data operations.</p><p>Let&rsquo;s start with a basic implementation to see the SmartGrid in action:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>
  Grid<span class="token punctuation">,</span>
  GridColumn<span class="token punctuation">,</span>
  GridToolbar<span class="token punctuation">,</span>
  GridToolbarAIAssistant<span class="token punctuation">,</span>
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@progress/kendo-react-grid'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> filterIcon <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@progress/kendo-svg-icons'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">App</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> transactions <span class="token operator">=</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
      customerName<span class="token punctuation">:</span> <span class="token string">'Acme Corp'</span><span class="token punctuation">,</span>
      amount<span class="token punctuation">:</span> <span class="token number">1250.0</span><span class="token punctuation">,</span>
      currency<span class="token punctuation">:</span> <span class="token string">'USD'</span><span class="token punctuation">,</span>
      status<span class="token punctuation">:</span> <span class="token string">'Completed'</span><span class="token punctuation">,</span>
      transType<span class="token punctuation">:</span> <span class="token string">'Deposit'</span><span class="token punctuation">,</span>
      transDate<span class="token punctuation">:</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token string">'2024-10-15'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
      customerName<span class="token punctuation">:</span> <span class="token string">'Tech Solutions'</span><span class="token punctuation">,</span>
      amount<span class="token punctuation">:</span> <span class="token number">450.0</span><span class="token punctuation">,</span>
      currency<span class="token punctuation">:</span> <span class="token string">'EUR'</span><span class="token punctuation">,</span>
      status<span class="token punctuation">:</span> <span class="token string">'Failed'</span><span class="token punctuation">,</span>
      transType<span class="token punctuation">:</span> <span class="token string">'Withdrawal'</span><span class="token punctuation">,</span>
      transDate<span class="token punctuation">:</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token string">'2024-10-16'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span>
      customerName<span class="token punctuation">:</span> <span class="token string">'Global Industries'</span><span class="token punctuation">,</span>
      amount<span class="token punctuation">:</span> <span class="token number">2100.0</span><span class="token punctuation">,</span>
      currency<span class="token punctuation">:</span> <span class="token string">'USD'</span><span class="token punctuation">,</span>
      status<span class="token punctuation">:</span> <span class="token string">'Completed'</span><span class="token punctuation">,</span>
      transType<span class="token punctuation">:</span> <span class="token string">'Deposit'</span><span class="token punctuation">,</span>
      transDate<span class="token punctuation">:</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token string">'2024-10-17'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> <span class="token number">4</span><span class="token punctuation">,</span>
      customerName<span class="token punctuation">:</span> <span class="token string">'StartUp Inc'</span><span class="token punctuation">,</span>
      amount<span class="token punctuation">:</span> <span class="token number">850.0</span><span class="token punctuation">,</span>
      currency<span class="token punctuation">:</span> <span class="token string">'GBP'</span><span class="token punctuation">,</span>
      status<span class="token punctuation">:</span> <span class="token string">'Pending'</span><span class="token punctuation">,</span>
      transType<span class="token punctuation">:</span> <span class="token string">'Withdrawal'</span><span class="token punctuation">,</span>
      transDate<span class="token punctuation">:</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token string">'2024-10-18'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> <span class="token number">5</span><span class="token punctuation">,</span>
      customerName<span class="token punctuation">:</span> <span class="token string">'Enterprise Co'</span><span class="token punctuation">,</span>
      amount<span class="token punctuation">:</span> <span class="token number">3200.0</span><span class="token punctuation">,</span>
      currency<span class="token punctuation">:</span> <span class="token string">'USD'</span><span class="token punctuation">,</span>
      status<span class="token punctuation">:</span> <span class="token string">'Completed'</span><span class="token punctuation">,</span>
      transType<span class="token punctuation">:</span> <span class="token string">'Deposit'</span><span class="token punctuation">,</span>
      transDate<span class="token punctuation">:</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token string">'2024-10-19'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">;</span>

  <span class="token keyword">return</span> <span class="token punctuation">(</span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>Grid</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 boolean">true</span><span class="token punctuation">}</span></span>
      <span class="token attr-name">dataItemKey</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>id<span class="token punctuation">"</span></span>
      <span class="token attr-name">data</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>transactions<span class="token punctuation">}</span></span>
      <span class="token attr-name">sortable</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token boolean">true</span><span class="token punctuation">}</span></span>
      <span class="token attr-name">groupable</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token boolean">true</span><span class="token punctuation">}</span></span>
      <span class="token attr-name">pageable</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token boolean">true</span><span class="token punctuation">}</span></span>
      <span class="token attr-name">columnMenuIcon</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>filterIcon<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>GridToolbar</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridToolbarAIAssistant</span>
          <span class="token attr-name">requestUrl</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://demos.telerik.com/service/v2/ai/grid/smart-state<span class="token punctuation">"</span></span>
          <span class="token attr-name">promptPlaceHolder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Filter, sort or group with AI<span class="token punctuation">"</span></span>
          <span class="token attr-name">suggestionsList</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token punctuation">[</span>
            <span class="token string">'Sort by amount descending'</span><span class="token punctuation">,</span>
            <span class="token string">'Show only completed transactions'</span><span class="token punctuation">,</span>
            <span class="token string">'Group by transaction type'</span><span class="token punctuation">,</span>
            <span class="token string">'Filter where currency is USD'</span><span class="token punctuation">,</span>
          <span class="token punctuation">]</span><span class="token punctuation">}</span></span>
          <span class="token attr-name">enableSpeechToText</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token boolean">true</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>GridToolbar</span><span class="token punctuation">&gt;</span></span>

      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>customerName<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Customer Name<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token number">180</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>GridColumn</span> <span class="token attr-name">field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>amount<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>Amount<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token number">120</span><span class="token punctuation">}</span></span> <span class="token attr-name">format</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{0:c2}<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>currency<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>Currency<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token number">100</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>GridColumn</span> <span class="token attr-name">field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>status<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Status<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token number">120</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>GridColumn</span> <span class="token attr-name">field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>transType<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>Type<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token number">120</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>GridColumn</span>
        <span class="token attr-name">field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>transDate<span class="token punctuation">"</span></span>
        <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Date<span class="token punctuation">"</span></span>
        <span class="token attr-name">width</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token number">140</span><span class="token punctuation">}</span></span>
        <span class="token attr-name">format</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{0:MM/dd/yyyy}<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>Grid</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span>
</code></pre><p>In the example above, we&rsquo;ve created a transaction grid using the AI-powered toolbar assistant. The <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid/api/gridtoolbaraiassistant">GridToolbarAIAssistant</a> component provides a natural language interface that lets users type commands like &ldquo;show only completed transactions&rdquo; or &ldquo;sort by amount descending,&rdquo; and the AI automatically applies those operations to the grid.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-12/kendo-smart-grid-basic.gif?sfvrsn=a554782d_2" alt="" /></p><p>The <code>autoProcessData</code> prop is key here in the above example. It enables the grid to automatically handle state updates when AI operations are applied, eliminating the need for manual state management in simple scenarios.</p><blockquote><p>The SmartGrid is part of KendoReact Premium, <a target="_blank" href="https://www.telerik.com/kendo-react-ui#two-paths-forward">an enterprise-grade UI library with 120+ components</a>. The AI features demonstrated here use a Telerik-hosted service for demonstration purposes. For production applications, you&rsquo;ll want to implement your own AI service tailored to your specific domain and data requirements.</p></blockquote><h2 id="voice-input-support">Voice Input Support</h2><p>A cool feature of the SmartGrid is its support for speech-to-text input. By setting <code>enableSpeechToText={true}</code> on the <code>GridToolbarAIAssistant</code>, users can speak their commands directly instead of typing them.</p><p>To use voice input, users click the microphone icon in the AI assistant and speak their command. The grid processes spoken commands the same way as typed text, so commands like &ldquo;show only completed transactions&rdquo; work just as seamlessly.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-12/kendo-smart-grid-voice.gif?sfvrsn=d6300b03_2" alt="" /></p><p>The voice input feature uses the browser&rsquo;s native Web Speech API, so it works across modern browsers without additional dependencies or setup.</p><h2 id="tracking-operations-with-controlled-integration">Tracking Operations with Controlled Integration</h2><p>While <code>autoProcessData</code> works great for simple scenarios, we may need more control over how AI operations are applied to our grid. For example, we can log user interactions, validate AI suggestions before applying them or display an operation history to users.</p><p>The controlled approach gives us this flexibility through the <code>onResponseSuccess</code> callback:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> process <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">import</span> <span class="token punctuation">{</span>
  Grid<span class="token punctuation">,</span>
  GridColumn<span class="token punctuation">,</span>
  GridToolbar<span class="token punctuation">,</span>
  GridToolbarAIAssistant<span class="token punctuation">,</span>
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@progress/kendo-react-grid'</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">App</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>dataState<span class="token punctuation">,</span> setDataState<span class="token punctuation">]</span> <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    skip<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
    take<span class="token punctuation">:</span> <span class="token number">10</span><span class="token punctuation">,</span>
    sort<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
    filter<span class="token punctuation">:</span> undefined<span class="token punctuation">,</span>
    group<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> <span class="token punctuation">[</span>outputs<span class="token punctuation">,</span> setOutputs<span class="token punctuation">]</span> <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">useState</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> aiAssistantRef <span class="token operator">=</span> React<span class="token punctuation">.</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> transactions <span class="token operator">=</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
      customerName<span class="token punctuation">:</span> <span class="token string">'Acme Corp'</span><span class="token punctuation">,</span>
      amount<span class="token punctuation">:</span> <span class="token number">1250.0</span><span class="token punctuation">,</span>
      status<span class="token punctuation">:</span> <span class="token string">'Completed'</span><span class="token punctuation">,</span>
      currency<span class="token punctuation">:</span> <span class="token string">'USD'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
      customerName<span class="token punctuation">:</span> <span class="token string">'Tech Solutions'</span><span class="token punctuation">,</span>
      amount<span class="token punctuation">:</span> <span class="token number">450.0</span><span class="token punctuation">,</span>
      status<span class="token punctuation">:</span> <span class="token string">'Failed'</span><span class="token punctuation">,</span>
      currency<span class="token punctuation">:</span> <span class="token string">'EUR'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span>
      customerName<span class="token punctuation">:</span> <span class="token string">'Global Industries'</span><span class="token punctuation">,</span>
      amount<span class="token punctuation">:</span> <span class="token number">2100.0</span><span class="token punctuation">,</span>
      status<span class="token punctuation">:</span> <span class="token string">'Completed'</span><span class="token punctuation">,</span>
      currency<span class="token punctuation">:</span> <span class="token string">'USD'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> <span class="token number">4</span><span class="token punctuation">,</span>
      customerName<span class="token punctuation">:</span> <span class="token string">'StartUp Inc'</span><span class="token punctuation">,</span>
      amount<span class="token punctuation">:</span> <span class="token number">850.0</span><span class="token punctuation">,</span>
      status<span class="token punctuation">:</span> <span class="token string">'Pending'</span><span class="token punctuation">,</span>
      currency<span class="token punctuation">:</span> <span class="token string">'GBP'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span>
      id<span class="token punctuation">:</span> <span class="token number">5</span><span class="token punctuation">,</span>
      customerName<span class="token punctuation">:</span> <span class="token string">'Enterprise Co'</span><span class="token punctuation">,</span>
      amount<span class="token punctuation">:</span> <span class="token number">3200.0</span><span class="token punctuation">,</span>
      status<span class="token punctuation">:</span> <span class="token string">'Completed'</span><span class="token punctuation">,</span>
      currency<span class="token punctuation">:</span> <span class="token string">'USD'</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> <span class="token function-variable function">handleResponseSuccess</span> <span class="token operator">=</span> <span class="token punctuation">(</span>response<span class="token punctuation">,</span> promptMessage<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span>response <span class="token operator">&amp;&amp;</span> response<span class="token punctuation">.</span>data<span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token comment">// Apply AI-suggested operations to grid state</span>
      <span class="token keyword">const</span> newState <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token operator">...</span>dataState <span class="token punctuation">}</span><span class="token punctuation">;</span>

      <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>sort<span class="token punctuation">)</span> newState<span class="token punctuation">.</span>sort <span class="token operator">=</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>sort<span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>filter<span class="token punctuation">)</span> newState<span class="token punctuation">.</span>filter <span class="token operator">=</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>filter<span class="token punctuation">;</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>group<span class="token punctuation">)</span> newState<span class="token punctuation">.</span>group <span class="token operator">=</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>group<span class="token punctuation">;</span>

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

      <span class="token comment">// Track the operation in history</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span>response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>messages<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> newOutput <span class="token operator">=</span> <span class="token punctuation">{</span>
          id<span class="token punctuation">:</span> outputs<span class="token punctuation">.</span>length <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">,</span>
          prompt<span class="token punctuation">:</span> promptMessage<span class="token punctuation">,</span>
          responseContent<span class="token punctuation">:</span> response<span class="token punctuation">.</span>data<span class="token punctuation">.</span>messages<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token string">'\n'</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">setOutputs</span><span class="token punctuation">(</span><span class="token punctuation">[</span>newOutput<span class="token punctuation">,</span> <span class="token operator">...</span>outputs<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>

    aiAssistantRef<span class="token punctuation">.</span>current<span class="token operator">?</span><span class="token punctuation">.</span><span class="token function">hide</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> processedData <span class="token operator">=</span> <span class="token function">process</span><span class="token punctuation">(</span>transactions<span class="token punctuation">,</span> dataState<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">return</span> <span class="token punctuation">(</span>
    <span class="token operator">&lt;</span>Grid
      dataItemKey<span class="token operator">=</span><span class="token string">"id"</span>
      data<span class="token operator">=</span><span class="token punctuation">{</span>processedData<span class="token punctuation">.</span>data<span class="token punctuation">}</span>
      total<span class="token operator">=</span><span class="token punctuation">{</span>processedData<span class="token punctuation">.</span>total<span class="token punctuation">}</span>
      sortable<span class="token operator">=</span><span class="token punctuation">{</span><span class="token boolean">true</span><span class="token punctuation">}</span>
      sort<span class="token operator">=</span><span class="token punctuation">{</span>dataState<span class="token punctuation">.</span>sort<span class="token punctuation">}</span>
      onSortChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setDataState</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span>dataState<span class="token punctuation">,</span> sort<span class="token punctuation">:</span> e<span class="token punctuation">.</span>sort <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
      filterable<span class="token operator">=</span><span class="token punctuation">{</span><span class="token boolean">true</span><span class="token punctuation">}</span>
      filter<span class="token operator">=</span><span class="token punctuation">{</span>dataState<span class="token punctuation">.</span>filter<span class="token punctuation">}</span>
      onFilterChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setDataState</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span>dataState<span class="token punctuation">,</span> filter<span class="token punctuation">:</span> e<span class="token punctuation">.</span>filter <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
      groupable<span class="token operator">=</span><span class="token punctuation">{</span><span class="token boolean">true</span><span class="token punctuation">}</span>
      group<span class="token operator">=</span><span class="token punctuation">{</span>dataState<span class="token punctuation">.</span>group<span class="token punctuation">}</span>
      onGroupChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setDataState</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token operator">...</span>dataState<span class="token punctuation">,</span> group<span class="token punctuation">:</span> e<span class="token punctuation">.</span>group <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">}</span>
      pageable<span class="token operator">=</span><span class="token punctuation">{</span><span class="token boolean">true</span><span class="token punctuation">}</span>
      skip<span class="token operator">=</span><span class="token punctuation">{</span>dataState<span class="token punctuation">.</span>skip<span class="token punctuation">}</span>
      take<span class="token operator">=</span><span class="token punctuation">{</span>dataState<span class="token punctuation">.</span>take<span class="token punctuation">}</span>
      onPageChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
        <span class="token function">setDataState</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
          <span class="token operator">...</span>dataState<span class="token punctuation">,</span>
          skip<span class="token punctuation">:</span> e<span class="token punctuation">.</span>page<span class="token punctuation">.</span>skip<span class="token punctuation">,</span>
          take<span class="token punctuation">:</span> e<span class="token punctuation">.</span>page<span class="token punctuation">.</span>take<span class="token punctuation">,</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 tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridToolbar</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridToolbarAIAssistant</span>
          <span class="token attr-name">ref</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>aiAssistantRef<span class="token punctuation">}</span></span>
          <span class="token attr-name">requestUrl</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>https://demos.telerik.com/service/v2/ai/grid/smart-state<span class="token punctuation">"</span></span>
          <span class="token attr-name">onResponseSuccess</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>handleResponseSuccess<span class="token punctuation">}</span></span>
          <span class="token attr-name">outputs</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>outputs<span class="token punctuation">}</span></span>
          <span class="token attr-name">promptPlaceHolder</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Filter, sort or group with AI<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>GridToolbar</span><span class="token punctuation">&gt;</span></span>

      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>customerName<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Customer<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token number">180</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>GridColumn</span> <span class="token attr-name">field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>amount<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>Amount<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token number">120</span><span class="token punctuation">}</span></span> <span class="token attr-name">format</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>{0:c2}<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>GridColumn</span> <span class="token attr-name">field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>status<span class="token punctuation">"</span></span> <span class="token attr-name">title</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>Status<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token number">120</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>GridColumn</span> <span class="token attr-name">field</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>currency<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>Currency<span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span><span class="token number">100</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>Grid</span><span class="token punctuation">&gt;</span></span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span>
</code></pre><p>In this controlled approach, we explicitly manage the grid&rsquo;s state and intercept AI responses via <code>onResponseSuccess</code>. This allows us to:</p><ul><li><strong>Validate AI suggestions</strong> before applying them to the grid.</li><li><strong>Log user interactions</strong> and AI operations for analytics or debugging.</li><li><strong>Display operation history</strong> through the <code>outputs</code> prop, giving users transparency into what commands they&rsquo;ve issued.</li></ul><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-12/kendo-smart-grid-controlled.gif?sfvrsn=b399003_2" alt="" /></p><p>The <code>outputs</code> prop displays a history of AI operations, helping users understand how their natural language requests were interpreted and what operations were performed on the grid.</p><h2 id="wrap-up">Wrap-up</h2><p>The KendoReact SmartGrid brings AI-powered capabilities to data grids, making complex data operations accessible through natural language. By simply describing what they want to see, users can filter, sort and group data without needing to understand the intricacies of the grid interface.</p><p>For more details on implementing the SmartGrid and exploring its capabilities, check out the official documentation:</p><ul><li><a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid/smart">KendoReact Smart DataGrid Overview</a></li><li><a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/grid">KendoReact Grid Documentation</a></li></ul><p>And to try it for yourself, download the 30-day free trial:</p><p><a target="_blank" href="https://www.telerik.com/try/kendo-react-ui" class="Btn">Try Now</a></p><img src="https://feeds.telerik.com/link/23056/17259438.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:3569d367-3697-4c90-bd8d-d44bf89b92f0</id>
    <title type="text">KendoReact Internationalization</title>
    <summary type="text">The React Internationalization component package can help translate your app’s numbers and dates into regionally appropriate formats.</summary>
    <published>2026-01-12T19:55:38Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>Hassan Djirdeh </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17251907/kendoreact-internationalization"/>
    <content type="text"><![CDATA[<p><span class="featured">The React Internationalization component package can help translate your app&rsquo;s numbers and dates into regionally appropriate formats.</span></p><p>When building applications for users around the world, displaying data in familiar formats is important. A date like &ldquo;3/4/2024&rdquo; might mean March 4 to Americans but April 3 to Europeans. Similarly, the number &ldquo;1,234.56&rdquo; uses different separators in different countries; some use commas for thousands and periods for decimals, while others do the opposite. This is where <strong>internationalization</strong> comes in.</p><p>In this article, we&rsquo;ll explore the Progress <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/intl/i18n">KendoReact Internationalization</a> package and see how it handles the parsing and formatting of dates and numbers according to different conventions.</p><blockquote><p>The KendoReact Internationalization package is part of <a target="_blank" href="https://www.telerik.com/kendo-react-ui">KendoReact</a>, an enterprise-grade UI library with more than 120 components for building polished, performant apps.</p></blockquote><h2 id="internationalization-vs.-localization">Internationalization vs. Localization</h2><p>Before we dive in, let&rsquo;s clarify the difference between <em>internationalization</em> and <em>localization</em>, as these terms are often used interchangeably but can serve distinct purposes:</p><ul><li><strong>Internationalization</strong> focuses on parsing and formatting dates and numbers according to locale-specific rules. It makes &ldquo;1234.56&rdquo; display as &ldquo;1.234,56&rdquo; in Germany or dates follow the DD/MM/YYYY format in the UK.</li><li><strong>Localization</strong> handles translating UI text and supporting right-to-left (RTL) layouts for languages like Arabic. It sets button labels, error messages and other interface text in the user&rsquo;s preferred language.</li></ul><blockquote><p>For an introduction into localization and the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/intl/l10n">KendoReact Localization</a> package, check out our article on <a target="_blank" href="https://www.telerik.com" data-sf-ec-immutable="">Getting Started with KendoReact Localization</a>.</p></blockquote><p>Together, these two packages provide the complete <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/intl">globalization</a> features of KendoReact. In this article, we&rsquo;ll focus exclusively on internationalization.</p><h2 id="kendoreact-internationalization">KendoReact Internationalization</h2><p>The KendoReact Internationalization package is built on the <a target="_blank" href="https://cldr.unicode.org/">Unicode Common Locale Data Repository (CLDR)</a>, which provides standardized locale data for hundreds of languages and regions. The React Internationalization package exports an <code>IntlProvider</code> component that supplies formatting and parsing capabilities throughout a React app&rsquo;s component tree.</p><h2 id="setting-up-internationalization">Setting Up Internationalization</h2><p>To get started, we&rsquo;ll need to install the package:</p><pre class=" language-bash"><code class="prism  language-bash"><span class="token function">npm</span> <span class="token function">install</span> @progress/kendo-react-intl
</code></pre><p>For locales other than the default <code>en-US</code>, <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/intl/i18n/parsing-and-formatting#using-cldr-json-data">we&rsquo;ll also need CLDR data</a>, which can be found by installing the necessary packages:</p><pre class=" language-bash"><code class="prism  language-bash"><span class="token function">npm</span> <span class="token function">install</span> cldr-core cldr-numbers-full cldr-dates-full
</code></pre><p>These packages contain the locale-specific formatting rules that the Internationalization package uses to format dates and numbers correctly.</p><h2 id="formatting-numbers">Formatting Numbers</h2><p>Let&rsquo;s start with a practical example of number formatting. Different locales format numbers differently; what appears as &ldquo;1,234.56&rdquo; in the US becomes &ldquo;1.234,56&rdquo; in Germany and &ldquo;1 234,56&rdquo; in France.</p><p>Here&rsquo;s how to format numbers for different locales using the <code>IntlProvider</code> and the <code>useInternationalization</code> hook:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>
  IntlProvider<span class="token punctuation">,</span>
  load<span class="token punctuation">,</span>
  useInternationalization<span class="token punctuation">,</span>
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@progress/kendo-react-intl'</span><span class="token punctuation">;</span>

<span class="token comment">// Import CLDR data</span>
<span class="token keyword">import</span> likelySubtags <span class="token keyword">from</span> <span class="token string">'cldr-core/supplemental/likelySubtags.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> currencyData <span class="token keyword">from</span> <span class="token string">'cldr-core/supplemental/currencyData.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> weekData <span class="token keyword">from</span> <span class="token string">'cldr-core/supplemental/weekData.json'</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> enNumbers <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/en/numbers.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> enCurrencies <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/en/currencies.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> deNumbers <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/de/numbers.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> deCurrencies <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/de/currencies.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> esNumbers <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/es/numbers.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> esCurrencies <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/es/currencies.json'</span><span class="token punctuation">;</span>

<span class="token comment">// Load CLDR data</span>
<span class="token function">load</span><span class="token punctuation">(</span>
  likelySubtags<span class="token punctuation">,</span>
  currencyData<span class="token punctuation">,</span>
  weekData<span class="token punctuation">,</span>
  enNumbers<span class="token punctuation">,</span>
  enCurrencies<span class="token punctuation">,</span>
  deNumbers<span class="token punctuation">,</span>
  deCurrencies<span class="token punctuation">,</span>
  esNumbers<span class="token punctuation">,</span>
  esCurrencies
<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">NumberDisplay</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> value <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> intl <span class="token operator">=</span> <span class="token function">useInternationalization</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 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><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>Standard format<span class="token punctuation">:</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span> <span class="token punctuation">{</span>intl<span class="token punctuation">.</span><span class="token function">formatNumber</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> <span class="token string">'n2'</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>p</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><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>Currency format<span class="token punctuation">:</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span> <span class="token punctuation">{</span>intl<span class="token punctuation">.</span><span class="token function">formatNumber</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> <span class="token string">'c'</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>p</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><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>Percentage<span class="token punctuation">:</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span> <span class="token punctuation">{</span>intl<span class="token punctuation">.</span><span class="token function">formatNumber</span><span class="token punctuation">(</span>value <span class="token operator">/</span> <span class="token number">100</span><span class="token punctuation">,</span> <span class="token string">'p2'</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>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>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">App</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> numberValue <span class="token operator">=</span> <span class="token number">1234.56</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 punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">&gt;</span></span><span class="token function">English</span> <span class="token punctuation">(</span>US<span class="token punctuation">)</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>IntlProvider</span> <span class="token attr-name">locale</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>en<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>NumberDisplay</span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>numberValue<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>IntlProvider</span><span class="token punctuation">&gt;</span></span>

      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">&gt;</span></span><span class="token function">German</span> <span class="token punctuation">(</span>Germany<span class="token punctuation">)</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>IntlProvider</span> <span class="token attr-name">locale</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>de<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>NumberDisplay</span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>numberValue<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>IntlProvider</span><span class="token punctuation">&gt;</span></span>

      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">&gt;</span></span><span class="token function">Spanish</span> <span class="token punctuation">(</span>Spain<span class="token punctuation">)</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>IntlProvider</span> <span class="token attr-name">locale</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>es<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>NumberDisplay</span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>numberValue<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>IntlProvider</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>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span>
</code></pre><p>In this example, we load the necessary CLDR data at the top of our file using the <code>load</code> function. Then, we wrap each section with an <code>IntlProvider</code> that specifies the locale. Inside the <code>NumberDisplay</code> component, we use the <code>useInternationalization</code> hook to access the internationalization service, which provides the <code>formatNumber</code> method.</p><p>The <code>formatNumber</code> method accepts a value and a format string. Common format strings include:</p><ul><li><code>'n2'</code> - number with two decimal places</li><li><code>'c'</code> - currency</li><li><code>'p2'</code> - percentage with two decimal places</li></ul><p>When we run this app, we see the same number value (<code>1234.56</code>) displayed in three different sections, each formatted according to its respective locale&rsquo;s conventions:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2025/2025-12//kendoreact-internationalization-numbers.png" alt="" /></p><h2 id="formatting-dates">Formatting Dates</h2><p>Date formatting is equally important. The date November 6, 2000, can be written as &ldquo;11/6/2000&rdquo; in the US, &ldquo;06/11/2000&rdquo; in the UK or &ldquo;6.11.2000&rdquo; in Germany. Let&rsquo;s see how to handle these variations:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>
  IntlProvider<span class="token punctuation">,</span>
  load<span class="token punctuation">,</span>
  useInternationalization<span class="token punctuation">,</span>
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@progress/kendo-react-intl'</span><span class="token punctuation">;</span>

<span class="token comment">// Import CLDR data</span>
<span class="token keyword">import</span> likelySubtags <span class="token keyword">from</span> <span class="token string">'cldr-core/supplemental/likelySubtags.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> weekData <span class="token keyword">from</span> <span class="token string">'cldr-core/supplemental/weekData.json'</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> enCaGregorian <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/en/ca-gregorian.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> enTimeZoneNames <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/en/timeZoneNames.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> deCaGregorian <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/de/ca-gregorian.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> deTimeZoneNames <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/de/timeZoneNames.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> esCaGregorian <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/es/ca-gregorian.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> esTimeZoneNames <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/es/timeZoneNames.json'</span><span class="token punctuation">;</span>

<span class="token comment">// Load CLDR data</span>
<span class="token function">load</span><span class="token punctuation">(</span>
  likelySubtags<span class="token punctuation">,</span>
  weekData<span class="token punctuation">,</span>
  enCaGregorian<span class="token punctuation">,</span>
  enTimeZoneNames<span class="token punctuation">,</span>
  deCaGregorian<span class="token punctuation">,</span>
  deTimeZoneNames<span class="token punctuation">,</span>
  esCaGregorian<span class="token punctuation">,</span>
  esTimeZoneNames
<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">DateDisplay</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> value <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> intl <span class="token operator">=</span> <span class="token function">useInternationalization</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 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><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>Short date<span class="token punctuation">:</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span> <span class="token punctuation">{</span>intl<span class="token punctuation">.</span><span class="token function">formatDate</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> <span class="token string">'d'</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>p</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><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>Long date<span class="token punctuation">:</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span> <span class="token punctuation">{</span>intl<span class="token punctuation">.</span><span class="token function">formatDate</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> <span class="token string">'D'</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>p</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><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span>Full date<span class="token punctuation">:</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>strong</span><span class="token punctuation">&gt;</span></span> <span class="token punctuation">{</span>intl<span class="token punctuation">.</span><span class="token function">formatDate</span><span class="token punctuation">(</span>value<span class="token punctuation">,</span> <span class="token string">'EEEE, MMMM d, y'</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>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>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">App</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> dateValue <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token number">2000</span><span class="token punctuation">,</span> <span class="token number">10</span><span class="token punctuation">,</span> <span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// November 6th, 2000</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 punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">&gt;</span></span><span class="token function">English</span> <span class="token punctuation">(</span>US<span class="token punctuation">)</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>IntlProvider</span> <span class="token attr-name">locale</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>en<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>DateDisplay</span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>dateValue<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>IntlProvider</span><span class="token punctuation">&gt;</span></span>

      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">&gt;</span></span><span class="token function">German</span> <span class="token punctuation">(</span>Germany<span class="token punctuation">)</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>IntlProvider</span> <span class="token attr-name">locale</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>de<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>DateDisplay</span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>dateValue<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>IntlProvider</span><span class="token punctuation">&gt;</span></span>

      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h3</span><span class="token punctuation">&gt;</span></span><span class="token function">Spanish</span> <span class="token punctuation">(</span>Spain<span class="token punctuation">)</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h3</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>IntlProvider</span> <span class="token attr-name">locale</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>es<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>DateDisplay</span> <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>dateValue<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>IntlProvider</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>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span>
</code></pre><p>The <code>formatDate</code> method works similarly to <code>formatNumber</code>, accepting a date value and a format string. Common format patterns include:</p><ul><li><code>'d'</code> - short date pattern</li><li><code>'D'</code> - long date pattern</li><li>Custom patterns like <code>'EEEE, MMMM d, y'</code> for full control</li></ul><p>Running this app displays the same date (November 6, 2000) formatted in three different ways across English, German and Spanish locales:</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2025/2025-12//kendoreact-internationalization-dates.png" alt="" /></p><h2 id="interactive-locale-switching">Interactive Locale Switching</h2><p>We&rsquo;ll now build a more interactive example that allows users to switch between locales dynamically. This demonstrates how the Internationalization package can adapt to user preferences in real time:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> React <span class="token keyword">from</span> <span class="token string">'react'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>
  IntlProvider<span class="token punctuation">,</span>
  load<span class="token punctuation">,</span>
  useInternationalization<span class="token punctuation">,</span>
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@progress/kendo-react-intl'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> DropDownList <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@progress/kendo-react-dropdowns'</span><span class="token punctuation">;</span>

<span class="token comment">// Import CLDR data</span>
<span class="token keyword">import</span> likelySubtags <span class="token keyword">from</span> <span class="token string">'cldr-core/supplemental/likelySubtags.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> currencyData <span class="token keyword">from</span> <span class="token string">'cldr-core/supplemental/currencyData.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> weekData <span class="token keyword">from</span> <span class="token string">'cldr-core/supplemental/weekData.json'</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> enNumbers <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/en/numbers.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> enCurrencies <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/en/currencies.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> enCaGregorian <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/en/ca-gregorian.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> enTimeZoneNames <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/en/timeZoneNames.json'</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> deNumbers <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/de/numbers.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> deCurrencies <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/de/currencies.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> deCaGregorian <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/de/ca-gregorian.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> deTimeZoneNames <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/de/timeZoneNames.json'</span><span class="token punctuation">;</span>

<span class="token keyword">import</span> esNumbers <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/es/numbers.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> esCurrencies <span class="token keyword">from</span> <span class="token string">'cldr-numbers-full/main/es/currencies.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> esCaGregorian <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/es/ca-gregorian.json'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> esTimeZoneNames <span class="token keyword">from</span> <span class="token string">'cldr-dates-full/main/es/timeZoneNames.json'</span><span class="token punctuation">;</span>

<span class="token comment">// Load all CLDR data</span>
<span class="token function">load</span><span class="token punctuation">(</span>
  likelySubtags<span class="token punctuation">,</span>
  currencyData<span class="token punctuation">,</span>
  weekData<span class="token punctuation">,</span>
  enNumbers<span class="token punctuation">,</span>
  enCurrencies<span class="token punctuation">,</span>
  enCaGregorian<span class="token punctuation">,</span>
  enTimeZoneNames<span class="token punctuation">,</span>
  deNumbers<span class="token punctuation">,</span>
  deCurrencies<span class="token punctuation">,</span>
  deCaGregorian<span class="token punctuation">,</span>
  deTimeZoneNames<span class="token punctuation">,</span>
  esNumbers<span class="token punctuation">,</span>
  esCurrencies<span class="token punctuation">,</span>
  esCaGregorian<span class="token punctuation">,</span>
  esTimeZoneNames
<span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">DataDisplay</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> intl <span class="token operator">=</span> <span class="token function">useInternationalization</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> orderDate <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Date</span><span class="token punctuation">(</span><span class="token number">2024</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">15</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// March 15, 2024</span>
  <span class="token keyword">const</span> orderTotal <span class="token operator">=</span> <span class="token number">1234.56</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> taxRate <span class="token operator">=</span> <span class="token number">0.08</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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>data-card<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>h4</span><span class="token punctuation">&gt;</span></span>Order Summary<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>h4</span><span class="token punctuation">&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>data-row<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>Order Date<span class="token punctuation">:</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>intl<span class="token punctuation">.</span><span class="token function">formatDate</span><span class="token punctuation">(</span>orderDate<span class="token punctuation">,</span> <span class="token string">'d'</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>strong</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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>data-row<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>Subtotal<span class="token punctuation">:</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>intl<span class="token punctuation">.</span><span class="token function">formatNumber</span><span class="token punctuation">(</span>orderTotal<span class="token punctuation">,</span> <span class="token string">'c'</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>strong</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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>data-row<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>Tax Rate<span class="token punctuation">:</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>intl<span class="token punctuation">.</span><span class="token function">formatNumber</span><span class="token punctuation">(</span>taxRate<span class="token punctuation">,</span> <span class="token string">'p0'</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>strong</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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>data-row<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>Tax Amount<span class="token punctuation">:</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>intl<span class="token punctuation">.</span><span class="token function">formatNumber</span><span class="token punctuation">(</span>orderTotal <span class="token operator">*</span> taxRate<span class="token punctuation">,</span> <span class="token string">'c'</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>strong</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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>data-row total<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>Total<span class="token punctuation">:</span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>strong</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">{</span>intl<span class="token punctuation">.</span><span class="token function">formatNumber</span><span class="token punctuation">(</span>orderTotal <span class="token operator">*</span> <span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">+</span> taxRate<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">'c'</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>strong</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 punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> <span class="token function-variable function">App</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>locale<span class="token punctuation">,</span> setLocale<span class="token punctuation">]</span> <span class="token operator">=</span> React<span class="token punctuation">.</span><span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">'en'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> <span class="token function-variable function">handleLocaleChange</span> <span class="token operator">=</span> <span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">setLocale</span><span class="token punctuation">(</span>event<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<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>

  <span class="token keyword">const</span> localeOptions <span class="token operator">=</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span> text<span class="token punctuation">:</span> <span class="token string">'English (US)'</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> <span class="token string">'en'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> text<span class="token punctuation">:</span> <span class="token string">'Deutsch (Deutschland)'</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> <span class="token string">'de'</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">{</span> text<span class="token punctuation">:</span> <span class="token string">'Espa&ntilde;ol (Espa&ntilde;a)'</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> <span class="token string">'es'</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 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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>locale-selector<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>p</span><span class="token punctuation">&gt;</span></span>Select Locale<span class="token punctuation">:</span><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>DropDownList</span>
          <span class="token attr-name">data</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>localeOptions<span class="token punctuation">}</span></span>
          <span class="token attr-name">textField</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>text<span class="token punctuation">"</span></span>
          <span class="token attr-name">dataItemKey</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>value<span class="token punctuation">"</span></span>
          <span class="token attr-name">value</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>localeOptions<span class="token punctuation">.</span><span class="token function">find</span><span class="token punctuation">(</span><span class="token punctuation">(</span>l<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> l<span class="token punctuation">.</span>value <span class="token operator">===</span> locale<span class="token punctuation">)</span><span class="token punctuation">}</span></span>
          <span class="token attr-name">onChange</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>handleLocaleChange<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>IntlProvider</span> <span class="token attr-name">locale</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>locale<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>DataDisplay</span> <span class="token punctuation">/&gt;</span></span>
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>IntlProvider</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>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span>
</code></pre><p>In the above example, we&rsquo;ve created an order summary that displays dates, currency values and percentages. When the user selects a different locale from the dropdown, all values automatically update to match the formatting conventions of that locale. The date format changes, decimal separators adjust, currency symbols update and percentage displays follow local conventions&mdash;all without any manual formatting logic.</p><p><img src="https://www.telerik.com/sfimages/default-source/blogs/2025/2025-12//kendoreact-internationalization-interactive.gif" alt="" /></p><h2 id="wrap-up">Wrap-up</h2><p>The KendoReact Internationalization package provides an effective way to format dates and numbers according to cultural conventions. By leveraging <a target="_blank" href="https://cldr.unicode.org/">CLDR data</a> and the <code>IntlProvider</code> component, we can enable our React applications to display data in formats that feel natural to users, regardless of their location.</p><p>For more details on the KendoReact Internationalization package, including more advanced formatting options and parsing capabilities, check out the <a target="_blank" href="https://www.telerik.com/kendo-react-ui/components/intl">official documentation</a>.</p><p>Try the library for yourself:</p><p><a target="_blank" href="https://www.telerik.com/try/kendo-react-ui" class="Btn">Try KendoReact</a></p><img src="https://feeds.telerik.com/link/23056/17251907.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:2088b2d1-76ec-4de8-a350-6a48c23bfd16</id>
    <title type="text">Building ChatGPT Apps with React Components: Introducing KendoReact + Apps SDK</title>
    <summary type="text">Our new KendoReact + ChatGPT repo makes it easier to build Apps SDK experiences using KendoReact Free, a collection of free enterprise-grade components for React.</summary>
    <published>2026-01-08T19:41:59Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>Kathryn Grayson Nanz </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17249592/building-chatgpt-apps-react-components-introducing-kendoreact-apps-sdk"/>
    <content type="text"><![CDATA[<p><span class="featured">Our new KendoReact + ChatGPT repo makes it easier to build Apps SDK experiences using KendoReact Free, a collection of free enterprise-grade components for React.</span></p><p>The <a href="https://developers.openai.com/apps-sdk/" target="_blank">OpenAI Apps SDK</a> opens the door to a new category of applications: chat-native apps that combine conversation, data and UI into a single interface. But for many React developers, figuring out exactly how to get UI components to show up and behave correctly inside the Apps SDK model is a hurdle that can be difficult to jump.</p><p>If that sounds like you, then good news! Our new <a target="_blank" href="https://github.com/telerik/openai-apps-sdk-kendo-examples?tab=readme-ov-file">KendoReact + ChatGPT repo</a> makes it easier to build Apps SDK experiences using Progress KendoReact Free, a collection of free enterprise-grade components for React. </p><p>This repo exists to help remove some of that guesswork. It includes working examples using <a target="_blank" href="https://www.telerik.com/kendo-react-ui/free-react-components">KendoReact Free</a> components, so you can understand exactly how to implement them in this new context. It also includes a Node.js server setup and end-to-end patterns that you can adapt for your own Apps SDK projects.</p><h2>For Example?</h2><p>For example, one of the most challenging aspects of working with the Apps SDK is the narrow view size: the chat view space is only 768px wide. That means that any UI components a developer wants to use must already be completely responsive or adaptive, or it won&rsquo;t work well in the limited space available. </p><p>By using the KendoReact components, this is already handled! Our components have been highly responsive for years, built to work just as well on mobile as on desktop&mdash;making them a perfect match for the smaller view of the chat UI. Similarly, our components already have inbuilt interactivity with the view, meaning that clients won&rsquo;t need to do the same level of extensive prompting and testing they would with other components.</p><p>We designed this as a practical starting point: a place to see what our optimized, functional components look like in a real Apps SDK context, so you can fork the examples and use them to launch your own projects.</p><h2>Why This Matters to You</h2><p>The Apps SDK introduces a new way to build software: chat-native applications where UI elements are rendered as part of a conversational interface. KendoReact Free gives you a collection of our most popular and beloved UI components, and this repo shows how to bring them into that new environment with minimal friction. </p><p>That means that all the things you get with KendoReact Free are now available for your ChatGPT apps, including:</p><ul><li>50 extensively tested and enterprise-quality components</li><li>A professional design system created by our dedicated team</li><li>Four design themes that align with popular existing third-party design systems (Material, Fluent, Bootstrap and Kendo)</li><li>Responsive and mobile interactivity&mdash;perfect for ChatGPT&rsquo;s narrow conversation-oriented view</li><li>Easy template extensibility</li><li>A library of components that are screen-reader friendly, WCAG and Section 508 compliant, and human-tested for accessibility</li></ul><p>If you&rsquo;re experimenting with the Apps SDK, building internal tools or prototyping a new chat-first workflow, this project gives you a faster way to get hands-on.</p><h2>Try It Out!</h2><p>To get started, just clone <a target="_blank" href="https://github.com/telerik/openai-apps-sdk-kendo-examples?tab=readme-ov-file">the repo</a>, follow the setup instructions, explore the examples and adapt from there. Everything is MIT-licensed and open to use and extend.</p><p>If you have feedback, questions or ideas, open an issue or start a discussion in the repo. We look forward to seeing what you build!</p><img src="https://feeds.telerik.com/link/23056/17249592.gif" height="1" width="1"/>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:474def98-435d-4ab8-8bb0-94f20b4b6c94</id>
    <title type="text">React Basics: Thinking in React</title>
    <summary type="text">To be an effective React developer, you need to learn how to think in React, to structure your app UI component hierarchy so that the state flows in one direction.</summary>
    <published>2026-01-05T14:42:07Z</published>
    <updated>2026-07-05T18:13:58Z</updated>
    <author>
      <name>David Adeneye Abiodun </name>
    </author>
    <link rel="alternate" href="https://feeds.telerik.com/link/23056/17247213/react-basics-thinking-react"/>
    <content type="text"><![CDATA[<p><span class="featured">To be an effective React developer, you need to learn how to think in React, to structure your app UI component hierarchy so that the state flows in one direction.</span></p><p>When you&rsquo;re new to React, it&rsquo;s easy to focus on JSX, hooks, props and state. But what really unlocks the power of React isn&rsquo;t memorizing APIs. It&rsquo;s learning how to think in React.</p><p>Once you understand React&rsquo;s mental model, UI development stops feeling like a tangle of jQuery patches or DOM manipulations and instead feels like snapping together Lego pieces.</p><p>This guide walks you through how to think in React, step by step, so you can build interfaces that are clean, scalable and easy to reason about.</p><h2 id="why-‘thinking-in-react’-is-a-big-deal">Why &lsquo;Thinking in React&rsquo; Is a Big Deal</h2><p>If you&rsquo;re coming from an HTML/CSS or jQuery background, you&rsquo;re probably used to working with static templates and manually tweaking the DOM to make things interactive. React changes that completely.</p><p>In React, the UI is a direct reflection of your state. Instead of imperatively telling the DOM what to do, you simply describe how the UI should look based on the current state&mdash;and React takes care of keeping everything in sync. To make the most of this, you have to shift your mindset from &ldquo;pages and DOM updates&rdquo; to &ldquo;components and state-driven UI.&rdquo;</p><p>When building a user interface application, you&rsquo;ll usually start with two things: a mockup from the designer and some data from the backend API. For this guide, we&rsquo;ll use a simple todo app as our example.</p><p>Our todo application UI/Mockup will look like this:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/mockup-for-todo-app.png?sfvrsn=4da22ec7_2" title="Mockup for Todo App" alt="Todo UI Mock" /></p><p>And here&rsquo;s the link to the source code: <a target="_blank" href="https://codesandbox.io/p/sandbox/todo-app-in-react-zqpth8">https://codesandbox.io/p/sandbox/todo-app-in-react-zqpth8</a>.</p><p>And here&rsquo;s the kind of JSON response we&rsquo;d expect from an API (for now, we&rsquo;ll hardcode it since we don&rsquo;t have a real backend):</p><pre class=" language-json"><code class="prism  language-json"><span class="token punctuation">[</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">"Write React Article"</span><span class="token punctuation">,</span> done<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> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">"Edit React Articles"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">"Submit React Articles"</span><span class="token punctuation">,</span> done<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>
</code></pre><p>According to the React documentation, to implement a UI in React, you typically follow five steps. We will utilize these techniques throughout the process of thinking through how to build the todo application.</p><h2 id="step-1-break-the-ui-into-a-component-hierarchy">Step 1: Break the UI into a Component Hierarchy</h2><p>When you&rsquo;re preparing to build a UI in React, the best starting point is a clear design. This may come from your product designer (using Figma, Sketch, <a href="https://www.telerik.com/themebuilder" target="_blank">Progress ThemeBuilder</a> or similar), or you might create it yourself as a UX engineer. Either way, your primary task is to decompose that design into logical, reusable components.</p><p>Let&rsquo;s take our todo application as an example. The simplest way to begin is by outlining each component and subcomponent in the mockup and clearly labeling them. Many design tools streamline this by enabling designers to name layers or components directly in the file.</p><p>As you break down the design, keep these perspectives in mind:</p><ul><li><strong>Programming perspective:</strong> Think in terms of the single responsibility principle. A component should ideally do one thing well. If it starts growing beyond that, break it into smaller subcomponents.</li><li><strong>CSS perspective:</strong> Imagine what you&rsquo;d normally assign a class selector to. Components often map to those same chunks of UI, though not always as granular.</li><li><strong>Design perspective:</strong> Examine how layers are grouped and organized within the design file. That structure often hints at where components should be split.</li></ul><p>In many cases, the structure of your backend data will also influence your component breakdown. Well-structured APIs often map neatly onto the UI&mdash;both follow the same information architecture. Break your UI into components so that each one corresponds to a specific part of your data model.</p><p>For our todo application, here&rsquo;s a breakdown of six main components:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/todo-app-components.png?sfvrsn=7abcd6a4_2" title="Todo App Components breakdown" alt="Todo App Components" /></p><ol><li><code>TodoApplicationTable</code> (yellow): Wraps the entire app.</li><li><code>TodoFormSection</code> (green): Handles user input for adding todos.</li><li><code>TodosTable</code> (blue): Displays the list of todos along with related actions.</li><li><code>TodosCountSection</code> (orange): Shows the total number of todos.</li><li><code>TodoItem</code> (purple): Represents each individual todo and its actions.</li><li><code>FilterTodosTable</code> (red): Provides controls for filtering the todo list.</li></ol><p>One thing to note: Inside <code>TodosTable</code> (blue), the <code>TodoCountSection</code> isn&rsquo;t broken out as a separate component. That&rsquo;s a design choice. It works fine to keep it in line for now, but if the header grows more complex later, you&rsquo;ll likely want to extract it into its own <code>TodosTableHeader</code> component.</p><p>Once you&rsquo;ve identified the pieces, the next step is to arrange them into a hierarchy. Components that appear inside others in the mockup should be children in your component tree. For our app, that looks like this:</p><pre><code>TodoApplicationTable
  ├── TodoFormSection
  └── TodosTable
       ├── TodoCountSection
       ├── TodoItem
  └── FilterTodosTable
</code></pre><p>And that&rsquo;s it for Step 1. You&rsquo;ve successfully mapped your design into a clear component hierarchy.</p><h2 id="step-2-build-a-static-version-in-react">Step 2: Build a Static Version in React</h2><p>With your component hierarchy mapped out, the next step is to start coding. Before adding any interactivity, it&rsquo;s best to build a static version of the UI&mdash;one that renders the correct structure and data but doesn&rsquo;t handle any changes yet.</p><p>This step keeps things simple. Writing the static version is mostly about composition: putting components together and passing data down through <code>props</code>. Interactivity and state management come later, since those usually involve more thinking than typing.</p><p>At this stage, avoid using the state entirely. <strong>State is for dynamic, changing data.</strong> Since we&rsquo;re only building the static skeleton, <code>props</code> are all you need. Parent components pass data to children, keeping your UI predictable and consistent.</p><p>You can approach this in two ways:</p><ul><li><strong>Top-down:</strong> Start with higher-level components such as <code>TodoApplicationTable</code> and work your way down.</li><li><strong>Bottom-up:</strong> Start small with leaf components such as <code>FilterTodoTable</code> and build upward.</li></ul><p>For simple apps, top-down usually feels faster. For bigger projects, a bottom-up approach often helps keep things organized.</p><p>Here&rsquo;s a static version of our todo application:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">import</span> <span class="token string">"./styles.css"</span><span class="token punctuation">;</span>

<span class="token keyword">const</span> initialTodos <span class="token operator">=</span> <span class="token punctuation">[</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">"Write React Article"</span><span class="token punctuation">,</span> done<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> id<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">"Edit React Articles"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">{</span> id<span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string">"Submit React Articles"</span><span class="token punctuation">,</span> done<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">;</span>

<span class="token keyword">function</span> <span class="token function">App</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 operator">&lt;</span>div className<span class="token operator">=</span><span class="token string">"todoapp stack-large"</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>h1 className<span class="token operator">=</span><span class="token string">"header"</span><span class="token operator">&gt;</span>Todo App<span class="token operator">&lt;</span><span class="token operator">/</span>h1<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>TodoForm <span class="token operator">/</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>TodoList todos<span class="token operator">=</span><span class="token punctuation">{</span>initialTodos<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>FilterTodo <span class="token operator">/</span><span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">default</span> App<span class="token punctuation">;</span>


<span class="token keyword">function</span> <span class="token function">TodoForm</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 operator">&lt;</span>form<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>input
        type<span class="token operator">=</span><span class="token string">"text"</span>
        id<span class="token operator">=</span><span class="token string">"new-todo-input"</span>
        placeholder<span class="token operator">=</span><span class="token string">"What needs to be done?"</span>
        className<span class="token operator">=</span><span class="token string">"input input__lg"</span>
        name<span class="token operator">=</span><span class="token string">"text"</span>
        autoComplete<span class="token operator">=</span><span class="token string">"off"</span>
      <span class="token operator">/</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>button type<span class="token operator">=</span><span class="token string">"submit"</span> className<span class="token operator">=</span><span class="token string">"btn btn__primary btn__lg"</span><span class="token operator">&gt;</span>
        Add
      <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>form<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>


<span class="token keyword">function</span> <span class="token function">TodoList</span><span class="token punctuation">(</span><span class="token punctuation">{</span> todos <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 operator">&lt;</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>h2 id<span class="token operator">=</span><span class="token string">"list-heading"</span><span class="token operator">&gt;</span><span class="token punctuation">{</span>todos<span class="token punctuation">.</span>length<span class="token punctuation">}</span> tasks remaining<span class="token operator">&lt;</span><span class="token operator">/</span>h2<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>ul role<span class="token operator">=</span><span class="token string">"list"</span> className<span class="token operator">=</span><span class="token string">"todo-list stack-large todo-exception"</span><span class="token operator">&gt;</span>
        <span class="token punctuation">{</span>todos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>todo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span>
          <span class="token operator">&lt;</span>li key<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<span class="token punctuation">}</span> className<span class="token operator">=</span><span class="token string">"todo stack-small"</span><span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span>div className<span class="token operator">=</span><span class="token string">"todo-cb"</span><span class="token operator">&gt;</span>
              <span class="token operator">&lt;</span>input id<span class="token operator">=</span><span class="token punctuation">{</span><span class="token template-string"><span class="token string">`todo-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>todo<span class="token punctuation">.</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">}</span> type<span class="token operator">=</span><span class="token string">"checkbox"</span> defaultChecked<span class="token operator">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>done<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
              <span class="token operator">&lt;</span>label className<span class="token operator">=</span><span class="token string">"todo-label"</span> htmlFor<span class="token operator">=</span><span class="token punctuation">{</span><span class="token template-string"><span class="token string">`todo-</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>todo<span class="token punctuation">.</span>id<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">}</span><span class="token operator">&gt;</span>
                <span class="token punctuation">{</span>todo<span class="token punctuation">.</span>title<span class="token punctuation">}</span>
              <span class="token operator">&lt;</span><span class="token operator">/</span>label<span class="token operator">&gt;</span>
              <span class="token operator">&lt;</span>button type<span class="token operator">=</span><span class="token string">"button"</span> className<span class="token operator">=</span><span class="token string">"btn"</span><span class="token operator">&gt;</span>
                Edit <span class="token operator">&lt;</span>span className<span class="token operator">=</span><span class="token string">"visually-hidden"</span><span class="token operator">&gt;</span>Edit<span class="token operator">&lt;</span><span class="token operator">/</span>span<span class="token operator">&gt;</span>
              <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
              <span class="token operator">&lt;</span>button type<span class="token operator">=</span><span class="token string">"button"</span> className<span class="token operator">=</span><span class="token string">"btn btn__danger"</span><span class="token operator">&gt;</span>
                Delete <span class="token operator">&lt;</span>span className<span class="token operator">=</span><span class="token string">"visually-hidden"</span><span class="token operator">&gt;</span>Delete<span class="token operator">&lt;</span><span class="token operator">/</span>span<span class="token operator">&gt;</span>
              <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
            <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
          <span class="token operator">&lt;</span><span class="token operator">/</span>li<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">&lt;</span><span class="token operator">/</span>ul<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span><span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>


<span class="token keyword">function</span> <span class="token function">FilterTodo</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 operator">&lt;</span>div className<span class="token operator">=</span><span class="token string">"filters btn-group todo-exception"</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>button type<span class="token operator">=</span><span class="token string">"button"</span> className<span class="token operator">=</span><span class="token string">"btn toggle-btn"</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>span<span class="token operator">&gt;</span>All<span class="token operator">&lt;</span><span class="token operator">/</span>span<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>button type<span class="token operator">=</span><span class="token string">"button"</span> className<span class="token operator">=</span><span class="token string">"btn toggle-btn"</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>span<span class="token operator">&gt;</span>Active<span class="token operator">&lt;</span><span class="token operator">/</span>span<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>button type<span class="token operator">=</span><span class="token string">"button"</span> className<span class="token operator">=</span><span class="token string">"btn toggle-btn"</span><span class="token operator">&gt;</span>
        <span class="token operator">&lt;</span>span<span class="token operator">&gt;</span>Completed<span class="token operator">&lt;</span><span class="token operator">/</span>span<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>At this point, each component is reusable and renders part of the data model. Notice how <code>TodoList</code> receives the list of todos as a <code>prop</code> and passes it down the tree. This is React&rsquo;s <strong>one-way data flow</strong> in action: data always moves from parent to child, top to bottom.</p><p>No state yet&mdash;that comes in the next step when we make the app interactive.</p><h2 id="step-3-identify-the-minimal-but-complete-representation-of-ui-state">Step 3: Identify the Minimal (but Complete) Representation of UI State</h2><p>Now comes the key question: what actually changes in this UI?</p><p>To make your app interactive, you need to let users update the underlying data model. That&rsquo;s where the state comes in. State is the minimal set of data your app needs to remember over time. The golden rule: keep your state <a target="_blank" href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY (Don&rsquo;t Repeat Yourself)</a>. Store only what&rsquo;s essential and derive everything else from it when needed.</p><p>For example, in our todo app, you definitely need to store the list of todos as an array. But if you also want to display the total number of todos, you don&rsquo;t keep that number in state&mdash;you just calculate it from <code>todos.length</code>.</p><p>Now, let&rsquo;s apply this thinking to our todo application demo example. Here are the key pieces of data:</p><ul><li>The original list of todos</li><li>The input text entered by the user</li><li>The value of the checkbox</li><li>The filtered list of todos</li><li>The total number of todos</li><li>The value of the todo currently being edited</li></ul><p>Which of this should be state? To figure that out, apply three simple questions to each piece:</p><ol><li><strong>Does it change over time?</strong> If no, it&rsquo;s not state.</li><li><strong>Is it passed in from a parent via props?</strong> If yes, it&rsquo;s not state.</li><li><strong>Can it be derived from existing state or props?</strong> If yes, it&rsquo;s not state.</li></ol><p>What&rsquo;s left is probably state. Let&rsquo;s go through them one by one again:</p><ul><li><strong>The original list of todos</strong> &rarr; comes in via props, not state.</li><li><strong>The input text</strong> &rarr; changes over time, so yes, it&rsquo;s state.</li><li><strong>Checkbox value</strong> &rarr; can be derived from the todos array, not state.</li><li><strong>Filtered list of todos</strong> &rarr; depends on user actions and changes over time, so yes, it&rsquo;s state.</li><li><strong>The total number of todos</strong> &rarr; can be computed from <code>todos.length</code>, not state.</li><li><strong>The edited todo value</strong> &rarr; changes over time, so yes, it&rsquo;s state.</li></ul><p>That leaves us with three pieces of state. Everything else can (and should) be derived from these. Keeping your state minimal and explicit makes your app easier to reason about, debug and maintain.</p><h2 id="step-4-identify-where-your-state-should-live">Step 4: Identify Where Your State Should Live</h2><p>Once you&rsquo;ve identified the minimal pieces of state your app needs, the next step is figuring out which component should own that state. In React, data flows in one direction, from parent to child, so placing state in the right spot makes everything easier to manage.</p><p>If you&rsquo;re new to this, it might feel a bit tricky, but you can work it out by following this process:</p><ol><li>For each piece of state, list all the components that depend on it.</li><li>Find their closest common parent in the components tree.</li><li>Decides the closest common parent in the component tree:<br />a. Most of the time, you&rsquo;ll put it in that common parent.<br />b. Sometimes, it makes sense to lift the state even higher.<br />c. If no existing component feels right, create a new one just to hold the state and place it above the common parent.</li></ol><p>This helps to manage the state at the right level, so data can flow down as props to all components that need it.</p><p>In the previous steps, you will have noticed we have three pieces of state in the todo application: the added todo text, the value of the edited todo and the filtered todo list. In this example, the state of todos is what&rsquo;s common to every other component.</p><p>So, let&rsquo;s go through our strategy for them:</p><ol><li>Identify components that use state:</li></ol><ul><li><code>TodosTable</code> needs to list todos based on the state of (added todo text, checkbox value, editing and filtering options).</li><li><code>TodoFormSection</code> needs to get the state of the title of the todo that&rsquo;s been added.</li><li><code>FilterTodosTable</code> needs to display the state of the filtering.</li></ul><ol start="2"><li><p>Find their common parent: The first component both components share is the <code>TodoApplicationTable</code> which is the App component that housed all the components.</p></li><li><p>Decide where the state lives: We&rsquo;ll keep the states common state to all, which is the <code>todos</code> state and the <code>filtering</code> state, in the parent component. The new todo input state will be kept in the <code>TodoFormSection</code> component. In our code demo, we named the component <code>TodoForm.js</code>.</p></li></ol><p>Then add state to the component with the <a target="_blank" href="https://react.dev/reference/react/useState"><code>useState()</code> hook</a>. Hooks are a special function that enable you to &ldquo;hook into&rdquo; React. Add two state variables at the top of the <code>App</code> component which we can also refer to as <code>TodoApplicationTable</code>, and specify their initial state.</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> setTodos<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span>initialTodos<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token punctuation">[</span>filter<span class="token punctuation">,</span> setFilter<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">"All"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Remember, we&rsquo;re using the initial mock data first. So we set the <code>todos</code> state to that data before adding any user-created items to the list. For filtering, the default state is set to <code>All</code>, which is why we&rsquo;re using <code>All</code> as the initial value.</p><p>Then pass the <code>todos</code> as props to the <code>TodoList</code> components as a prop :</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>TodoList</span> <span class="token attr-name">todos</span><span class="token attr-value"><span class="token punctuation">=</span>{todos}</span> <span class="token punctuation">/&gt;</span></span>
</code></pre><p>For the next state to add todo from user input, open the <code>TodoForm.js</code> component, which can also be referred to as the <code>TodoFormSection</code>, and add one state variable at the top of the component:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> <span class="token punctuation">[</span>title<span class="token punctuation">,</span> setTitle<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The next state is for editing the todo, also add one state variable at the top level of <code>Todo</code> components, which <code>TodoList</code> serves as a parent component too:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">const</span> <span class="token punctuation">[</span>isEditing<span class="token punctuation">,</span> setIsEditing<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>At this point, you haven&rsquo;t added any code to respond to user actions such as typing yet. This will be the final step.</p><h2 id="step-5-add-inverse-data-flow">Step 5: Add Inverse Data Flow</h2><p>Now it&rsquo;s time to wire up interactivity. At this point, our app renders correctly, and state flows down from parent to child. But React apps aren&rsquo;t just about displaying data&mdash;they need to respond to user input. This is where <strong>inverse data flow</strong> comes in.</p><p>React makes this process explicit, which means a little more typing compared to traditional two-way data binding. If you try typing in the input or toggling the checkbox in the example, nothing happens. That&rsquo;s intentional.</p><p>The goal is to update state whenever the user interacts with the form inputs. Since the todos state lives in the main parent component, only it can set the state of the todo. So for the <code>TodoForm</code> component to update the todos state whenever a user input a new todo, we need to pass the function that add the new todo to the lists of the todo to the <code>TodoFrom</code> components:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">TodoApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>todos<span class="token punctuation">,</span> setTodos<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span>initialTodos<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">function</span> <span class="token function">handleAddTodo</span><span class="token punctuation">(</span>title<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token function">setTodos</span><span class="token punctuation">(</span><span class="token punctuation">[</span>
      <span class="token operator">...</span>todos<span class="token punctuation">,</span>
      <span class="token punctuation">{</span>
        id<span class="token punctuation">:</span> nextId<span class="token operator">++</span><span class="token punctuation">,</span>
        title<span class="token punctuation">:</span> title<span class="token punctuation">,</span>
        done<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
    <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
&hellip;
<span class="token keyword">return</span> <span class="token punctuation">(</span>
    <span class="token operator">&lt;</span>div className<span class="token operator">=</span><span class="token string">"todoapp stack-large"</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>h1 className<span class="token operator">=</span><span class="token string">"header"</span><span class="token operator">&gt;</span>Todo App<span class="token operator">&lt;</span><span class="token operator">/</span>h1<span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>TodoForm onAddTodo<span class="token operator">=</span><span class="token punctuation">{</span>handleAddTodo<span class="token punctuation">}</span> <span class="token operator">/</span><span class="token operator">&gt;</span>
      
     &hellip;<span class="token punctuation">.</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>div<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Then in your <code>TodoForm</code> add the the state and function to receive user input and call <code>onAddTodo</code> whenever user submits:</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">function</span> <span class="token function">TodoForm</span><span class="token punctuation">(</span><span class="token punctuation">{</span> onAddTodo <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>title<span class="token punctuation">,</span> setTitle<span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token function">useState</span><span class="token punctuation">(</span><span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> <span class="token function-variable function">handleSubmit</span> <span class="token operator">=</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// prevent page refresh</span>
    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>title<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token comment">// optional: avoid empty todos</span>
    <span class="token function">onAddTodo</span><span class="token punctuation">(</span>title<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">setTitle</span><span class="token punctuation">(</span><span class="token string">""</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 operator">&lt;</span>form onSubmit<span class="token operator">=</span><span class="token punctuation">{</span>handleSubmit<span class="token punctuation">}</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>input
        type<span class="token operator">=</span><span class="token string">"text"</span>
        id<span class="token operator">=</span><span class="token string">"new-todo-input"</span>
        placeholder<span class="token operator">=</span><span class="token string">"What needs to be done?"</span>
        className<span class="token operator">=</span><span class="token string">"input input__lg"</span>
        name<span class="token operator">=</span><span class="token string">"text"</span>
        autoComplete<span class="token operator">=</span><span class="token string">"off"</span>
        value<span class="token operator">=</span><span class="token punctuation">{</span>title<span class="token punctuation">}</span>
        onChange<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function">setTitle</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span><span class="token punctuation">}</span>
      <span class="token operator">/</span><span class="token operator">&gt;</span>
      <span class="token operator">&lt;</span>button type<span class="token operator">=</span><span class="token string">"submit"</span> className<span class="token operator">=</span><span class="token string">"btn btn__primary btn__lg"</span><span class="token operator">&gt;</span>
        Add
      <span class="token operator">&lt;</span><span class="token operator">/</span>button<span class="token operator">&gt;</span>
    <span class="token operator">&lt;</span><span class="token operator">/</span>form<span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>We follow the same pattern for editing and toggling: App owns the state, defines an updater and passes it down.</p><pre class=" language-js"><code class="prism  language-js"><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">TodoApp</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
   <span class="token comment">// &hellip;</span>
    <span class="token keyword">function</span> <span class="token function">handleChangeTodo</span><span class="token punctuation">(</span>nextTodo<span class="token punctuation">)</span> <span class="token punctuation">{</span>
       <span class="token function">setTodos</span><span class="token punctuation">(</span>
          todos<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
          <span class="token keyword">if</span> <span class="token punctuation">(</span>t<span class="token punctuation">.</span>id <span class="token operator">===</span> nextTodo<span class="token punctuation">.</span>id<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          <span class="token keyword">return</span> nextTodo<span class="token punctuation">;</span>
     <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
          <span class="token keyword">return</span> t<span class="token punctuation">;</span>
        <span class="token punctuation">}</span>
     <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">)</span><span class="token punctuation">;</span>
   <span class="token keyword">function</span> <span class="token function">handleDeleteTodo</span><span class="token punctuation">(</span>todoId<span class="token punctuation">)</span> <span class="token punctuation">{</span>
       <span class="token function">setTodos</span><span class="token punctuation">(</span>todos<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> t<span class="token punctuation">.</span>id <span class="token operator">!==</span> todoId<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 operator">&lt;</span>TodoList
      todos<span class="token operator">=</span><span class="token punctuation">{</span>todos<span class="token punctuation">}</span>
      onChangeTodo<span class="token operator">=</span><span class="token punctuation">{</span>handleChangeTodo<span class="token punctuation">}</span>
      onDeleteTodo<span class="token operator">=</span><span class="token punctuation">{</span>handleDeleteTodo<span class="token punctuation">}</span>
    <span class="token operator">/</span><span class="token operator">&gt;</span>
  <span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Inside <code>TodoItem</code>, we call these functions when the user edits text, toggles a checkbox or deletes a task.</p><p>Repeat this process for the other components, adding inverse data flow with state and interactivity as needed. If you&rsquo;re new to React, we&rsquo;ll walk you through building this todo application from scratch in blog post <a target="_blank" href="https://www.telerik.com" data-sf-ec-immutable="">React Basics: Interactivity and State</a>.</p><p>Here&rsquo;s the full code: <a target="_blank" href="https://codesandbox.io/p/sandbox/todo-app-in-react-zqpth8">https://codesandbox.io/p/sandbox/todo-app-in-react-zqpth8</a></p><p>Now the application is fully working:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2025/2025-11/todo-app-demo.gif?sfvrsn=19691eb2_2" title="Demo of a Todo App" alt="Todo App Demo gif" /></p><h2 id="conclusion">Conclusion</h2><p>&ldquo;Thinking in React&rdquo; is less about coding and more about adopting a new way of seeing UIs. You&rsquo;re no longer shuffling DOM nodes by hand&mdash;you&rsquo;re structuring your app as a hierarchy of components, each powered by state flowing in one direction. Once you grasp that, scaling your application feels far less intimidating because every piece has its place.</p><p>In this walk-through, we use a todo app to put React&rsquo;s five-step process into practice: breaking down the UI, building a static version, identifying state, deciding where it belongs and implementing inverse data flow.</p><p>With this approach, you&rsquo;re not just writing React, you&rsquo;re <strong>thinking</strong> in React. And that mindset shift is what turns a beginner into a confident React developer.</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">Turn Prompts into Pages: Telerik Agentic UI Generator</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Accelerate enterprise UI delivery with orchestrated, <a target="_blank" href="https://www.telerik.com/blogs/turn-prompts-pages-telerik-agentic-ui-generator">prompt-driven tools</a>.</p></div></div></aside><img src="https://feeds.telerik.com/link/23056/17247213.gif" height="1" width="1"/>]]></content>
  </entry>
</feed>
