<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~files/atom-premium.xsl"?>
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedpress="https://feed.press/xmlns" xmlns:media="http://search.yahoo.com/mrss/" xmlns:podcast="https://podcastindex.org/namespace/1.0">
  <feedpress:locale>en</feedpress:locale>
  <feedpress:newsletterId>telerik-blogs</feedpress:newsletterId>
  <link rel="hub" href="https://feedpress.superfeedr.com/"/>
  <logo>https://static.feedpress.com/logo/telerik-blogs-5aafd3c47efc3.jpg</logo>
  <title type="text">Telerik Blogs</title>
  <subtitle type="text">The official blog of Progress Telerik - expert articles and tutorials for developers.</subtitle>
  <id>uuid:0b4b1b1d-c796-4783-9add-608768fdb5a3;id=1458</id>
  <updated>2026-07-20T16:30:05Z</updated>
  <link rel="alternate" href="https://www.telerik.com/"/>
  <link rel="self" type="application/atom+xml" href="https://feeds.telerik.com/blogs"/>
  <entry>
    <id>urn:uuid:46694ffa-0562-42b4-b802-c7ce5cd162bd</id>
    <title type="text">The Benefits of Building Emotional Intelligence as a Web Designer</title>
    <summary type="text">You know about book smarts and street smarts. But what about emotional intelligence? Let’s examine this other type of “smarts” and how it can be especially beneficial for web designers to have.</summary>
    <published>2026-07-17T14:39:20Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Suzanne Scacca </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/benefits-building-emotional-intelligence-web-designer"/>
    <content type="text"><![CDATA[<p><span class="featured">You know about book smarts and street smarts. But what about emotional intelligence? Let&rsquo;s examine this other type of &ldquo;smarts&rdquo; and how it can be especially beneficial for web designers to have.</span></p><p>Emotional intelligence (EI) refers to one&rsquo;s ability to understand and manage emotions&mdash;not just their own, but those of the people around them.</p><p>A high emotional intelligence can be incredibly beneficial for web designers. For starters, honing your EI can help you work more effectively with others. Also, it can enable you to create more effective digital user experiences.</p><p>In this post, I&rsquo;m going to share some tips on how designers can improve their work relationships with coworkers, clients and collaborators through emotional intelligence. I&rsquo;ve also included info on how to use this skill to create more emotionally intelligent designs.</p><h2 id="what-does-emotional-intelligence-look-like">What Does Emotional Intelligence Look Like?</h2><p>There are two sides to emotional intelligence. First, let&rsquo;s look at what emotional intelligence looks like when we turn it inward:</p><table><style>table,
 th,
    td {
      border: 1px;
      border-color: #bdbdba;
      border-style: dotted;
      border-collapse: collapse;
      margin-right: auto;
      padding: 0in 5.4pt 0in 5.4pt;
      text-align: left;
    }
  </style>
 <thead><tr><th><strong>Self-Awareness</strong></th><th><strong>Self-Regulation</strong></th></tr></thead><tbody><tr><td>This means you&rsquo;re able to recognize how you are feeling and why. If you can name and understand an emotion, that&rsquo;s the first step to controlling it.</td><td>There&rsquo;s nothing wrong with feeling extreme emotions. However, an emotionally intelligent person will be able to down-regulate before they get out of hand.</td></tr></tbody></table><br /><p>Next, let&rsquo;s look at emotional intelligence turned outward:</p><table><style>table,
 th,
    td {
      border: 1px;
      border-color: #bdbdba;
      border-style: dotted;
      border-collapse: collapse;
      margin-right: auto;
      padding: 0in 5.4pt 0in 5.4pt;
      text-align: left;
    }
  </style>
 <thead><tr><th><strong>Social Awareness</strong></th><th><strong>Relationship Management</strong></th></tr></thead><tbody><tr><td><p>This means you&rsquo;re able to recognize how others are feeling. Furthermore, you can empathize with them and understand why they feel that way.</p><p>Unlike self awareness which comes from looking inward, you have to be able to read external cues from others to demonstrate social awareness&mdash;like body language, tone of voice and actions. You cannot control the emotions of anyone but yourself. That said, you can adapt your own behaviors and reactions based on how someone else is feeling.</p></td><td><p>In doing so, you can potentially affect the relationship and/or the outcomes of the interaction. It requires a good deal of empathy and critical thinking in order to achieve long-lasting and positive results.</p><p>In order to turn your attention outward, you&rsquo;ll first need to master your own emotional awareness and regulation.</p></td></tr></tbody></table><br /><h2 id="improving-your-work-relationships-with-emotional-intelligence">Improving Your Work Relationships with Emotional Intelligence</h2><p>Here are some ways in which you might benefit from utilizing emotional intelligence in the workplace:</p><ul><li>Resolve conflicts speedily and equitably</li><li>Reduce your own stress and prevent burnout</li><li>Contribute to a healthy, supportive work environment</li><li>Improve productivity and collaboration with coworkers</li><li>Improve the client&rsquo;s experience and build trust and loyalty in the process</li><li>Develop a positive relationship with superiors and pave the way for greater responsibility and job opportunities</li></ul><p>Here are some ways you can use emotional intelligence to improve your relationships at work.</p><h2 id="hit-the-pause-button-before-reacting">Hit the Pause Button Before Reacting</h2><p>Something happens that makes you want to react.</p><p>A client says they don&rsquo;t like your designs for the second time without giving any constructive feedback. You get scheduled for a third meeting today despite your manager knowing you&rsquo;re under a time crunch. The account manager didn&rsquo;t give you all the details about a prospect and so you misspoke during the initial call.</p><p>Your body and mind may be screaming at you to react. You feel stuck.</p><p>If you react emotionally and right away, there could be negative consequences. But if you don&rsquo;t do anything about it, you could be stuck with your emotions running high with nowhere for them to go.</p><p>To navigate something like this, you need to be able to pause and reflect on the situation objectively. This will give you a chance to slow down your emotions, consider what led to the situation from the other person&rsquo;s point of view, and then come up with a logical and grounded resolution.</p><p>This also can be a valuable learning opportunity.</p><p>We all receive criticism at some point in our design careers. We all make mistakes, too. In some cases, our egos want to lash out or reject any responsibility for the situation.</p><p>With emotional intelligence, we teach our inner ego not to feel personally attacked or demoralized. Instead, we accept the role we played in the situation and calmly navigate it with the person who has confronted us.</p><h2 id="talk-things-out">Talk Things Out</h2><p>I think one of the biggest problems that came about as a result of virtual work is the growing disconnection we feel from others. It&rsquo;s all too easy to receive an email or request and to misinterpret its intent.</p><p>This can lead to:</p><ul><li>Guessing or assuming what the person meant, and then running with your own mistaken conclusions</li><li>Allowing a (possibly perceived) negative experience to fester and then it turning into something bigger than it needed to be</li><li>Feeling frustrated that someone isn&rsquo;t getting what you&rsquo;re saying, leaving you to feel as if you have to just do it yourself</li></ul><p>In reality, a five-minute phone or Zoom call could have avoided all of this.</p><p>While I&rsquo;m partially laying the blame at the foot of digital communication, poor communication skills in general can create these problems as well.</p><p>So, it&rsquo;s not just important that you&rsquo;re able to talk things out with others. You need to be able to listen and really hear what they&rsquo;re saying. This is where social awareness comes into play.</p><p>For example, let&rsquo;s say a junior designer was just hired at your agency. You&rsquo;ve given them a bunch of tasks to do. You ask them, &ldquo;Are you good with all this?&rdquo; And their answer is &hellip;</p><p>&ldquo;Yes? Yes. I think so.&rdquo;</p><p>You heard the word &ldquo;yes,&rdquo; so that must mean they&rsquo;re good. Now onto the pile of work waiting for you.</p><p>Is that really what they were saying though? While we expect everyone to be responsible for the tasks they assume, this new hire may have just been too nervous to say &ldquo;no.&rdquo; Their first &ldquo;yes&rdquo; sounded more like a question than an affirmative. And perhaps their body language suggested they were feeling anxious.</p><p>So, be careful not to accept certain buzzwords as the truth. Listen and then talk it out if you feel as though there&rsquo;s a misunderstanding or misalignment between you and someone else.</p><h2 id="be-honest-with-yourself-and-others">Be Honest with Yourself and Others</h2><p>Honesty is a big thing in emotional intelligence. You can see in the example above how dishonesty (no matter how innocent it may seem) can create a lot of problems for a web designer or their team.</p><p>When working in collaborative environments&mdash;with coworkers or with clients&mdash;honesty is critical. And it starts with yourself.</p><p>Rather than allowing your ego, fear or other emotions to make decisions for you, have an honest conversation with yourself first.</p><p>Let&rsquo;s say your boss is really pleased with the job you&rsquo;ve been doing as a senior web designer. They would like you to move into a creative director role by year&rsquo;s end. The job comes with much longer hours. You&rsquo;ll also have to complete a few courses between now and then with certifying exams.</p><p>You&rsquo;re excited about the promotion and the increase in pay. In the moment, you want to accept the job, agree to whatever they want and immediately share the news with your partner.</p><p>Instead, you ask them for 48 hours to think about it. It&rsquo;s not that you&rsquo;re <em>not</em> thrilled about the opportunity, but there are some things to think through.</p><p>For starters, can you manage to commit more hours a week? Your partner is already stressed about how much time you work. Plus, the two of you were talking about having kids someday. Would that kind of schedule really be fair to all of you?</p><p>What&rsquo;s more, do you even want to be a creative director? Will you find the work as fulfilling as what you do now? Does money matter more than job satisfaction or fulfillment?</p><p>An emotionally intelligent person wouldn&rsquo;t betray themselves by making a hasty decision or one in which they have doubts. They would consider the matter from all angles, and then be honest about their needs, desires, limitations and so on with others.</p><h2 id="applying-emotional-intelligence-to-your-web-design-work">Applying Emotional Intelligence to Your Web Design Work</h2><p>Once you&rsquo;ve learned how to wield your emotional intelligence on a personal level, you can leverage this skill to create emotionally intelligent digital experiences.</p><p>Let&rsquo;s go back to the idea of emotional intelligence turned outward.</p><p>On one side of the coin, there is social awareness. This looks like:</p><ul><li>Recognizing how other people feel</li><li>Empathizing with their situation</li><li>Owning your role in the situation</li></ul><p>On the other side is relationship management. This looks like:</p><ul><li>Providing a stable and consistent response</li><li>Adapting your behaviors and reactions to other people and situations</li><li>Being a beacon of responsibility and dependability, so that others feel safe in trusting you</li></ul><p>While we can use tools like Google Analytics to tell us what happens on our websites or apps, they don&rsquo;t help us understand the users&rsquo; <em>emotional</em> journey. And without that empathy or understanding, we can&rsquo;t create digital experiences that are consistent, adaptable, helpful, trustworthy and ultimately satisfying.</p><p>To create these types of experiences, you can use:</p><h3 id="user-psychology">User Psychology</h3><p>User psychology can be a useful first step in emotionally intelligent design. For instance, you can use <a target="_blank" href="https://www.progress.com/blogs/using-color-psychology-education-web-design">psychology to help you choose colors</a> for your digital experience. It&rsquo;s a helpful tool, for sure, but it can&rsquo;t be the only one you use as it really only gives us insights about a generic &ldquo;audience.&rdquo;</p><h3 id="user-research-and-testing">User Research and Testing</h3><p><a target="_blank" href="https://www.progress.com/blogs/user-research-methods-what-which-ones-why">User research methods</a> like surveys, interviews, focus groups and usability tests give us invaluable insights about users. Instead of seeing users as IP addresses that interact with certain pages or screens, they become real people that we&rsquo;re serving.</p><p>With this information, you can go on to develop user personas, empathy maps, user journey maps and content strategies built around your target users&rsquo; needs, preferences and challenges.</p><h3 id="feedback-collection">Feedback Collection</h3><p>Once your product is live, it&rsquo;s important to <a target="_blank" href="https://www.telerik.com/blogs/why-digital-products-should-always-solicit-feedback-bug-reports-users">solicit feedback from your users</a> from time to time. The feedback could pertain to their experience with:</p><ul><li>Using the website or app</li><li>Contacting support</li><li>Something they purchased</li></ul><p>Feedback isn&rsquo;t always constructive. Look at any reviews page online and you&rsquo;ll see people leave one-star reviews with no comments. But if enough people feel passionately enough about an experience, you&rsquo;ll start getting some really valuable input about what your users like and don&rsquo;t like.</p><h3 id="iterative-design">Iterative Design</h3><p>In an ideal world, we&rsquo;d start every web project off with a <a target="_blank" href="https://www.progress.com/blogs/why-you-cant-have-empathetic-design-without-mvp">minimum viable product</a>. This would allow us to deliver just what our users need without wasting too much time on things they might not.</p><p>Websites, apps and omnichannel digital experiences can and should continually be improved upon. Whether you&rsquo;re able to begin all your projects as MVPs or not, iterative design should be part of your process.</p><p>When we use critical thinking, empathy and active listening, iterative design goes beyond the superficial. It allows us to really understand our users on an emotional level and to enhance their experiences based on what we learn about them over time.</p><h2 id="wrapping-up">Wrapping Up</h2><p>Emotional intelligence is an incredibly valuable soft skill to have as a web designer. Not only can it make things go more smoothly for you at work, it also gives you the ability to design from a more empathetic lens.</p><p>Instead of navigating your job as a checklist of things to do, you start to see how your choices and actions affect others&mdash;both positively and negatively.</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">The Importance of Empathy in Automation</h4></div><div class="col-8"><p class="u-fs16 u-mb0">To take AI beyond automation into true intelligence, we need to <a target="_blank" href="https://www.telerik.com/blogs/importance-empathy-automation">include empathy.</a>.</p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:d13dae8a-acb0-47b9-b3ce-42c9cac5ec96</id>
    <title type="text">Question Everything: Why the Question Matters More Than the Answer</title>
    <summary type="text">Questions unlock better solutions. “How might we” is a powerful divergent thinking tool, especially when AI defaults to answers.</summary>
    <published>2026-07-15T20:26:12Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Teon Beijl </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/question-everything-why-question-matters-more-than-answer"/>
    <content type="text"><![CDATA[<p><span class="featured">Questions unlock better solutions. &ldquo;How might we&rdquo; is a powerful divergent thinking tool, especially when AI defaults to answers.</span></p><p>When my daughter spills her drink, she comes to me to tell me it happened.</p><p>In that moment, I have two options:</p><ul><li>Act: &ldquo;Go grab a towel and clean it up.&rdquo;</li><li>Ask: &ldquo;How do you think we can fix that?&rdquo;</li></ul><p>The first gets the mess cleaned the fastest. The second takes longer but lets her think.</p><p>She grabs paper towels, maybe uses her shirt. She asks for help, or she figures it out herself. Either way, she&rsquo;s not just following instructions. She&rsquo;s solving.</p><p>It&rsquo;s a small moment, but it captures something important about how we approach problems.</p><p>When we turn a problem into a question, something shifts. Our brains respond differently to questions than to instructions. We get more creative and more motivated. We find solutions we wouldn&rsquo;t have reached if we&rsquo;d just executed.</p><h2 id="the-push-for-execution">The Push for Execution</h2><p>In fast-moving product teams, that shift doesn&rsquo;t always happen naturally. We&rsquo;re under pressure with tight deadlines, sprints and bugs to fix. There&rsquo;s a push for execution. The problem gets defined, and a solution shipped. This is efficient but not necessarily effective.</p><p>I see it with clients too. There&rsquo;s a tendency to jump straight to conclusions. Create something, move to the next thing.</p><p>We&rsquo;ve manufactured the wrong sense of progress. We&rsquo;ve flattened our thinking to activity-based monitoring. We measure work done, not impact made.</p><p>And the systems we operate in make us fear being punished for slowing down. But slowing down doesn&rsquo;t have to be negative. It&rsquo;s adding depth and establishing foundations to build on.</p><p>I&rsquo;ve seen it happen more than once. A team delivers exactly what was asked. On time and according to spec. But the problem isn&rsquo;t solved. Not because of poor execution, but because questions were not asked.</p><p>The solution was locked in before the problem was understood.</p><h2 id="diverge-before-you-converge">Diverge Before You Converge</h2><p>This is where design thinking offers something valuable. Divergence and convergence.</p><p>Divergence is expanding. You open up the problem space, explore possibilities, challenge assumptions and resist the urge to settle too quickly.</p><p>Convergence is getting to the point. You bring constraints back in. Budget, technical feasibility, timeline. And you shape your exploration into something actionable.</p><p>Most teams are good at converging. They ship.</p><p>But divergence? That takes discipline. It means staying in the question longer than feels comfortable.</p><h2 id="how-might-we">How Might We ... ?</h2><p>A practical tool for divergence is the <em>how might we</em> method. It&rsquo;s simple. Take any problem and reframe it as a question starting with &ldquo;how might we.&rdquo;</p><p>The phrasing matters. <em>How might we</em> is optimistic without being naive. It assumes a solution exists but leaves the how open.</p><p>Here&rsquo;s what it looks like in practice.</p><ul><li><p>&ldquo;Users keep dropping off at checkout.&rdquo;<br /><em>How might we reduce the friction between cart and payment?</em></p></li><li><p>&ldquo;Our onboarding takes too long.&rdquo;<br /><em>How might we get new users to their first success faster?</em></p></li><li><p>&ldquo;Admins can&rsquo;t manage permissions at scale.&rdquo;<br /><em>How might we make it easier to give multiple people the right access?</em></p></li></ul><p>Each reframe does the same thing. It takes a complaint or observation and turns it into a space you can explore.</p><p>The problem stays the same. But the number of possible solutions grows. That expansion is where the real value lives.</p><h2 id="the-question-before-the-prompt">The Question Before the Prompt</h2><p>This matters even more now that AI has become part of how teams work.</p><p>AI is good at execution. Like generating code or drafting copy. It&rsquo;s a powerful convergence engine. But that&rsquo;s also the problem.</p><p>AI defaults to answers. Give it a problem, it will solve it immediately. It won&rsquo;t ask if it&rsquo;s the right problem. It won&rsquo;t explore alternatives.</p><p>That doesn&rsquo;t keep the frame open. Convergence without divergence is just fast guessing. You end up with the wrong sense of progress again. Shipping solutions to problems nobody questioned.</p><p>So the skill that matters most right now is one AI doesn&rsquo;t default to. Asking more questions. Keeping the frame open longer. Resisting the pull to converge before you&rsquo;ve explored.</p><p>Give AI a vague problem, you get a vague answer. Give it a sharp question, you get a sharper solution.</p><h2 id="takeaways">Takeaways</h2><p>The question mark might be the most underrated tool in your workflow. Not because questions slow things down. But because they open things up.</p><p>Before you assign, build or prompt, ask the question that reframes the problem:&nbsp;<em>How might we?</em></p><p>Because the quality of your answer will never exceed the quality of your question.</p><p><strong>Question everything. On purpose.</strong></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">How Depending Leads to Independence</h4></div><div class="col-8"><p class="u-fs16 u-mb0"><a target="_blank" href="https://www.telerik.com/blogs/how-depending-leads-independence">True independence isn&rsquo;t the absence of dependencies.</a> It&rsquo;s an intentional integration. An assembly that&rsquo;s clear, chosen and changeable.</p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:af49060b-9d0c-49a8-9cd9-80511c4cddc1</id>
    <title type="text">Optimistic Updates with TanStack Query in Vue</title>
    <summary type="text">Optimistic updates in Vue can help interactions feel instantaneous for improved UI. They don't improve performance though. Get the details for how and when to use them.</summary>
    <published>2026-07-15T15:30:17Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Marina Mosti </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/optimistic-updates-tanstack-query-vue"/>
    <content type="text"><![CDATA[<p><span class="featured">Optimistic updates in Vue can help interactions feel instantaneous for improved UI. They don&rsquo;t improve performance though. Get the details for how and when to use them.</span></p><p>In my previous article on <a target="_blank" href="https://www.telerik.com/blogs/handling-mutations-tanstack-query-vue">Handling Mutations with TanStack Query for Vue</a>, we covered the basics of mutations, lifecycle callbacks and <code>invalidateQueries</code>. We brushed over optimistic updates with a quick example, but I wanted to come back and give them their own dedicated piece because they really deserve it.</p><p>Optimistic updates are one of those features that, when used well, make your app feel instantaneous. Used poorly, though, they can introduce some of the most frustrating bugs you&rsquo;ll ever try to debug.</p><p>Today we&rsquo;ll walk through the full flow with <code>onMutate</code>, the snapshot and rollback dance, and <code>onSettled</code>, plus we&rsquo;ll talk honestly about when to reach for them and when to leave them alone.</p><h2 id="what-is-an-optimistic-update">What Is an Optimistic Update?</h2><p>The default behavior with mutations goes like this:</p><ol><li>The user clicks a button.</li><li>The request fires.</li><li>You wait for the response.</li><li>Then, finally, you update the UI.</li></ol><p>For most flows, that&rsquo;s perfectly fine, especially if you&rsquo;re showing a loading state on the button itself.</p><p>But there are some interactions where waiting feels wrong. A like button is the classic example. The user expects that heart to fill in <em>immediately</em>, not 250ms later when the API responds. The same goes for things like favoriting an item, toggling a setting, marking a todo as done or any inline edit that should feel like the user is editing local state directly.</p><p>An optimistic update flips the script. Instead of waiting for the server, we update the cached data right away as if the mutation had already succeeded. The mutation still runs in the background, and if it fails we roll back to where we were. That&rsquo;s the whole idea in a nutshell.</p><p>The reason TanStack Query is so good at this is the cache. We already have one place where the data lives, so optimistically writing into it and then either keeping or rolling back that change is very clean.</p><h2 id="the-lifecycle-we-care-about">The Lifecycle We Care About</h2><p>Three of the lifecycle callbacks from <code>useMutation</code> carry the whole flow:</p><ul><li><strong><code>onMutate</code></strong>: Fires <em>before</em> the <code>mutationFn</code> runs. This is where we cancel any in flight queries that could overwrite our optimistic value, take a snapshot of the current cache and write the new value in.</li><li><strong><code>onError</code></strong>: Fires if the mutation rejects. This is where we restore the snapshot we took in <code>onMutate</code>.</li><li><strong><code>onSettled</code></strong>: Fires after either success or error. This is where we invalidate the query so the server eventually wins, no matter what happened in between.</li></ul><p>Notice that <code>onSuccess</code> doesn&rsquo;t really play a role here. The whole point is, we already updated the UI in <code>onMutate</code>, so there&rsquo;s nothing extra to do on success. The cache is already showing the new value, we just want the next refetch to confirm it.</p><h2 id="a-like-button-end-to-end">A Like Button, End to End</h2><p>Let&rsquo;s build a like button on a post that flips a <code>liked</code> flag and increments a <code>likes</code> counter. Assume we already have a query that fetches the list of posts.</p><p><code>PostList.vue</code></p><pre class=" language-vue"><code class="prism  language-vue">&lt;script setup&gt;
import { useQuery } from '@tanstack/vue-query'
import LikeButton from './LikeButton.vue'

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

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

&lt;template&gt;
  &lt;p v-if="isPending"&gt;Loading...&lt;/p&gt;
  &lt;ul v-else&gt;
    &lt;li v-for="post in posts" :key="post.id"&gt;
      &lt;!-- Render the title and a like button per each post --&gt;
      {{ post.title }}

      &lt;LikeButton :post="post" /&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
&lt;/template&gt;
</code></pre><p>Nothing surprising there. Now for the interesting part, let&rsquo;s look at the <code>LikeButton</code> component itself.</p><p><code>LikeButton.vue</code></p><pre class=" language-vue"><code class="prism  language-vue">&lt;script setup&gt;
import { useMutation, useQueryClient } from '@tanstack/vue-query'

const props = defineProps({
  post: { type: Object, required: true },
})

const queryClient = useQueryClient()

const toggleLike = async (postId) =&gt; {
  const response = await fetch(`https://myapp.com/posts/${postId}/like`, {
    method: 'POST',
  })
  if (!response.ok) {
    throw new Error('Failed to toggle like')
  }
  return response.json()
}

const { mutate } = useMutation({
  mutationFn: toggleLike,
  onMutate: async (postId) =&gt; {
    await queryClient.cancelQueries({ queryKey: ['posts'] })

    const previousPosts = queryClient.getQueryData(['posts'])

    queryClient.setQueryData(['posts'], (old) =&gt;
      old.map((p) =&gt;
        p.id === postId
          ? { 
            ...p, 
            liked: !p.liked, 
            likes: p.liked ? p.likes - 1 : p.likes + 1 
          }
          : p
      )
    )

    return { previousPosts }
  },
  onError: (err, postId, context) =&gt; {
    queryClient.setQueryData(['posts'], context.previousPosts)
  },
  onSettled: () =&gt; {
    queryClient.invalidateQueries({ queryKey: ['posts'] })
  },
})
&lt;/script&gt;

&lt;template&gt;
  &lt;button @click="mutate(post.id)"&gt;
    {{ post.liked ? '&hearts;' : '♡' }} {{ post.likes }}
  &lt;/button&gt;
&lt;/template&gt;
</code></pre><p>Let&rsquo;s break it down step by step, because there&rsquo;s a lot happening.</p><h3 id="step-1-cancel-in-flight-queries">Step 1: Cancel In-flight Queries</h3><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">await</span> queryClient<span class="token punctuation">.</span><span class="token function">cancelQueries</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">'posts'</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>This is the easiest one to skip and the easiest one to regret skipping. If a refetch of <code>['posts']</code> is currently happening (say, because the user just tabbed back to the window), it could finish <em>after</em> we write our optimistic value, with stale server data, and stomp all over our update. <code>cancelQueries</code> makes sure we have a clean slate before we touch the cache.</p><h3 id="step-2-snapshot-the-current-cache">Step 2: Snapshot the Current Cache</h3><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">const</span> previousPosts <span class="token operator">=</span> queryClient<span class="token punctuation">.</span><span class="token function">getQueryData</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'posts'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
</code></pre><p>We grab whatever is currently in the cache for that query. This is our &ldquo;undo&rdquo; state. If the mutation fails, this is the value we&rsquo;ll restore.</p><h3 id="step-3-write-the-optimistic-value">Step 3: Write the Optimistic Value</h3><pre class=" language-javascript"><code class="prism  language-javascript">queryClient<span class="token punctuation">.</span><span class="token function">setQueryData</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'posts'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>old<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
  old<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span>p<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
    p<span class="token punctuation">.</span>id <span class="token operator">===</span> postId
      <span class="token operator">?</span> <span class="token punctuation">{</span> 
        <span class="token operator">...</span>p<span class="token punctuation">,</span> 
        liked<span class="token punctuation">:</span> <span class="token operator">!</span>p<span class="token punctuation">.</span>liked<span class="token punctuation">,</span> 
        likes<span class="token punctuation">:</span> p<span class="token punctuation">.</span>liked <span class="token operator">?</span> p<span class="token punctuation">.</span>likes <span class="token operator">-</span> <span class="token number">1</span> <span class="token punctuation">:</span> p<span class="token punctuation">.</span>likes <span class="token operator">+</span> <span class="token number">1</span> 
      <span class="token punctuation">}</span>
      <span class="token punctuation">:</span> p
  <span class="token punctuation">)</span>
<span class="token punctuation">)</span>
</code></pre><p><code>setQueryData</code> accepts either a value or an updater function. Using the function form is much safer here because it gives us the latest cached value directly from the cache, rather than relying on something we read earlier.</p><p><strong>⚠️ Important!</strong> Notice how we map and spread instead of mutating in place. Returning a new array (and a new object for the updated post) is what triggers Vue&rsquo;s reactivity through TanStack Query&rsquo;s reactive layer, and it also keeps the snapshot we took above intact for rollback.</p><h3 id="step-4-return-the-context">Step 4: Return the Context</h3><pre class=" language-javascript"><code class="prism  language-javascript"><span class="token keyword">return</span> <span class="token punctuation">{</span> previousPosts <span class="token punctuation">}</span>
</code></pre><p>Whatever you return from <code>onMutate</code> shows up as the third argument (<code>context</code>) in <code>onError</code> and <code>onSettled</code>. This is the official way to thread the snapshot through the lifecycle. Don&rsquo;t be tempted to stash it in a regular variable in your <code>&lt;script setup&gt;</code>, it will get the wrong value if multiple mutations are in flight at once.</p><h3 id="step-5-roll-back-on-error">Step 5: Roll Back on Error</h3><pre class=" language-javascript"><code class="prism  language-javascript">onError<span class="token punctuation">:</span> <span class="token punctuation">(</span>err<span class="token punctuation">,</span> postId<span class="token punctuation">,</span> context<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  queryClient<span class="token punctuation">.</span><span class="token function">setQueryData</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'posts'</span><span class="token punctuation">]</span><span class="token punctuation">,</span> context<span class="token punctuation">.</span>previousPosts<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>If the mutation fails, we put the snapshot back. The UI flips back to its previous state. From the user&rsquo;s perspective the like just didn&rsquo;t happen.</p><p>In a production app, though, you may want to use a toast or something similar to alert the user that something went wrong. Otherwise, they will only see the button revert back to its previous state with no extra information.</p><h3 id="step-6-invalidate-on-settled">Step 6: Invalidate on Settled</h3><pre class=" language-javascript"><code class="prism  language-javascript">onSettled<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>
  queryClient<span class="token punctuation">.</span><span class="token function">invalidateQueries</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">'posts'</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre><p>Win or lose, we invalidate the query. On success, this confirms whatever the server actually saved (which might differ from what we optimistically wrote). On error, this gives us authoritative data to overwrite whatever rollback state we ended up in. Either way the cache ends up in sync with the server.</p><h2 id="a-note-on-cancelqueries-and-race-conditions">A Note on cancelQueries and Race Conditions</h2><p>I want to spend an extra moment on <code>cancelQueries</code>, because every time I&rsquo;ve seen optimistic updates go subtly wrong in production, it has been because of this.</p><p>Imagine the user clicks the like button at the same moment that a background refetch from window focus is in flight. Without <code>cancelQueries</code>, the refetch finishes a few milliseconds after our optimistic write and overwrites our heart icon with the old server data. The user sees their like flicker in and then back out. They click again. Now we have a double-fire problem and a confused user.</p><p>Calling <code>cancelQueries</code> aborts that in flight fetch and prevents it from touching the cache. It&rsquo;s one line that prevents an entire category of bugs.</p><h2 id="a-second-example-inline-edit">A Second Example: Inline Edit</h2><p>The like button is the textbook case, but optimistic updates really shine for inline edits too. Imagine an editable title on a document where the user clicks, types and tabs away, and you don&rsquo;t want them to wait for the round trip before the new title is visible.</p><p><code>EditableTitle.vue</code></p><pre class=" language-vue"><code class="prism  language-vue">&lt;script setup&gt;
import { ref, watch } from 'vue'
import { useMutation, useQueryClient } from '@tanstack/vue-query'

const props = defineProps({
  doc: { type: Object, required: true },
})

const draft = ref(props.doc.title)
watch(() =&gt; props.doc.title, (next) =&gt; { draft.value = next })

const queryClient = useQueryClient()

const updateTitle = async ({ id, title }) =&gt; {
  const response = await fetch(`https://myapp.com/docs/${id}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ title }),
  })
  if (!response.ok) {
    throw new Error('Failed to update title')
  }
  return response.json()
}

const { mutate } = useMutation({
  mutationFn: updateTitle,
  onMutate: async ({ id, title }) =&gt; {
    await queryClient.cancelQueries({ queryKey: ['doc', id] })
    const previousDoc = queryClient.getQueryData(['doc', id])
    queryClient.setQueryData(['doc', id], (old) =&gt; ({ ...old, title }))
    return { previousDoc }
  },
  onError: (err, { id }, context) =&gt; {
    queryClient.setQueryData(['doc', id], context.previousDoc)
  },
  onSettled: (data, error, { id }) =&gt; {
    queryClient.invalidateQueries({ queryKey: ['doc', id] })
  },
})

const onBlur = () =&gt; {
  if (draft.value !== props.doc.title) {
    mutate({ id: props.doc.id, title: draft.value })
  }
}
&lt;/script&gt;

&lt;template&gt;
  &lt;input v-model="draft" @blur="onBlur" /&gt;
&lt;/template&gt;
</code></pre><p>The shape is identical to the like button: cancel, snapshot, write, invalidate. The only difference is that the variables passed to <code>mutate</code> carry both an <code>id</code> and a <code>title</code>, so we destructure them in each callback.</p><p>Notice how the <code>id</code> is part of every callback&rsquo;s variables. This is what lets us roll back the <em>correct</em> document if multiple inline edits are happening on different documents at the same time. Generic context that isn&rsquo;t keyed by entity ID is one of the easiest ways to introduce bugs in this pattern.</p><h2 id="when-to-use-optimistic-updates">When to Use Optimistic Updates</h2><p>I&rsquo;ll be quite honest with you: I don&rsquo;t reach for optimistic updates by default. The plain &ldquo;spinner plus invalidate&rdquo; flow is fine for the majority of forms, edit screens and CRUD buttons in a typical app. The complexity of optimistic updates only pays off in specific situations.</p><p>The cases where they really earn their keep:</p><ul><li><strong>Toggle interactions</strong>: Likes, favorites, follows, bookmarks. Anything with a strong &ldquo;this should feel local&rdquo; expectation.</li><li><strong>High-frequency edits</strong>: Inline editing of titles, drag-to-reorder, slider inputs that round-trip per change.</li><li><strong>Slow networks</strong>: Anywhere you know your users may be on flaky mobile data and a 500ms round trip will feel painful.</li></ul><p>Cases where I would skip them:</p><ul><li><strong>Form submissions</strong>: A clear loading state on the submit button is honest and easy to reason about.</li><li><strong>Destructive actions</strong>: Deleting something optimistically and then having to &ldquo;undelete&rdquo; it on error is a worse experience than waiting half a second.</li><li><strong>Anything where the server might transform the payload</strong>: If the response is going to differ in non-trivial ways from what you optimistically wrote, you&rsquo;ll spend more time fighting reconciliation than you saved on perceived speed.</li></ul><p>Big disclaimer though, optimistic updates are a UX tool, not a performance one. They don&rsquo;t make your API faster, they just hide latency. If your real problem is that the API is slow, fix the API. </p><h2 id="wrapping-up">Wrapping Up</h2><p>Optimistic updates take a bit more wiring than a regular mutation, but the four steps are always the same: cancel related queries, snapshot the cache, write the optimistic value and reconcile on settled. Once you&rsquo;ve written one, the rest tend to be variations on the same theme.</p><p>We covered the lifecycle, walked through a like button end to end, and talked about when to reach for optimistic updates or skip them.</p><p>The <a target="_blank" href="https://tanstack.com/query/latest/docs/framework/vue/guides/optimistic-updates">official optimistic updates guide</a> goes into a few more patterns, including using the cache to drive the optimistic value directly from the variables passed to <code>mutate</code>, which is worth a read once you have the basics down.</p><p>Happy mutating!</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">Vue Basics: A Comprehensive Guide to Vue 3 Directives</h4></div><div class="col-8"><p class="u-fs16 u-mb0">New to <a target="_blank" href="https://www.telerik.com/blogs/vue-basics-comprehensive-guide-vue-3-directives">Vue 3? This guide breaks down everything you need to know about directives</a>, from dynamic bindings to conditional rendering and custom behaviors, with examples you can use right away.</p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:846f90ba-035b-406a-97de-337b9ca25ffc</id>
    <title type="text">Self-Knowledge with AI: The Greatest Investment a Developer Can Make</title>
    <summary type="text">How a journal, a to-do list and an honest conversation with AI changed the way I see myself and the way I build software.</summary>
    <published>2026-07-15T12:20:56Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Jefferson S. Motta </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/self-knowledge-ai-greatest-investment-developer-can-make"/>
    <content type="text"><![CDATA[<p><span class="featured">How a journal, a to-do list and an honest conversation with AI changed the way I see myself and the way I build software.</span></p><p>In my technical accounting course back in the &rsquo;90s, there was a class on psychology. Out of everything I learned that semester, one phrase stayed with me until today: &ldquo;Know thyself,&rdquo; inscribed at the Temple of Apollo in Delphi and made eternal by Socrates.</p><p>Twenty-something years later, with a full career in software engineering behind me, I have realized this is the most underrated skill in our profession. We collect certificates, frameworks, languages and architecture. We stack courses on LinkedIn as if they were trophies. But we rarely stop to debug ourselves.</p><p>And what an irony. We, who spend our lives fixing other people&rsquo;s bugs, are the ones least likely to look at our own.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/melissa-holowaty-emal28ood9c-unsplash.jpg?sfvrsn=6d41393e_2" height="3456" style="max-width:100%;height:auto;" title="melissa-holowaty-eMAL28ood9c-unsplash" width="5184" alt="Delphi" sf-size="3157259" /><br /><span style="font-size:11px;">Photo by <a target="_blank" href="https://unsplash.com/@holowat2?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Melissa Holowaty</a> on <a target="_blank" href="https://unsplash.com/photos/ancient-greek-temple-ruins-on-a-hillside-with-trees-eMAL28ood9c?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span></p><h2 id="why-this-matters-for-those-who-write-code">Why This Matters for Those Who Write Code</h2><p>Socrates also said that &ldquo;an unexamined life is not worth living.&rdquo; I would adapt this for our craft: a career without reflection is not worth building.</p><p>How many times have you accepted a project knowing it would drain your energy? How many times have you fought over a technical decision that, deep down, was just ego? How many times have you postponed rest because &ldquo;just one more commit&rdquo;? These are all symptoms, and symptoms must be treated at the cause, not in the stack trace.</p><p>The cause is almost always in a place that hurts to look at. That is why most people do not look.</p><h2 id="the-method-that-works-for-me">The Method That Works for Me</h2><p>There is no shortcut to self-knowledge, but there are tools. Mine are absurdly simple.</p><h3 id="a-journal-connected-to-goals">1. A Journal Connected to Goals</h3><p>This is not a teenager&rsquo;s diary. It is an engineer&rsquo;s log. I write down epiphanies, decisions, situations that pushed me over the edge, and patterns that keep repeating. Will Durant said, &ldquo;We are what we repeatedly do,&rdquo; so I started observing what I was repeating.</p><h3 id="reflection-before-sleep">2. Reflection Before Sleep</h3><p>I replay the day in my head and imagine a better version of myself reacting to the same situations. It sounds silly, but it is mental training, just like writing tests for code that does not exist yet. You are programming future behavior.</p><h3 id="microsoft-to-do-as-a-measurement-instrument">3. Microsoft TO DO as a Measurement Instrument</h3><p>I record everything I do during the month and categorize it. The categorized data becomes the input I feed to the AI later (more on that below).</p><h2 id="life-happens-in-cycles">Life Happens in Cycles</h2><p>One thing I&rsquo;ve noticed from keeping this habit is that life repeats itself in cycles, and the same lesson keeps coming back until you learn it. Every day, trivial situations (or not) will reappear. If you have already mapped out a better way to react in your head, you will change automatically when they return.</p><p>And there are moments when the entire system shifts: a trip, houseguests, a new project. When the ecosystem changes, a window opens to rewrite behaviors. It is like deploying a new version. The environment is already in maintenance mode, so take advantage of it.</p><p>I use these windows consciously. But I can only do that because I already know what I want to change. And that knowledge only came from the habit of observing myself.</p><h2 id="where-ai-comes-in-and-where-it-falls-short">Where AI Comes In, and Where It Falls Short</h2><h3 id="part-i-using-the-to-do">Part I: Using the TO DO</h3><p>I use the TO DO tasks at the end of the month, export them and feed them to AI, asking for an honest analysis: what made me waste time, what I should eliminate and where I am running away from my own goals.</p><p>I did this throughout 2025 and into early 2026, and the results scared me, in a good way: too many tasks for a single developer; too many projects in the timeline; and it helped give me focus on the main solutions.</p><h3 id="part-ii-using-the-llm">Part II: Using the LLM</h3><p>This is the part that helped me the most over the past two years. The exercise goes like this.</p><p><strong>1. Create a long document with the story of your life.</strong> Be detailed. List your traumas, your victories, your shames, your patterns. Be brutally honest.</p><p><strong>2. Add a section about what you want to learn in the coming months.</strong></p><p><strong>3. Add a section about what you want to achieve in life.</strong></p><p><strong>4. Then ask the AI model:</strong></p><blockquote><p>&ldquo;What did I fail to notice in my own story that is hidden in what I wrote?&rdquo;</p></blockquote><p>The result may surprise you. It surprised me. AI sees patterns you do not see, because you are inside the pattern.</p><p>But, and this &ldquo;but&rdquo; is large, AI hallucinates. It will make things up. It will reach wrong conclusions. It will tell you what you want to hear if you give it room. Use the result as a hypothesis, never as a diagnosis. The ideal is still a real therapist.</p><p>I use AI as a complementary mirror, and when something really hits me, I take it to someone who knows me, a close friend or a family member, to check it against a human perspective.</p><p>AI is a tool. A good and powerful one, but a tool. It does not replace therapy. It does not replace friendship. And it does not replace the courage to look at yourself.</p><h2 id="why-it-hurts-and-why-it-is-worth-it">Why It Hurts and Why It Is Worth It</h2><p>Knowing yourself hurts. You may face things you would rather have forgotten. But those are exactly the things blocking your progress.</p><p>Nobody can help you out of your own shell. There is a cruel observation in nature: if you help a chick break its shell, it dies. It did not develop the strength needed for what comes next. Our shell works the same way. The effort itself is the development.</p><p>Seek adversity on purpose. Look for challenges that scare you a little. Try to be better than you were yesterday, not better than the developer at the next desk. You are your own obstacle, and nobody is going to fight that fight for you.</p><h2 id="conclusion">Conclusion</h2><p>This is the path that worked for me. It will not work the same way for you. You need to find your own tools, your own pace, your own style of honesty.</p><p>But start. Open a document today, write your story, write what you want from life and what is holding you back. Reread it in a month. Reread it in a year. You will see things that are invisible to you today.</p><blockquote><p><strong><em>Investing in new frameworks gives you a few months&rsquo; advantage. Investing in yourself earns you a lifetime.</em></strong></p></blockquote><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">How to Identify Technologies Worth Exploring</h4></div><div class="col-8"><p class="u-fs16 u-mb0"><a target="_blank" href="https://www.telerik.com https://www.telerik.com/blogs/how-to-identify-technologies-worth-exploring">Read Jefferson&rsquo;s perspective</a> on one of the biggest challenges we face as technology professionals today: separating what really matters from the constant noise that bombards us every day.</p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:cdf15d91-3853-4df5-9081-f0766ab544bc</id>
    <title type="text">Using AI to Build a Blazor App 2: Define the Requirement</title>
    <summary type="text">Knowing roughly what you’re aiming for is one thing, but sooner or later you need to get clear on the specifics. What’s the feature you’re actually aiming for?</summary>
    <published>2026-07-14T14:24:06Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Jon Hilton </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/using-ai-build-blazor-app-2-define-requirement"/>
    <content type="text"><![CDATA[<p><span class="featured">Knowing roughly what you&rsquo;re aiming for is one thing, but sooner or later you need to get clear on the specifics. What&rsquo;s the feature you&rsquo;re actually aiming for?</span></p><blockquote><p>AI will build whatever you ask it to, so you&rsquo;d better ask for the right thing.</p></blockquote><p>Last time, we saw how to <a href="https://www.telerik.com/blogs/using-ai-build-blazor-app-1-start-problem" target="_blank">use AI to dig into a problem and define the rough shape</a> of a viable solution. By the end of that session, we&rsquo;d decided to keep things simple: a markdown editor that works directly with MDX files, a way to draft the corresponding newsletter emails and a publishing workflow to make the article live on the web.</p><p>We&rsquo;d avoided heavy technical discussions, UI design or carving out specific features, and ended up with a number of decision records (markdown) which captured the decisions we made along the way. Now we needed to get more specific and figure out which feature to build first.</p><p>Here&rsquo;s the prompt I tried this time (using GPT 5.5).</p><blockquote><p>We previously worked through an ideation process for this new project and captured key decisions as ADRs on disk. Please now work with me to carve out the headline feature we want to build first, that we think has the best chance of delivering the value we carved out in phase 1. We know what we&rsquo;re trying to build and why, now we want to make this concrete by figuring out what this core feature is. Do not get into specific implementation details here. The goal is to shape the feature (mapping to the decisions we&rsquo;ve already made).</p><p>Ask me questions, one at a time, to flesh this out enough that we can later define our first behavioural tests, which will then drive implementation.</p><p>For each question, include your recommended answer.</p></blockquote><p>By no means is this a perfect prompt, but the key thing I wanted was for GPT to focus on the headline feature&mdash;the thing we can build that will deliver the greatest value (to address the problem we identified in Part 1).</p><p>To avoid getting into the technical weeds at this stage, the instructions are to stay firmly in &ldquo;what can the user do, and what does it achieve&rdquo; territory. Just as with Part 1, this flushed out some crucial details.</p><p>For example, GPT asked how I wanted the flow between writing an article and then drafting its related newsletter to work.</p><p>I realized I didn&rsquo;t want this to be a linear process (write article, prepare newsletter) but more of an editing surface where I could jump between the two.</p><blockquote><p>I&rsquo;d say the prepare newsletter can happen any time, I might potentially work on the article, then mid-writing realise I&rsquo;ve got the hook for the newsletter and decide to jump over there to draft that (or at least capture it). So it&rsquo;s not linear</p></blockquote><p>To which GPT responded:</p><blockquote><p>That&rsquo;s a useful constraint: the headline feature is not a linear wizard. It&rsquo;s an <strong>article workspace with a linked newsletter draft available at any time</strong>, plus a deliberate publish action when the blog post is ready.</p><p><strong>Recommended shape:</strong> every article has a companion newsletter area that can be opened whenever inspiration hits. It can hold rough notes, hooks, or a near-final email. Publishing the blog post later gives that newsletter draft the final URL it needs.</p></blockquote><p>We dug into details around how any AI assistance might work with the newsletter editor. I don&rsquo;t like the idea of AI writing content for me, but would be happy to use AI for hook and subject line suggestions.</p><blockquote><p>Next question: <strong>how should the app present those hook/subject suggestions so they help without taking over?</strong></p></blockquote><p>I answered:</p><blockquote><p>I think maybe options for the user to generate hooks, generate subjects etc. then a way to take one and use it (or get new ones if they&rsquo;re no good)</p></blockquote><p>GPT:</p><blockquote><p>That gives the assistance a clean role: <strong>explicitly requested idea generation for newsletter framing</strong>, with user-controlled insertion.</p></blockquote><p>We covered a lot of ground in this chat, including:</p><ul><li>When you mark a finished article as ready to publish, should the feature generate the newsletter draft automatically?</li><li>When you begin the publish article action, what guardrails should the app check before it lets you proceed?</li><li>When the blog post is published, what should happen to the newsletter draft automatically?</li><li>What is the main user-facing promise of this headline feature in one sentence?</li></ul><h2 id="ai-recommended-answers">AI Recommended Answers</h2><p>In the prompt, I&rsquo;d asked the LLM to include its recommended answer when asking questions.</p><p>This seems &ldquo;off&rdquo; at first (surely that&rsquo;s your job, as the human!), but it turns out to be quite helpful.</p><p>There is a danger that you&rsquo;ll just start accepting what the AI suggests because it&rsquo;s the easiest option, so it&rsquo;s worth thinking carefully about what you really think before blindly accepting its recommendations. But in many cases the AI gets it right, or close enough that you can tweak it and move on.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/gpt-happy-path.png?sfvrsn=5cffb767_2" title="GPT not only asks the question, it offers a recommended answer. Useful as a starting point you can push back on, rather than a blank canvas." alt="GPT response recommending the headline-feature happy path from idea capture through Astro publish" /></p><p>One of the biggest challenges with defining scope is being clear what <strong>not</strong> to build.</p><p>On this occasion, GPT caught this, finishing with this question:</p><blockquote><p>Before we stop, one final shaping question: <strong>what would be explicitly out of scope for the first version of Article Workspace?</strong></p><ul><li>Social posts.</li><li>Newsletter sending/scheduling.</li><li>Full AI article generation.</li><li>Complex idea inbox or content pipeline states.</li><li>Analytics beyond simple publishing visibility.</li><li>Multi-user/productized workflows.</li><li>Deep DevScribe/Bento integration.</li><li>Rigid streak mechanics.</li><li>Implementation-specific Git UI details beyond a deliberate publish action.</li></ul></blockquote><p>It&rsquo;s tempting to think that, with AI, we could include some of these and let it go at them. But we still care about code quality and building features that work, and the best way to do that with LLMs is to be specific and reduce how many things we ask it to do at a time.</p><p>So I agreed with all of these being out of scope and we settled on this shape for the headline feature.</p><pre class=" language-markdown"><code class="prism  language-markdown"><span class="token title important"><span class="token punctuation">#</span> Headline Feature: Article Workspace</span>

Article Workspace lets you capture an idea, develop it into a Markdown article, prepare a companion newsletter draft, publish the blog post to Astro, and keep the email follow-up visible so the audience receives useful value and the weekly publishing rhythm builds trust over time.

<span class="token title important"><span class="token punctuation">##</span> Core Happy Path</span>

You open the app, capture a useful idea with why it matters, immediately get a draft article workspace, write the Markdown article, optionally draft newsletter hooks, subject, and body alongside it, confirm publish readiness, publish the blog post to Astro by marking it not draft and committing/pushing, then see the final URL available to finish the linked newsletter draft for separate sending.

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

<span class="token list punctuation">-</span> Idea capture is the earliest form of a draft, not a separate workflow.
<span class="token list punctuation">-</span> Capture requires the idea plus why it matters / what is at stake / angle.
<span class="token list punctuation">-</span> The &ldquo;why it matters&rdquo; note stays visible near the editor, not inside the published article by default.
<span class="token list punctuation">-</span> Newsletter writing can happen at any time, not only after the article is finished.
<span class="token list punctuation">-</span> AI assistance is limited to optional hooks and subject-line suggestions.
<span class="token list punctuation">-</span> Blog publishing and newsletter sending remain separate deliberate actions.
<span class="token list punctuation">-</span> Blog success means the article is in Astro source, marked not draft, committed, and pushed.
<span class="token list punctuation">-</span> Newsletter success means subject/body are ready, reviewed, and have access to the final article URL unless sending the full article verbatim.

<span class="token title important"><span class="token punctuation">##</span> Out of Scope For First Version</span>

Social posts, newsletter sending/scheduling, full AI article generation, complex idea inbox or pipeline states, broad analytics, multi-user workflows, deep DevScribe/Bento integration, rigid streak mechanics, and implementation-specific Git UI detail beyond a deliberate publish action.
</code></pre><p>One last step, I asked GPT to capture this and it chose to record a new ADR.</p><p>It could be that ADRs aren&rsquo;t the right tool here (using them for everything feels like we might be over-simplifying how we capture requirement details), but in the interest of pragmatism I chose to accept it and move on to the next stage.</p><h2 id="measure-twice-cut-once">Measure Twice, Cut Once?</h2><p>Let&rsquo;s address the elephant in the room. When you work through a process like this (with or without AI), it can feel like a lot of effort up front. The temptation is to bypass this stage. Wouldn&rsquo;t it be quicker to simply throw a prompt at Codex and hope it sorts it out for us?!</p><p>In short, not really.</p><p>With the prompt-and-pray approach, you might feel like you&rsquo;re going faster, but this thinking, scoping and being clear about what you&rsquo;re building is going to happen somewhere.</p><p>The danger of jumping straight to prompt is you&rsquo;ll end up with something that doesn&rsquo;t solve the problem. Or you&rsquo;ll spend much, much longer iterating to the solution because the thinking is happening as new code is written.</p><p>LLMs work best with smaller amounts of information in their context window (fewer tokens). If you can break the work down ahead of time, you can keep your context minimal for each small step.</p><p>If you start with wide-open scope, you&rsquo;ll find yourself throwing more and more features at the problem (because building stuff is fun!). But the real goal here is to take the smallest step, in order to get feedback (from real users).</p><p>I&rsquo;ve found this up-front thinking work pays dividends down the road.</p><p>But rather than take my word for it, give it a go for yourself. Get that impulsive part of your brain to take a back seat for an hour or so while you work with AI to get clear about what you&rsquo;re building.</p><p>Then, when you&rsquo;re ready, move on to the next step: figuring out what to build first and what that should actually look like. We&rsquo;ll get into that in Part 3.</p><p>We&rsquo;ll also discover how some old techniques, borrowed from a classic software development playbook, make AI work better too.</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">Telerik UI for Blazor Meets A2UI: The Next Step Toward Dynamic UI Generation</h4></div><div class="col-8"><p class="u-fs16 u-mb0"><a target="_blank" href="https://www.telerik.com/blogs/telerik-ui-blazor-meets-a2ui-next-step-toward-dynamic-ui-generation">Learn how A2UI enables AI systems</a> to generate interactive, dynamically generated Blazor interfaces and how Progress Telerik UI for Blazor will help.</p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:4db51f4e-9e2e-4b06-9992-7768fc46c6f0</id>
    <title type="text">Async Validation in Angular Signal Forms</title>
    <summary type="text">Learn how to implement async validation in Angular Signal Forms using the validateHttp function, for example to check whether a username is already taken.</summary>
    <published>2026-07-14T11:38:56Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Dhananjay Kumar </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/async-validation-angular-signal-forms"/>
    <content type="text"><![CDATA[<p><span class="featured">Learn how to implement async validation in Angular Signal Forms using the validateHttp function, for example to check whether a username is already taken.</span></p><p>Signal Forms create a form from a writable signal model, where the model is the source of truth, and any change in the form or the model is automatically reflected in the other.</p><p>Read more about <a href="https://www.telerik.com/blogs/getting-started-angular-signal-forms" target="_blank">Signal Forms here</a>.</p><p>In this article, we will explore async validation in Signal Forms. You perform async validation for various purposes, such as to:</p><ul><li>Check if usernames or emails are already taken</li><li>Look up data in the database to confirm values</li><li>Use external services (APIs) to verify things like addresses or tax IDs</li><li>Apply business rules that only the server can validate</li></ul><p>When creating a signup form, you need to check whether the email is already in use by calling a database via an API. Since this takes time, you should use Signal Forms async validation to handle it.</p><p>To validate against an API or HTTP endpoint, Angular provides the <code>validateHttp()</code> function. Let&rsquo;s explore how to use that.</p><h2 id="create-a-signup-form">Create a Signup Form</h2><p>To begin, create a signup form by first defining an interface to represent the signup type.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">SignupFormValue</span> <span class="token punctuation">{</span>
  readonly email<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  readonly password<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Next, use it to create the signal-based Signup form as shown in the next code listing:</p><pre class=" language-ts"><code class="prism  language-ts">  signupModel<span class="token punctuation">:</span> WritableSignal<span class="token operator">&lt;</span>SignupFormValue<span class="token operator">&gt;</span> <span class="token operator">=</span> signal<span class="token operator">&lt;</span>SignupFormValue<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    email<span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
    password<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>

  signupForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>signupModel<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Then, in the template, bind it to the controls as shown below.</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">(submit)</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>onSubmit($event)<span class="token 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>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signup-email<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>br</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">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signup-email<span class="token punctuation">"</span></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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signupForm.email<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>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>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signup-password<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>br</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">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signup-password<span class="token punctuation">"</span></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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signupForm.password<span class="token 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>
  <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>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 punctuation">&gt;</span></span>Sign up<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>p</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>
</code></pre><p>Next, create the submit function as shown below.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">protected</span> <span class="token function">onSubmit</span><span class="token punctuation">(</span>event<span class="token punctuation">:</span> Event<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
    event<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 function">submit</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>signupForm<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">const</span> credentials <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">signupModel</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">'Signup submitted:'</span><span class="token punctuation">,</span> credentials<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>As of now, we have created the signal-based signup form.</p><h2 id="adding-sync-validations">Adding Sync Validations</h2><p>To make it more usable, let us add some validations to the email field. We are going to add the validations below to the email field.</p><ol><li>Required</li><li>Email</li></ol><pre class=" language-ts"><code class="prism  language-ts">  signupForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>signupModel<span class="token punctuation">,</span> <span class="token punctuation">(</span>schemaPath<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">required</span><span class="token punctuation">(</span>schemaPath<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'Email is required'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">email</span><span class="token punctuation">(</span>schemaPath<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'Please enter a valid email address'</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>Both required and email are synchronous validators, and they run on each value change of controls. The <code>required()</code> validates that the control value is neither null nor an empty string (<code>''</code>), and <code>email()</code> validates the control value against a standard email regular expression pattern.</p><p>Both validators execute synchronously on each value change of the control.</p><p>We can conditionally show error messages for sync validators in the template using the field-state signals <code>touched()</code>, <code>invalid()</code> and <code>errors()</code>.</p><pre class=" language-ts"><code class="prism  language-ts">@<span class="token keyword">if</span> <span class="token punctuation">(</span>signupForm<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 function">touched</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> signupForm<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 function">invalid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token operator">&lt;</span>ul <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"error-list"</span><span class="token operator">&gt;</span>
      @<span class="token keyword">for</span> <span class="token punctuation">(</span>error <span class="token keyword">of</span> signupForm<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 function">errors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> track error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token operator">&lt;</span>li<span class="token operator">&gt;</span><span class="token punctuation">{</span><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 operator">&lt;</span><span class="token operator">/</span>li<span class="token operator">&gt;</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 punctuation">}</span>
</code></pre><p>The <code>touched() &amp;&amp; invalid()</code> signals are used to show errors only after the user focuses and then blurs the field, so the form does not show errors on initial load.</p><h2 id="adding-async-validations">Adding Async Validations</h2><p>There are two ways you can add async validation to a signal form.</p><ol><li>Using <code>validateHttp()</code></li><li>Using <code>validateAysnc()</code></li></ol><p>In most cases, applications should use <code>validateHttp()</code> for asynchronous validation, as it simplifies HTTP-based validation with minimal configuration and supports most common scenarios.</p><p><code>validateAsync()</code> is a lower-level API that directly exposes Angular&rsquo;s resource primitive, providing full control over the validation process but requiring more implementation effort and a deeper understanding of the resource API.</p><p>Let&rsquo;s see how we can use <code>validateHttp()</code> to check whether an email exists or not .</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token function">validateHttp</span><span class="token punctuation">(</span>schemaPath<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span>
      request<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> value <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> emailValue <span class="token operator">=</span> <span class="token function">value</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>emailValue<span class="token punctuation">)</span> <span class="token keyword">return</span> undefined<span class="token punctuation">;</span> <span class="token comment">// skip when empty</span>
        <span class="token keyword">return</span> <span class="token template-string"><span class="token string">`http://localhost:3000/user/check?email=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>emailValue<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      onSuccess<span class="token punctuation">:</span> <span class="token punctuation">(</span>response<span class="token punctuation">:</span> <span class="token punctuation">{</span> exists<span class="token punctuation">:</span> <span class="token keyword">boolean</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">return</span> response<span class="token punctuation">.</span>exists
          <span class="token operator">?</span> <span class="token punctuation">{</span>
              kind<span class="token punctuation">:</span> <span class="token string">'emailTaken'</span><span class="token punctuation">,</span>
              message<span class="token punctuation">:</span> <span class="token string">'This email is already registered'</span><span class="token punctuation">,</span>
            <span class="token punctuation">}</span>
          <span class="token punctuation">:</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      onError<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>
        kind<span class="token punctuation">:</span> <span class="token string">'serverError'</span><span class="token punctuation">,</span>
        message<span class="token punctuation">:</span> <span class="token string">'Could not verify email. Please try again later.'</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>We have an API that returns a response indicating whether the email already exists in the database, and we use the response&rsquo;s exists property to determine whether the email is already registered.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token punctuation">{</span>
  <span class="token string">"exists"</span><span class="token punctuation">:</span> <span class="token keyword">true</span><span class="token punctuation">,</span>
  <span class="token string">"user"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">"id"</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
    <span class="token string">"name"</span><span class="token punctuation">:</span> <span class="token string">"User 1"</span><span class="token punctuation">,</span>
    <span class="token string">"email"</span><span class="token punctuation">:</span> <span class="token string">"user1@example.com"</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>We can show the pending state and error message from both sync and async validator as shown below:</p><pre class=" language-ts"><code class="prism  language-ts">@<span class="token keyword">if</span> <span class="token punctuation">(</span>signupForm<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 function">pending</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token operator">&lt;</span>span <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"pending"</span><span class="token operator">&gt;</span>Checking email availability<span class="token operator">...</span><span class="token operator">&lt;</span><span class="token operator">/</span>span<span class="token operator">&gt;</span>
  <span class="token punctuation">}</span>
  @<span class="token keyword">if</span> <span class="token punctuation">(</span>signupForm<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 function">touched</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> signupForm<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 function">invalid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token operator">&lt;</span>ul <span class="token keyword">class</span><span class="token operator">=</span><span class="token string">"error-list"</span><span class="token operator">&gt;</span>
      @<span class="token keyword">for</span> <span class="token punctuation">(</span>error <span class="token keyword">of</span> signupForm<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 function">errors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> track error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token operator">&lt;</span>li<span class="token operator">&gt;</span><span class="token punctuation">{</span><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 operator">&lt;</span><span class="token operator">/</span>li<span class="token operator">&gt;</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 punctuation">}</span>
</code></pre><p>The validation flow works like this:</p><ul><li>User enters a value.</li><li>Synchronous validators run first.</li><li>If sync validation fails, async validation does not run.</li><li>If sync validation passes, async validation starts, and <code>pending()</code> becomes true.</li><li>Once the request completes, <code>pending()</code> becomes false.</li><li>Errors are updated based on the response.</li></ul><p>This can be put in a flow chart as shown below:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/angular-signal-forms-validation-flow.png?sfvrsn=1b23b96b_2" alt="Angular Signal Form validation flow: User enters a value. Angular runs sync validators. If sync validation fails, show error message; if succeeds, Angular starts async validation and sets pending to true. Once request completes, pending becomes false. Errors are updated based on the response." /></p><p>Putting everything together, a signal-based signup form with both synchronous and asynchronous validators will look like this:</p><pre class=" language-ts"><code class="prism  language-ts">signupModel<span class="token punctuation">:</span> WritableSignal<span class="token operator">&lt;</span>SignupFormValue<span class="token operator">&gt;</span> <span class="token operator">=</span> signal<span class="token operator">&lt;</span>SignupFormValue<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    email<span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
    password<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>

  signupForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>signupModel<span class="token punctuation">,</span> <span class="token punctuation">(</span>schemaPath<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">required</span><span class="token punctuation">(</span>schemaPath<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'Email is required'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">email</span><span class="token punctuation">(</span>schemaPath<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'Please enter a valid email address'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">validateHttp</span><span class="token punctuation">(</span>schemaPath<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span>
      request<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> value <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> emailValue <span class="token operator">=</span> <span class="token function">value</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>emailValue<span class="token punctuation">)</span> <span class="token keyword">return</span> undefined<span class="token punctuation">;</span> <span class="token comment">// skip when empty</span>
        <span class="token keyword">return</span> <span class="token template-string"><span class="token string">`http://localhost:3000/user/check?email=</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>emailValue<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      onSuccess<span class="token punctuation">:</span> <span class="token punctuation">(</span>response<span class="token punctuation">:</span> <span class="token punctuation">{</span> exists<span class="token punctuation">:</span> <span class="token keyword">boolean</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">return</span> response<span class="token punctuation">.</span>exists
          <span class="token operator">?</span> <span class="token punctuation">{</span>
              kind<span class="token punctuation">:</span> <span class="token string">'emailTaken'</span><span class="token punctuation">,</span>
              message<span class="token punctuation">:</span> <span class="token string">'This email is already registered'</span><span class="token punctuation">,</span>
            <span class="token punctuation">}</span>
          <span class="token punctuation">:</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      onError<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>
        kind<span class="token punctuation">:</span> <span class="token string">'serverError'</span><span class="token punctuation">,</span>
        message<span class="token punctuation">:</span> <span class="token string">'Could not verify email. Please try again later.'</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">protected</span> <span class="token function">onSubmit</span><span class="token punctuation">(</span>event<span class="token punctuation">:</span> Event<span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
    event<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 function">submit</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>signupForm<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">const</span> credentials <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">signupModel</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">'Signup submitted:'</span><span class="token punctuation">,</span> credentials<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>And on the template, the pending state and error messages can be shown as below:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">(submit)</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>onSubmit($event)<span class="token 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>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signup-email<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>br</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">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signup-email<span class="token punctuation">"</span></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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signupForm.email<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>

  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
  @if (signupForm.email().pending()) {
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>pending<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Checking email availability...<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
  }
  @if (signupForm.email().touched() &amp;&amp; signupForm.email().invalid()) {
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>ul</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>error-list<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
      @for (error of signupForm.email().errors(); track error) {
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>li</span><span class="token punctuation">&gt;</span></span>{{ error.message }}<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 tag"><span class="token tag"><span class="token punctuation">&lt;/</span>ul</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>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signup-password<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>br</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">id</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signup-password<span class="token punctuation">"</span></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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>signupForm.password<span class="token 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>
  <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>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 punctuation">&gt;</span></span>Sign up<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>p</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>
</code></pre><h2 id="validatehttp-function">validateHttp Function</h2><p>The <code>validateHttp</code> function contains three components:</p><ol><li>Request function</li><li>Success handler function</li><li>Error handler function</li></ol><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/angular-signal-form-validatehttp.png?sfvrsn=68791e3b_2" alt="Angular Signal Form validateHtep function components: request function, success handler function, error handler function" /></p><p>In the <code>validateHttp</code>, we have set the request function. It returns one of these three values:</p><ol><li>Undefined to skip the validation.</li><li>A URL for performing the validation using the GET operation.</li><li>An <code>HttpResourceRequest</code> if you need to set additional request options, such as the verb header, etc. This is the same object you use with <code>httpResource</code> to configure the request object.</li></ol><blockquote><p>Read more about the <a href="https://www.telerik.com/blogs/getting-started-httpresource-api-angular" target="_blank">httpResource here</a>.</p></blockquote><p>The <code>onSuccess</code> function receives the HTTP response and returns validation errors or undefined for valid values. It can also return multiple errors when needed.</p><p>The <code>onError</code> function handles request failures like network errors or HTTP errors.</p><p>If you need to validate an email using an endpoint with a POST request, custom headers and the email in the request body, you can do so by returning an <code>httpResourceRequest</code> from <code>validateHttp</code>, as shown below.</p><pre class=" language-ts"><code class="prism  language-ts">signupForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>signupModel<span class="token punctuation">,</span> <span class="token punctuation">(</span>schemaPath<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">required</span><span class="token punctuation">(</span>schemaPath<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'Email is required'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">email</span><span class="token punctuation">(</span>schemaPath<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'Please enter a valid email address'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token function">validateHttp</span><span class="token punctuation">(</span>schemaPath<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span>
      request<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> value <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token keyword">const</span> emailValue <span class="token operator">=</span> <span class="token function">value</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>emailValue<span class="token punctuation">)</span> <span class="token keyword">return</span> undefined<span class="token punctuation">;</span>
        <span class="token keyword">return</span> <span class="token punctuation">{</span>
          url<span class="token punctuation">:</span> <span class="token string">'http://localhost:3000/user/check'</span><span class="token punctuation">,</span>
          method<span class="token punctuation">:</span> <span class="token string">'POST'</span><span class="token punctuation">,</span>
          body<span class="token punctuation">:</span> <span class="token punctuation">{</span> email<span class="token punctuation">:</span> emailValue <span class="token punctuation">}</span><span class="token punctuation">,</span>
          headers<span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token string">'x-api-key'</span><span class="token punctuation">:</span> <span class="token string">'my-secret-key'</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>
      onSuccess<span class="token punctuation">:</span> <span class="token punctuation">(</span>response<span class="token punctuation">:</span> <span class="token punctuation">{</span> exists<span class="token punctuation">:</span> <span class="token keyword">boolean</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">return</span> response<span class="token punctuation">.</span>exists
          <span class="token operator">?</span> <span class="token punctuation">{</span> kind<span class="token punctuation">:</span> <span class="token string">'emailTaken'</span><span class="token punctuation">,</span> message<span class="token punctuation">:</span> <span class="token string">'This email is already registered'</span> <span class="token punctuation">}</span>
          <span class="token punctuation">:</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      onError<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>
        kind<span class="token punctuation">:</span> <span class="token string">'serverError'</span><span class="token punctuation">,</span>
        message<span class="token punctuation">:</span> <span class="token string">'Could not verify email. Please try again later.'</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>
</code></pre><p>The other function for async validation is <code>validateAsync()</code> function. It is a lower-level API that exposes Angular&rsquo;s resource primitive directly. It offers complete control but requires more code and familiarity with Angular&rsquo;s resource API. Most applications should use <code>validateHttp()</code>.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/angular-signal-form-validateasync.png?sfvrsn=91cb8667_2" alt="For Angular Signal Forms. Based on Resource API, validateAsync onSuccess, onError, params, factory." /></p><p>You should use <code>validateAsync()</code> for these purposes:</p><ul><li>Non-HTTP validation &ndash; WebSocket connections, IndexedDB lookups or Web Worker computations</li><li>Custom caching strategies &ndash; Application-specific caching beyond simple memorization</li><li>Complex retry logic &ndash; Custom backoff strategies or conditional retries</li><li>Direct resource access &ndash; When you need the full resource lifecycle</li></ul><p>In a future article, we&rsquo;ll take a deeper look at <code>validateAsync</code>. For now, I hope this gives you a clear understanding of how to implement async validation in Angular Signal Forms using the <code>validateHttp</code> function. Thanks for reading!</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">Angular Signals: The Essentials You Need to Know</h4></div><div class="col-8"><p class="u-fs16 u-mb0"><a target="_blank" href="https://www.telerik.com/blogs/angular-signals-essentials-you-need-know">Angular Signals</a> mark a significant shift for the framework, not an optional feature. Learn more so you can keep up!</p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:7d534c7b-328a-47e2-b23c-45a268c89b19</id>
    <title type="text">Goodbye ValueConverters, Hello C# Expressions in .NET MAUI</title>
    <summary type="text">C# Expressions mean you can now skip the Converters and add logic directly in your XAML using C#. This is great news for .MET MAUI developers!</summary>
    <published>2026-07-13T15:27:52Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Leomaris Reyes </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/goodbye-valueconverters-hello-csharp-expressions-net-maui"/>
    <content type="text"><![CDATA[<p><span class="featured">C# Expressions mean you can now skip the Converters and add logic directly in your XAML using C#. This is great news for .MET MAUI developers!</span></p><p>Improving and optimizing your code should be your top priority every single day. As a .NET MAUI developer, I know Converters are very familiar to you. They&rsquo;ve helped us transform data and adapt it to the UI. However, we can all agree that sometimes they made our code a bit more verbose than necessary.</p><p>But here&rsquo;s some <strong>BIG news </strong> &hellip; and honestly, one of the most impactful changes in recent times. The Microsoft team, always thinking about developer experience, now allows us to say goodbye to Converters and start using C# Expressions instead! </p><p>What does this mean? You can now add logic directly in your XAML using C#: perform math operations, use string interpolation, boolean logic and much more. In this article, we&rsquo;ll walk through before-and-after scenarios so you can clearly see how much code you can save, not to mention how much more readable and team-friendly your code will become.</p><p>Grab your coffee ☕ and let&rsquo;s get started!</p><h2 id="lets-start">Let&rsquo;s Start!</h2><p>Today, we&rsquo;ll dive directly into the code to explore the differences and how we can use them moving forward. Each example will be broken down into subtopics so you can clearly understand the before and after. </p><h2 id="case-1-simple-property-binding">Case 1: Simple Property Binding</h2><p>When we need to connect the value of a property with the UI, we typically use Bindings followed by the property name. For example, if we want to display values from properties like <code>FullName</code> or <code>Company</code>, we would write something like this:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Label Text="{Binding FullName}" /&gt;

&lt;Label Text="{Binding Company}" /&gt;
</code></pre><h3 id="applying-c-expressions">Applying C# Expressions</h3><p>Now, what if I told you that you can achieve the same result <strong>without using Bindings</strong>? </p><p>With C# Expressions, you can do it in two different ways:</p><p><strong> Implicit syntax</strong></p><pre class=" language-xml"><code class="prism  language-xml">&lt;Label Text="{FullName}" /&gt;

&lt;Label Text="{Company}" /&gt;
</code></pre><p><strong> Explicit syntax</strong></p><p>You can also use the <code>= prefix</code> to make it more explicit that you&rsquo;re using a C# expression.</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Label Text="{= FullName}" /&gt;

&lt;Label Text="{= Company}" /&gt;
</code></pre><p>There is no functional difference between the implicit and explicit approaches; both achieve the same result. You can use whichever style you feel more comfortable with or that best fits your team&rsquo;s coding standards.</p><h2 id="case-2-string-interpolation">Case 2: String Interpolation</h2><p>We&rsquo;re used to formatting strings using <code>StringFormat</code>. For example, if we want to display a price like &ldquo;Total: $28 USD,&rdquo; we first need to define the Binding with the property, and then apply the format using <code>StringFormat='Total: ${0} USD'</code>.</p><p>If we need to include more properties in the format, we must keep adding placeholders, which can make the expression more complex and harder to read as it grows.</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Label Text="{Binding Price, StringFormat='Total: ${0} USD'}" FontSize="18" TextColor="Green" /&gt;
</code></pre><h3 id="applying-c-expressions-1">Applying C# Expressions</h3><p>With C# Expressions, you can do it directly.  Notice how much simpler and more readable the same expression becomes compared to the previous approach:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Label Text="{=$'Total: ${Price} USD'}" FontSize="18" TextColor="Green" /&gt;
</code></pre><h2 id="case-3-multi-root-expressions-calculations">Case 3: Multi-root Expressions (Calculations)</h2><p>I&rsquo;m speechless  &hellip; you can now perform calculations directly in XAML.</p><p>Before seeing how this works now, let&rsquo;s look at how we&rsquo;ve traditionally handled this scenario. To achieve this before, we usually needed to create a computed property in the ViewModel, something like:</p><pre class=" language-csharp"><code class="prism  language-csharp">public decimal Subtotal =&gt; UnitPrice * ItemsCount;
</code></pre><p>And then bind it in XAML:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Label Text="{Binding Subtotal, StringFormat='Subtotal: ${0:F2}'}" 
    FontSize="20" 
    FontAttributes="Bold" /&gt;
</code></pre><h3 id="now-with-c-expressions">Now with C# Expressions</h3><p>One of the most exciting improvements is that we can now perform calculations directly in XAML.</p><p>For example, if we want to display the subtotal of a purchase based on the unit price and the number of items, we no longer need a computed property or a converter for that. We can write the expression exactly where we need it:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Label Text="{=$'Subtotal: ${UnitPrice * ItemsCount:F2}'}" 
    FontSize="20" 
    FontAttributes="Bold" /&gt;
</code></pre><h2 id="case-4-boolean-negations">Case 4: Boolean Negations</h2><p>In case you still had any doubts about saying goodbye to Converters &hellip; </p><p>Before, inverting a boolean value was not that straightforward. If you wanted to show or hide an element based on the opposite value of a property, you couldn&rsquo;t do it directly in XAML. This required creating a converter, registering it in the resources, and then applying it in the binding. A process that, for such a small piece of logic, ended up being unnecessarily complex.</p><p>For example, let&rsquo;s look at this scenario where we want to display a message when the user is not logged in:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Label Text="Please log in to continue" 
    IsVisible="{Binding IsLoggedIn, Converter={StaticResource BooleanInvertConverter}}" 
    TextColor="Red" 
    FontSize="16" /&gt;
</code></pre><p>And also:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;local:BooleanInvertConverter x:Key="BooleanInvertConverter" /&gt;

public class BooleanInvertConverter : IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)C
=&gt; !(bool)value;

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
=&gt; !(bool)value; 
}
</code></pre><h3 id="now-with-c-expressions-1">Now with C# Expressions</h3><p>Look how simple this becomes with just this small piece of code:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Label Text="Please log in to continue" 
    IsVisible="{=!IsLoggedIn}" 
    TextColor="Red" 
    FontSize="16" /&gt;
</code></pre><p>Just one line. No converters, no extra classes, no additional setup. </p><p>And of course, you can also work with boolean expressions without negation, like this:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Switch IsToggled="{HasAccount}" /&gt;
</code></pre><h2 id="case-5-lambda-event-handlers">Case 5: Lambda Event Handlers</h2><p>Traditionally, to handle events in .NET MAUI, we needed to create methods in the code-behind or define Commands in the ViewModel. This meant more code, more structure and, in many cases, more complexity than necessary.</p><p>But before we see how this looks now, let&rsquo;s take a look at how we&rsquo;ve been doing it so far:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Button Text="{Binding ClickCount, StringFormat='Clicked {0} times'}" 
    Command="{Binding IncrementCommand}" 
    BackgroundColor="DarkOrange" 
    TextColor="White" /&gt;
</code></pre><p>In the view model:</p><pre class=" language-csharp"><code class="prism  language-csharp">public ICommand IncrementCommand { get; } 
public int ClickCount { get; set; }
 
public MainViewModel()
{ 
IncrementCommand = new Command(() =&gt;
{ 
    ClickCount++; 
}); 
}
</code></pre><h3 id="now-with-c-expressions-2">Now with C# Expressions</h3><p>Thanks to C# Expressions, we can also use lambda expressions directly in XAML to handle events.  This allows us to execute logic inline, exactly where we need it, making the code more direct, cleaner and much easier to read for simple interactions.</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Button Text="{=$'Clicked {ClickCount} times'}" 
    Clicked="{(s, e) =&gt; ClickCount++}" 
    BackgroundColor="DarkOrange" 
    TextColor="White" /&gt;
</code></pre><h2 id="conclusion">Conclusion</h2><p>And that&rsquo;s it!  In this article, we explored how C# Expressions are changing the way we build .NET MAUI apps. From simple bindings, string formatting and calculations, to boolean logic and even event handling&hellip; we saw how we can now write less code, make it more direct, and achieve things that previously required much more setup and effort.</p><p>I hope this article helps you start simplifying your own code and encourages you to take advantage of this more modern and expressive approach in your .NET MAUI applications.</p><p>If you have any questions or would like me to dive deeper into any of these scenarios, feel free to leave a comment, I&rsquo;ll be happy to help you! </p><p>See you in the next article! &zwj;♀️</p><h2 id="reference">Reference</h2><p>I used a video by Gerald Versluis as a reference for creating this article:</p><div data-sf-ec-immutable="" class="-sf-relative" contenteditable="false" style="width:560px;height:315px;"><div data-sf-disable-link-event=""><iframe width="560" height="315" src="https://www.youtube.com/embed/xPI8hLpyc5k?si=WhgcqoTyB5pV51fa" title="Gerald Versluis | RIP Converters, XAML C# Expressions are here" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"></iframe></div></div>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:a18a06d4-7545-4d98-b443-2c8bf61c38c4</id>
    <title type="text">5 Mistakes Teams Make When Scaling  .NET Applications</title>
    <summary type="text">Avoid these common infrastructure-related mistakes when you’re ready to scale your .NET app to keep your performance at scale too.</summary>
    <published>2026-07-10T16:44:45Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Brien Posey </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/5-mistakes-teams-make-when-scaling-net-applications"/>
    <content type="text"><![CDATA[<p><span class="featured">Avoid these common infrastructure-related mistakes when you&rsquo;re ready to scale your .NET app to keep your performance at scale too.</span></p><p>IT teams often assume that scaling a .NET application means moving it to a larger virtual machine or containerizing the application. While such actions do have their place, performance problems are often tied to infrastructure design rather than the application itself.</p><p>This article discusses some of the most common infrastructure-related mistakes that are often made when scaling a .NET application.</p><h2 id="adding-instances-without-thinking-about-traffic-distribution">1. Adding Instances Without Thinking About Traffic Distribution</h2><p>Application scaling very often involves adding additional nodes to handle the increased demand. However, it is a mistake to add nodes without considering how requests will reach those nodes. Oftentimes organizations will use round-robin load balancing to evenly distribute inbound requests across the available nodes. However, load balancing can break down when session affinity comes into play.</p><p>Session affinity occurs as a result of session data being stored on a specific server. This in turn causes all of a user&rsquo;s requests to be sent to that same server, because that&rsquo;s where the user&rsquo;s session lives. This isn&rsquo;t necessarily a problem by itself, but session affinity can become problematic under the right conditions, especially when there is a mixture of active and inactive users.</p><p>In such situations, a particular instance may become overloaded, even if plenty of capacity exists on other instances, simply because the session is bound to that instance. Additionally, if that instance were to fail, then the users on that instance lose their session.</p><p>A better approach is to design services to be stateless and to store session data within shared storage rather than placing it in an instance&rsquo;s memory. This allows the load balancer to freely distribute requests.</p><h2 id="assuming-that-all-requests-look-alike">2. Assuming That All Requests Look Alike</h2><p>Basic load distribution strategies, such as round robin, assume that every instance can deliver the same level of performance and that every request has the same impact. In reality though, these assumptions are na&iuml;ve.</p><p>Some requests, for example, trigger longer running operations, call external services or make more complex database queries. As such, some requests will inevitably be completed more quickly than others. This can result in some nodes being bogged down while others remain nearly idle.</p><p>The best way to avoid this type of problem is to avoid blindly distributing traffic across nodes. Instead, consider each node&rsquo;s health when making routing decisions. As an example, if a node were experiencing excessive latency or had a significant queue depth, then it would be better to route inbound requests to a node that is under less of a load rather than assigning the request to the struggling node just because it is that node&rsquo;s turn.</p><h2 id="not-having-a-load-shedding-strategy">3. Not Having a Load-Shedding Strategy</h2><p>Although it may seem counterintuitive, load shedding can sometimes be the most appropriate course of action. For those who might not be familiar with the term, load shedding refers to the practice of intentionally rejecting certain requests as a way of helping the system to stay healthy.</p><p>To use an analogy from everyday life, consider what might happen if a restaurant kept seating additional guests in spite of the kitchen and the waitstaff being completely overwhelmed. In such a situation, everyone would receive poor service. Conversely, if the restaurant were to pause and not seat anyone else until the staff had a chance to catch up, then it would result in a better overall experience for everyone.</p><p>Load shedding works similarly. When a system becomes overwhelmed with more requests than the nodes can handle, then the load balancer intentionally fails some requests so that those requests that do make it through can still be serviced normally.</p><h2 id="lack-of-visibility">4. Lack of Visibility</h2><p>This one seems so simple (and it is), yet it is so important. Organizations often make the mistake of just assuming that load balancing is working as intended without doing anything to verify their assumptions. The problem is that if an organization cannot see how requests are being routed, then scaling an application becomes guesswork.</p><p>Without proper visibility, you can&rsquo;t tell whether traffic is being evenly distributed, if there are nodes that are constantly struggling or which instances are handling slow requests. It becomes easy to misdiagnose bottlenecks and blame the application for poor performance when routing is the real problem (or vice versa).</p><p>Ideally, an organization should be able to collect per-instance metrics from its nodes including CPU and memory usage and the request count. There should also be a way to trace requests as they flow across services and there should be a way to track load distribution metrics to see how the individual nodes are being leveraged.</p><h2 id="treating-load-balancing-as-an-afterthought">5. Treating Load Balancing as an Afterthought</h2><p>One of the single biggest mistakes that is often made when scaling an application is treating load balancing as an afterthought. Typically, an application is deployed and works fine. Eventually though, demand increases and the application begins to struggle. IT distributes the application across several nodes and places a basic load balancer in front of those nodes. In these types of situations, load balancers often use default settings because, &ldquo;round robin is good enough for not&rdquo; or &ldquo;the cloud provider&rsquo;s defaults should be fine.&rdquo;</p><p>The end result is that while the application has indeed been scaled, there is a good chance that some of the original problems still exist. The added infrastructure now makes those problems more difficult to diagnose.</p><p>True load balancing is not about spreading traffic evenly. It&rsquo;s about making informed decisions as to where each inbound request should be routed. This informed decision making means using application aware routing to base routing decisions on application behavior, not just routing rules. It also means taking instance health and readiness into account and continually adjusting routing decisions based on latency, errors and available capacity.</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">Using AI to Build a Blazor App 1: Start With the Problem
</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Many AI-assisted projects fail before a line of code is written because the idea was never properly defined. <a target="_blank" href="https://www.telerik.com https://www.telerik.com/blogs/using-ai-build-blazor-app-1-start-problem">Here&rsquo;s how to use an LLM to interrogate your own thinking until a vague premise becomes something you can actually build.</a></p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:3ff2a0b3-8691-4d96-86da-ea2ef3472d6f</id>
    <title type="text">When the Time Is Right</title>
    <summary type="text">Priority tells you what matters. Timing tells you if the moment is right. Here’s why that distinction changes everything about how you build.</summary>
    <published>2026-07-09T13:18:49Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Teon Beijl </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/when-time-right"/>
    <content type="text"><![CDATA[<p><span class="featured">Priority tells you what matters. Timing tells you if the moment is right. Here&rsquo;s why that distinction changes everything about how you build.</span></p><p>A customer threatened to cancel. Full panic. A board meeting to discuss how to retain him, budget approved and allocated to honor his demands. A scrum team started fixing.&nbsp;I flew to the UK to sit with him.</p><p>We had redesigned the entire UI. Not a small fix. We completely overhauled the user experience. From a very old tool with toolbars to an application with a strip.</p><p>We didn&rsquo;t just move buttons to ribbons with tabs. We restructured the entire information architecture. Fixed, opinionated workflows that guided users through the critical steps to model complex geological concepts.</p><p>He hated it. He demanded a rollback.</p><p>I reserved time to interview him. Asked questions. Listened. Tried to sense his reality, his context, where he was coming from. How he was perceiving the changes.</p><p>What I discovered changed everything.</p><p>He wasn&rsquo;t even using the latest version. He hadn&rsquo;t tried the new UI at all. He had simply refused. While we moved on and developed the next generation of our software, he had not. He was still way behind. And somewhere at the back of the queue, feeling ignored and dismissed.</p><p>That feeling made him rage. But it also highlighted a bigger problem.</p><p>In the room next door, two junior geologists were being trained on the exact same concept. They loved it. The design was exactly what the team needed. What he needed too.</p><p>He was desperate for a transformation in his company. A way to step back from his expert role and become a guide for the younger generation. Once I explained the concepts, he understood. He was appreciative of the work. He was on board.</p><p>We almost rolled back an expensive, fundamental shift based on an emotional rant from a customer speaking from expired context.</p><p>It was never about the concept. It was about misunderstanding context.</p><h2 id="the-linearity-trap">The Linearity Trap</h2><p>A backlog is not a queue. It&rsquo;s a context-sensitive collection.</p><p>Every item in it was written at a specific moment, about a specific reality. That reality changes. The backlog doesn&rsquo;t update itself.</p><p>The linearity trap is treating it like a queue anyway. Adding new requests. Reorganizing. Managing delay. Shipping. Without ever asking: does this still belong in the collection?</p><p>The customer had been reporting improvements and bugs for a long time. But we moved on. We shipped. And while we did, he stood still. By the time his feedback reached the top of the queue, the context it was written in had long since changed.</p><p>The problem wasn&rsquo;t that we were behind. We were committing to work that was already outdated.</p><h2 id="delay-is-not-the-problem.-decay-is.">Delay Is Not the Problem. Decay Is.</h2><p>A lot of backlog management is really about managing time. Managing delivery. Focused on shipping as much high-quality work as possible by the promised date.</p><p>Managing delay is a conscious decision. It&rsquo;s about choosing when to push something back. That&rsquo;s prioritization. That&rsquo;s what I wrote about in the previous article.</p><p>But the bigger problem is decay.</p><p>Decay is what happens to the work you&rsquo;re not doing. While you&rsquo;re waiting, while you&rsquo;re delaying, what has been written down is eroding. Rusting. Because context expires.</p><p>It&rsquo;s like the best-before date on a package. By the time you grab it from the shelf, you need to check the date before you consume it.</p><p>A lot of backlogs are just queues constantly filled with requests, feedback and bug reports. Nobody checks the date. Nobody asks: is this still valid?</p><p>I&rsquo;ve seen old backlog items treated as still true when the context they described no longer existed. They were no longer worth pursuing. The more context you capture when you write them, the easier it is to later assess whether they still fit.</p><p>Something can be very old and still be good. But developing the sense to distinguish that is the work. Understanding whether it&rsquo;s expired or not. Whether it&rsquo;s still safe to consume.</p><p>In the UK, the right response would have been to delay any work on his requests. To first discover whether his feedback was based on expired context. Instead, urgency won. And decay nearly cost us everything.</p><h2 id="urgency-vs.-timing">Urgency vs. Timing</h2><p>The urgency that came up was really signaling a demand. Someone pushing. Pressure.</p><p>And under pressure, you don&rsquo;t always make the most strategic decisions. The board panicked. Money appeared. Time was freed up. Nobody stopped to check the return on investment, whether it was worth it. It was an emotional response.</p><p>That happens. We&rsquo;re humans. And that emotion is sometimes also why we close amazing deals and do great things for loyal customers. So it wasn&rsquo;t all wrong. Part of the response was also recognizing the loyalty of a customer who had been with us through harder times.</p><p>But urgency gets the attention. It drives the emotion. And what we need underneath it is judgment.</p><p>Timing is a readiness signal. It&rsquo;s not driven by pressure. It&rsquo;s driven by context.</p><ul><li>Is the customer ready? </li><li>Is the market ready? </li><li>Is the team ready? </li><li>Is the moment alive?</li></ul><p>Those are timing questions. Urgency can&rsquo;t answer them.</p><h2 id="compound-vs.-corrosion">Compound vs. Corrosion</h2><p>We&rsquo;re always looking for momentum. Shipping as much as we can, as fast as possible, without compromising quality.</p><p>But without sensing whether the time is right, we create progress without compounding.</p><p>Compounding is making sure what you ship ties into the next thing and builds over time into the desired version of your software aligned with your vision. Creating conditions so that what comes next makes sense.</p><p>The opposite is corrosion. Work that sits in the backlog long enough starts to corrode. And when you finally act on it, you might ship something that corrodes the foundation of your software rather than building it. Hurting not just momentum but overall quality.</p><p>When we made that big shift to the UI, we should have reassessed the entire backlog. Diagnosed it. Made sure there was no outdated work hiding in the queue because of it.</p><p>We didn&rsquo;t. And a decayed issue almost corroded the core concept.</p><h2 id="context-sensitivity">Context Sensitivity</h2><p>Why did feedback and previous user requests corrode? Because the timeframe wasn&rsquo;t short. That concept developed over multiple months. We should have been capturing the context shift and using it as a filter on our backlog to stay ahead of it.</p><p>But capturing context is the hard part.</p><p>The world moves on. Your market, client and economic reality change constantly. The technical reality shifts the moment you upgrade systems and build with new technologies. The context you&rsquo;re shipping into is constantly moving.</p><p>And on top of that, emotional states. Not everybody handles change well. Not everybody&rsquo;s personal context is aligned with where you&rsquo;re going.</p><p>You need to develop a sense to see those shifts.</p><h2 id="keep-a-human-in-the-loop">Keep a Human in the Loop</h2><p>AI is very good at capturing context. Writing it down. Cross-referencing it. Flagging patterns. It can help us see and track everything we can&rsquo;t manage on our own.</p><p>But the moment context is written down, it&rsquo;s already outdated. And managing that expiration date is a collaborative effort. AI can help cross-check against captured context, flag a customer as a potential churn risk, surface old backlog items that conflict with new ones.</p><p>What it can&rsquo;t do is sit in the room.</p><p>What I did with that customer&mdash;reserving time, asking questions, listening&mdash;that&rsquo;s a nuance. A human sense that becomes more important as we connect more and more data at greater and greater speed.</p><p>AI can read the signal. It can&rsquo;t always hear what&rsquo;s between the lines. The emotional rage that looks like a high-priority request but is really a loyalty problem. The developer who goes quiet in sprint planning. The feedback that reads as a bug report but is actually a cry for better onboarding.</p><p>That&rsquo;s not in the data. It&rsquo;s in the room.</p><h2 id="closure">Closure</h2><p>Timing isn&rsquo;t about managing deadlines. It&rsquo;s about reading whether the moment is right. Priority tells you what matters. Timing tells you whether now is the moment to act on it or not at all.</p><p>The backlog or data alone won&rsquo;t tell you that. You need to read the room. Develop that sense. Check the expiration date. Keep humans in the loop for the context AI can&rsquo;t capture.</p><p>We ship when it matters. We shred when it&rsquo;s expired.</p><p>On purpose. By design.</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">How Depending Leads to Independence</h4></div><div class="col-8"><p class="u-fs16 u-mb0"><a target="_blank" href="https://www.telerik.com/blogs/how-depending-leads-independence">True independence isn&rsquo;t the absence of dependencies.</a> It&rsquo;s an intentional integration. An assembly that&rsquo;s clear, chosen and changeable.</p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:3688cfa4-c260-412e-918e-9889e5046f2a</id>
    <title type="text">Available Now in .NET 11 for .NET MAUI</title>
    <summary type="text">Explore the .NET 11 updates available now in .NET MAUI, including map enhancements, long press recognition, animation improvements and more.</summary>
    <published>2026-07-08T15:35:21Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Leomaris Reyes </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/available-now-net-11-net-maui"/>
    <content type="text"><![CDATA[<p><span class="featured">Explore the .NET 11 updates available now in .NET MAUI, including map enhancements, long press recognition, animation improvements and more.</span></p><p>Oh yeahhh!!  .NET 11 for .NET MAUI is here!</p><p>And, as always, my recommendation is to stay up to date with every new release. What changed? What&rsquo;s new? What has been improved? Keeping up with these updates helps us build better apps and keeps our code inline with the latest recommendations from the Microsoft team.</p><p>For .NET 11, the team&rsquo;s main focus has been improving the overall quality of .NET MAUI, making it faster and easier for us as developers, to build amazing applications. In this article, you&rsquo;ll discover some of my favorite improvements that Microsoft has introduced for .NET MAUI in .NET 11!</p><h2 id="maps-improvements">1. Maps Improvements</h2><p>This is one of my favorite updates in .NET 11!  Let&rsquo;s start by exploring the <strong><a target="_blank" href="https://learn.microsoft.com/es-es/dotnet/api/microsoft.maui.controls.maps.map?view=net-maui-10.0">Map</a></strong> control, which has received some really nice improvements. Here are some of the highlights!</p><h3 id="pin-clustering">Pin Clustering</h3><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/01_improvement_map.png?sfvrsn=9f323c91_2" title=".NET MAUI 11 Map Improvement: Pin Clustering" alt="Illustrated maps. Without clustering the pins, they are everywhere. With clustering enabled, blue circles have numbers for how many pins are in that area" /><br /><span style="font-size:11px;">For educational purposes, this image was generated with AI</span></p><p>Imagine you&rsquo;re building a restaurant app where users can view nearby restaurants on a map. You may have <strong>300, 400 or even more locations</strong> to display. If the user opens the map while zoomed out, it could look something like this: </p><p>This quickly becomes overwhelming because many pins overlap, making it difficult to select a specific location.</p><p>With <strong>.NET 11,</strong> you can simply use this property:</p><pre class=" language-xml"><code class="prism  language-xml">IsClusteringEnabled="True"
</code></pre><p>And with that, .NET MAUI will automatically group nearby pins into clusters. So instead of displaying multiple overlapping pins like , users will see something like (5), indicating that five locations are grouped together.</p><p>As users zoom in, those clusters gradually split into individual pins, creating a much cleaner experience and making it much easier to interact with the map.</p><p>Using it in .NET MAUI is incredibly simple:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;maps:Map IsClusteringEnabled="True"

    ClusterClicked="OnClusterClicked" /&gt;
</code></pre><h3 id="custom-location-markers">Custom Location Markers</h3><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/02_improvement_map.png?sfvrsn=74e1d38_2" title=".NET MAUI 11 Map Improvement: Custom Location Markers" alt="Illustrated maps showing custom location markers for hospitals and pharmacies, instead of the simple pin" /><br /><span style="font-size:11px;">For educational purposes, this image was generated with AI</span></p><p>Yay!!  Another great addition is the ability to <strong>customize your map markers</strong>.</p><p>Instead of using the default pin for every location, you can now display a custom image for each marker, making your maps much more intuitive and visually appealing.</p><p>For example, imagine you&rsquo;re building a healthcare app. You could use one icon for <strong>hospitals</strong> and a different one for <strong>pharmacies</strong>, allowing users to quickly identify each type of location without even reading the labels.</p><p>This is possible by simply setting the <strong>ImageSource</strong> property on the pin.</p><p>Here&rsquo;s what it looks like in code:</p><pre class=" language-csharp"><code class="prism  language-csharp">var pin = new Pin
{ 
    Label = "Custom pin", 
    Location = new Location(18.4861, -69.9312), 
    ImageSource = ImageSource.FromFile("hospitals.png") 
};
</code></pre><h3 id="json-map-style-android">JSON Map Style (Android)</h3><p>Google Maps allows you to customize the map using a JSON file. This means you can change colors, switch between light and dark mode, customize labels and much more.</p><p>Previously, this was much more complicated in .NET MAUI. Now, all you have to do is assign your JSON file to the <strong>MapStyle</strong> property, and you&rsquo;re done! </p><h2 id="cancel-animations-with-cancellationtoken">2. Cancel Animations with CancellationToken</h2><p>This improvement may seem small, but it&rsquo;s actually a very useful one, especially when you have multiple animations running at the same time.</p><p>Before .NET 11, if you started an animation and later wanted to stop it, your only option was to do something like this:</p><pre class=" language-csharp"><code class="prism  language-csharp">image.CancelAnimations();
</code></pre><p>However, there was a small problem.  This method <strong>canceled every animation</strong> running on that control.</p><p>Imagine your image is running two animations simultaneously: a rotation animation and a fade animation. If you only wanted to stop the rotation while keeping the fade animation running, that simply wasn&rsquo;t possible.</p><p>Starting with .NET 11, the animation methods now optionally accept a <strong>CancellationToken</strong>. This means you can cancel a specific animation without affecting the others.</p><p>For example:</p><pre class=" language-csharp"><code class="prism  language-csharp">var cts = new CancellationTokenSource();

await image.FadeToAsync( 
opacity: 0, 
cancellationToken: cts.Token);
</code></pre><p>And when you want to stop that animation:</p><pre class=" language-csharp"><code class="prism  language-csharp">cts.Cancel();
</code></pre><p>That&rsquo;s it! Only the animation associated with that <strong>CancellationToken</strong> will be canceled, while any other animations on the same control will continue running normally.</p><h3 id="methods-without-async-are-now-obsolete">Methods Without Async Are Now Obsolete</h3><p>Another important change is that animation methods without the <strong>Async</strong> suffix have now been marked as obsolete. Methods such as <strong>FadeTo(), RotateTo() and ScaleTo()</strong> are now marked as <strong>[Obsolete].</strong> Instead, Microsoft recommends using their asynchronous counterparts:</p><ul><li>await FadeToAsync(&hellip;);</li><li>await RotateToAsync(&hellip;);</li><li>await ScaleToAsync(&hellip;);</li></ul><p>Of course, these methods also support the new <strong>CancellationToken</strong>, giving you much greater control over your animations.</p><h2 id="longpressgesturerecognizer">3. LongPressGestureRecognizer</h2><p>Have you noticed that some actions in mobile apps are only triggered when you press and hold a control for some seconds? Thanks to <strong>.NET 11</strong>, .NET MAUI now includes the <strong>LongPressGestureRecognizer</strong>, making it easy to detect this gesture.</p><p>It provides several useful features:</p><ul><li>Detects long-press gestures</li><li>Configurable press duration</li><li>Movement threshold to cancel the gesture if the finger moves too far</li><li>Gesture state tracking through GestureState</li><li>Support for Command and CommandParameter</li></ul><p>Using it is as simple as this:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;Image Source="flower.png"&gt; 
&lt;Image.GestureRecognizers&gt; 
&lt;LongPressGestureRecognizer 
    Duration="500" 
    LongPressed="OnLongPressed"/&gt; 
    &lt;/Image.GestureRecognizers&gt; 
&lt;/Image&gt;
</code></pre><p>The event:</p><pre class=" language-csharp"><code class="prism  language-csharp">void OnLongPressed(object sender, LongPressGestureRecognizerEventArgs e) 
{ 
    if (e.State == GestureState.Completed) 
    { 
    // Your code goes here 
    } 
}
</code></pre><h2 id="we-can-finally-use-gradients-in-boxview">4. We Can Finally Use Gradients in BoxView!</h2><p>BoxView is a super useful control that gives us a lot of flexibility when building UI. However, before .NET 11, it had one important limitation: <strong>it could only display a solid color.</strong> So, if you wanted a gradient, you had to rely on other controls or custom solutions.</p><p>With .NET 11, BoxView now includes the Fill property, which is of type Brush. This means you can finally apply gradients directly to a BoxView! And don&rsquo;t worry, if you only need a solid color, you can continue using the BackgroundColor property as before.</p><p>Here&rsquo;s a simple example:</p><pre class=" language-xml"><code class="prism  language-xml">&lt;BoxView HeightRequest="260"&gt; 
    &lt;BoxView.Fill&gt; 
    &lt;LinearGradientBrush&gt; 
    ... 
    &lt;/LinearGradientBrush&gt; 
    &lt;/BoxView.Fill&gt; 
&lt;/BoxView&gt;
</code></pre><h2 id="trimmable-css">5. Trimmable CSS</h2><p>I don&rsquo;t know if you knew this, but .NET MAUI has supported CSS for quite some time. The problem was that even if your application didn&rsquo;t use CSS, .NET MAUI still had to include all the infrastructure required to support it, increasing the final size of your app.</p><p>With .NET 11, that&rsquo;s no longer the case. If your application doesn&rsquo;t use CSS stylesheets, the CSS infrastructure is automatically removed during the publishing process. In short, if you don&rsquo;t use CSS, your application becomes smaller <strong>without you having to do anything</strong>.</p><h2 id="material-3-on-android">6. Material 3 on Android</h2><p>Previously, some .NET MAUI controls on Android were not fully aligned with Material 3. Starting with .NET 11 Preview 4, several Android handlers now use <strong>Material 3</strong> by default. This applies to controls such as:</p><ul><li>ImageButton</li><li>DatePicker</li><li>Entry</li><li>Slider</li></ul><p>In short, it was common to find controls that didn&rsquo;t fully follow Android&rsquo;s modern design guidelines. With .NET 11, that changes. These controls are now much more aligned with Material 3, resulting in a more modern, consistent look that feels fully integrated with the Android ecosystem. </p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/03_improvement_material_controls.png?sfvrsn=9620bef2_2" title=".NET MAUI 11 Material 3 on Android" alt="Material 3 on Android in dark and light mode on .NET MAUI now in >NET 11" /><br /><span style="font-size:11px;">Image obtained from the official documentation</span></p><h2 id="conclusion">Conclusion</h2><p>And that&rsquo;s it! I hope this article helps you discover some of the exciting new features coming to .NET MAUI with .NET 11.</p><p>Throughout this article, we explored some of my favorite improvements, including the new LongPressGestureRecognizer, Map enhancements, BoxView gradients, animation improvements and Android Material 3 support.</p><p>Of course, these are just a few of my favorites! I encourage you to explore the article &ldquo;<a target="_blank" href="https://learn.microsoft.com/en-us/dotnet/maui/whats-new/dotnet-11?view=net-maui-10.0">What&rsquo;s new in .NET MAUI for .NET 11</a>&rdquo; from the official documentation to discover even more improvements and enhancements included in this release.</p><p>I also encourage you to keep experimenting with these new features and continue learning something new every day. </p><p>See you in the next article! &zwj;♀️</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">What&rsquo;s New for Blazor in .NET 11 Preview Releases 1 and 2</h4></div><div class="col-8"><p class="u-fs16 u-mb0">Tour the <a target="_blank" href="https://www.telerik.com/blogs/whats-new-blazor-net-11-preview-releases-1-2">notable Blazor changes and new features in .NET 11 Previews 1 and 2</a> for an early glimpse into what&rsquo;s coming for Blazor in .NET 11.</p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:082495fa-3b2a-46e9-b13f-1d4ee4d3a2a9</id>
    <title type="text">The Importance of Empathy in Automation</title>
    <summary type="text">To take AI beyond automation into true intelligence, we need to include empathy.</summary>
    <published>2026-07-08T13:10:10Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Jefferson S. Motta </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/importance-empathy-automation"/>
    <content type="text"><![CDATA[<p><span class="featured">To take AI beyond automation into true intelligence, we need to include empathy.</span></p><p>In this post, I want to discuss a real danger we may face with AI agents, drawn from a specific interaction I witnessed on LinkedIn.</p><p>Recently, on a post where someone was presenting ideas about &ldquo;intelligent&rdquo; AI agents, the author claimed that if a customer fails to pay a SaaS subscription, the system will block access, trigger an automatic charge and apply a late fee, all without any human intervention. This was promoted as something futuristic and advanced.</p><p>I saw a disaster waiting to happen, especially considering that the same outcome could be achieved without AI, if that were ever desirable.</p><h2 id="what-was-not-considered-the-human-element">What Was Not Considered: The Human Element</h2><p>There are many situations that can lead to a missed payment. What happens, for example, if the client&rsquo;s assistant falls ill and cannot process the payment? What if they had a medical emergency and no one took over the task in time? What if the invoice never arrived? What if the client is going through a temporary hardship, but has been a loyal customer for years? Does that count for nothing? Where is the human element in the consumer relationship?</p><p>These are not far-fetched hypotheticals. They have happened with my own SaaS customers.</p><blockquote><p><strong><em>Automation without humanity is oppression at scale.</em></strong></p></blockquote><p>We could easily implement the AI agent&rsquo;s suggestion and follow the same path: missed payment, blocked account and a fine applied. But is this the right approach?</p><p>My background in neuroscience and communication reshaped how I think about the human side of technology-driven processes. That plus some life experiences lead me to consider the broader context:</p><ul><li>In many jurisdictions, consumer protection laws prohibit unilateral penalties without proper notice. Implementing such a drastic automatic response could have legal implications.</li><li>In society, there is the principle of good faith. Every commercial relationship presumes that both parties act honestly until proven otherwise. This action could have serious consequences for that business relationship and reputation.</li><li>We have to remember to consider the human context. Behind every business and every overdue charge, there are people with stories, setbacks and circumstances that must be considered before any penalty is applied.</li></ul><h2 id="what-we-should-actually-build">What We Should Actually Build</h2><p>A genuinely intelligent AI agent does not block first and ask questions later. It recognizes patterns. Has this client always paid on time? Then the delay probably has a reason. The system could send an empathetic notification before taking any action. It could offer a courtesy window. It could escalate the issue to a human when the situation is ambiguous. An intelligent agent could prioritize retaining a client, which is often worth far more than punishing a late payment.</p><p>For those building AI agents: <strong>before you automate punishment, automate empathy</strong>. Your system will reflect your values. If you build without considering the person on the other side, you could be constructing a scalable injustice machine, one capable of generating damages that could exceed any original problem the AI was trying to address.</p><blockquote><p><strong><em>Technology without empathy is not innovation. It is regression with a polished interface.</em></strong></p></blockquote><h2 id="conclusion">Conclusion</h2><p>We are at a moment where the architecture and design decisions we make today will shape how millions of people are treated by systems over which they have no control.</p><p>And to help avoid building oppressive and unjust machines, we need to cultivate empathy and uphold our organizations&rsquo; principles, with the greater purpose of <strong>serving people</strong> rather than merely extracting value from them.</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 Can&rsquo;t Solve It All: What Frontend Devs Still Hate Working On</h4></div><div class="col-8"><p class="u-fs16 u-mb0">What still causes the most friction when building modern web applications? <a target="_blank" href="https://www.telerik.com/blogs/ai-cant-solve-all-what-120-frontend-developers-say-they-still-hate-working">120+ developers at JSNation and React Summit weigh in.</a></p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:8720cf41-2dac-4533-a2fe-cf1af00e248f</id>
    <title type="text">Using AI to Build a Blazor App 1: Start With the Problem</title>
    <summary type="text">Many AI-assisted projects fail before a line of code is written because the idea was never properly defined. Here’s how to use an LLM to interrogate your own thinking until a vague premise becomes something you can actually build.</summary>
    <published>2026-07-07T15:04:02Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Jon Hilton </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/using-ai-build-blazor-app-1-start-problem"/>
    <content type="text"><![CDATA[<p><span class="featured">Many AI-assisted projects fail before a line of code is written because the idea was never properly defined. Here&rsquo;s how to use an LLM to interrogate your own thinking until a vague premise becomes something you can actually build.</span></p><p>How do you go from idea to working software? That&rsquo;s the challenge we face as software developers.</p><p>While AI <strong>feels</strong> like it&rsquo;s changed everything, it hasn&rsquo;t changed that fundamental problem.</p><p>It&rsquo;s not a question of how quickly AI can write code (answer: very quickly), but rather how soon can we find out if our assumptions are correct and if we&rsquo;re building the right thing to move the needle.</p><p>Do we even know what needle we&rsquo;re trying to move in the first place?</p><p>In this short series, we&rsquo;ll explore how we can use AI to build the right things, to solve the most urgent problems, without shipping substandard code.</p><ul><li>We&rsquo;ll start with a vague premise and see if AI can help us sharpen our understanding of what we&rsquo;re building (and why).</li><li>Then we&rsquo;ll move on to working with AI to carve out the headline feature we want to build first.</li><li>We&rsquo;ll remove ambiguity (and reach a shared understanding of what we&rsquo;re building) using prototypes and tracer bullets.</li><li>Then we&rsquo;ll establish the architecture of what we&rsquo;re building (including key technology choices), break the work down into smaller steps and build it.</li></ul><p>First, we need to get clear about what we&rsquo;re building.</p><h2 id="a-journey-of-a-thousand-miles-begins-with-a-single-step">A Journey of a Thousand Miles Begins with a Single Step</h2><p>If you&rsquo;ve been experimenting with AI, you already know the big AI trap. The temptation to just throw a vague prompt its way, cross your fingers and hope it magically builds the perfect web application (while you sleep).</p><p>In reality it&rsquo;s not that simple.</p><p>AI will happily run off and build whatever you ask it to, which is problematic if you ask it to build the wrong thing, start with fuzzy requirements or give it too much to do in one step.</p><p>So, the first task is the same as it&rsquo;s always been: get clear about the problem you&rsquo;re solving.</p><p>In this case, I started with a simple problem to solve. I have a goal of writing at least one article a week for my personal blog and also an email to my newsletter audience (based on the same blog post). In reality, that cadence is rarely once a week, and often drifts to once a month or, in the worst case, once &ldquo;every now and then.&rdquo;</p><p>So starting with that problem, I gave this prompt to GPT 5.5 (via the Codex GUI):</p><blockquote><p>I want to build a new web application using .NET 10 and Blazor. Please grill me on the idea, what it&rsquo;s for, key technology choices, and help me scope out a reasonable first version so we can get user feedback as soon as possible</p></blockquote><p>This did <strong>not</strong> go well.</p><p>GPT 5.5 responded with a giant wall of text and lots of questions. It started diving into implementation details about Blazor (which render mode to use, etc.) and essentially skipped the part where it finds out what I&rsquo;m trying to build before focusing on those implementation details.</p><p>I started over, and tried different variations of the same prompt.</p><p>First I got it to ask me one question at a time, which worked better, but I realized it was still jumping to technical details. I wanted to keep it focused on problems and ideal outcomes, not technical matters or even defining the feature details at this stage.</p><p>After a few iterations we arrived at this:</p><blockquote><p>I want to build a new web application using .NET 10 and Blazor.</p><p>Please grill me on the idea.</p><p>Ask me one question at a time, and include your brief recommendation for each question.</p><p>Dig into each answer until that thread is clear, then move on to the next question.</p><p>For notable decisions (anything fundamental, or where the reasoning for which can&rsquo;t be deduced from implementation), capture that decision as an ADR in a decisions folder.</p><p>Help me explore the problem space, and the core outcomes we want this application to achieve.</p><p>Do not scope features at this point, that will come next.</p></blockquote><p>This worked better.</p><p>Over the course of a 45-minute back-and-forth, we fleshed out the problem I was trying to solve, and the rough shape of any potential solution. I won&rsquo;t include the entire conversation here, but here are some highlights.</p><p>It asked:</p><blockquote><p>Where do ideas currently appear first: while at your computer, on your phone, in notes, in code/projects, from conversations, or somewhere else?</p></blockquote><p>The answer I gave included this phrase: &ldquo;Could be anywhere really.&rdquo;</p><p>GPT 5.5 wasn&rsquo;t having that.</p><blockquote><p>This one isn&rsquo;t quite a decision yet, because &ldquo;could be anywhere&rdquo; can lead to a lot of product surface.</p><p>Follow-up question:</p><p>For the first version, is it acceptable if mobile capture is only a lightweight inbox, with real writing/editing still expected to happen at a keyboard?</p></blockquote><p>In my experience, AI shines with this kind of process, digging into answers you give it and getting clear so you can make concrete decisions, not just hand-wavey &ldquo;ideas.&rdquo;</p><p>Some more questions it posed:</p><blockquote><p>What would make this application feel like a calm writing system rather than another obligation to maintain?</p></blockquote><blockquote><p>What is the smallest publishing rhythm that would still feel successful: exactly one post/email every week, or a sustainable average with occasional skips and catch-ups?</p></blockquote><blockquote><p>What would make you stop using this app, even if it technically worked?</p></blockquote><p>Notice how GPT 5.5 stayed on task here, exploring the problem and not getting into feature design, or technical choices.</p><p>Eventually we arrived at this summary:</p><blockquote><p>The core shape is now clear: a local-first, single-user Blazor/.NET app that helps you maintain a weekly blog-plus-newsletter rhythm, with the Astro repo as source of truth, a calm Markdown writing experience, lightweight AI assistance, and explicit publishing/Git steps.</p></blockquote><p>The main technical decision made was the one I started with (that we&rsquo;d be using Blazor to build it) and some high level thoughts around source of truth, and the existing systems (like my blog).</p><p>We also ended up with a number of architecture decision records (ADRs) captured in markdown documents. ADRs are typically used to capture technical decisions, but were also useful here for capturing the decisions we arrived at around the problem and solution itself.</p><p>Here&rsquo;s one example:</p><pre class=" language-markdown"><code class="prism  language-markdown"><span class="token title important"><span class="token punctuation">#</span> ADR 0028: Slug Generation</span>
Status
Accepted

<span class="token title important"><span class="token punctuation">##</span> Context</span>
Blog posts need a final URL slug. The slug should usually be short, readable, and aligned with the article's content or main takeaway.

Manual override is sometimes needed.

<span class="token title important"><span class="token punctuation">##</span> Decision</span>
The application should generate slugs automatically from the article title or main takeaway, while allowing the user to override them.

Slugs should be treated as stable after publication unless deliberately changed.

<span class="token title important"><span class="token punctuation">##</span> Consequences</span>
Slug generation should optimize for short, meaningful URLs rather than blindly mirroring the full title.
The user must retain control over the final slug.
Published slug changes should be treated as deliberate edits, not incidental metadata updates.
</code></pre><p>Whenever I work through an AI interview like this, I find I emerge with a much clearer picture of what I&rsquo;m building (and why). The LLM&rsquo;s questions force me to think and often surface angles I hadn&rsquo;t previously considered. In some cases I&rsquo;ve gone on to realize building a feature is actually unnecessary (a change to existing habits or processes would achieve the same result).</p><p>In this case, though, we&rsquo;re on the right track and have a clearly scoped brief for an editor to capture ideas and author content.</p><h2 id="make-it-work-for-you">Make It Work for You</h2><p>When you try to use AI to build something like this, you might feel like there should be some simple streamlined process you can work through. A clean process where you feed a feature idea in, AI gives you perfect code out and you ride off into the sunset.</p><p>Alas, software design is anything but streamlined, and no AI tool is going to take the chaos of defining and refining an idea and handle it in a nice straight line.</p><p>In this case, getting AI to grill you about the idea is one part of the design process, but there are others, which we&rsquo;ll come to shortly. We started high-level here because this was an embryonic idea and a greenfield application. But if you already had an app and were adding a feature, you might skip this step and jump straight into designing a specific feature/requirement.</p><h2 id="iterate-your-prompts">Iterate Your Prompts</h2><p>But what about the prompt itself? Did I happen upon some perfect, fully formed prompt that made AI work perfectly? In short, no!</p><p>Prompts rarely arrive fully formed. In this case, I started with a vague prompt, then refined and tested it until I found one that worked better for my needs. This is a key difference I&rsquo;ve noticed between people who get good results from an LLM and those who feel frustrated.</p><p>It&rsquo;s useful to learn how to write effective prompts, or &ldquo;meta prompts&rdquo; (where you get AI to write the prompt for you), and run rapid iterations so you can get something that works for you. Here it took around five minutes to get to a prompt that worked well for this exercise.</p><p>You can also turn to examples published online (Matt Pocock&rsquo;s skills are a clear standout), but it&rsquo;s useful to learn how to create your own skills/prompts, based on your own unique workflow and preferences.</p><p>With that said, we&rsquo;ve carved out our idea: a simple &ldquo;surface&rdquo; for capturing ideas and turning those into articles (with the source of truth being an MDX in an Astro blog).</p><p>Now to define the headline features. That&rsquo;s next, in <a href="https://www.telerik.com/blogs/using-ai-build-blazor-app-2-define-requirement" target="_blank">Part 2</a>.</p><aside><hr data-sf-ec-immutable="" /><div class="row"><div class="col-4 u-normal-full u-small-mb0"><h4 class="u-fs20 u-fw5 u-lh125 u-mb0">AI Can&rsquo;t Solve It All: What Frontend Devs Still Hate Working On</h4></div><div class="col-8"><p class="u-fs16 u-mb0">What still causes the most friction when building modern web applications? <a target="_blank" href="https://www.telerik.com/blogs/ai-cant-solve-all-what-120-frontend-developers-say-they-still-hate-working">120+ developers at JSNation and React Summit weigh in.</a></p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:23c4afbf-be58-4e27-8443-2bfe893aab86</id>
    <title type="text">Getting Started with Angular Signal Forms</title>
    <summary type="text">Learn the basics of Angular Signal Forms, including models, validation and two-way synchronization, to build reactive forms with confidence.</summary>
    <published>2026-07-07T12:13:00Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Dhananjay Kumar </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/getting-started-angular-signal-forms"/>
    <content type="text"><![CDATA[<p><span class="featured">Learn the basics of Angular Signal Forms, including models, validation and two-way synchronization, to build reactive forms with confidence.</span></p><p>Angular version 21.0 introduced one of the most exciting and awaited features: Signal Forms. Traditionally, there have been two types of forms:</p><ol><li>Template Forms</li><li>Reactive Forms</li></ol><p>Angular 21.0 introduced a new form type:</p><ol start="3"><li>Signal Forms</li></ol><p>And now,&nbsp;<a href="https://www.telerik.com/blogs/angular-22-evolution-modern-angular" target="_blank">Angular 22</a> makes Signal Forms stable.&nbsp;Signal Forms are a combination of Signal-based reactivity and forms. In my opinion, Signal Forms are the future of forms and user input in Angular.</p><p>This article gives you a basic introduction to Signal Forms and shows you how to work with models, validation and two-way synchronization.</p><h2 id="first-signal-form">First Signal Form</h2><p>Signal-based forms use a new form function. It creates a form from a writable signal model, where the model is the source of truth, and any change in the form or the model is automatically reflected in the other.</p><p>Let&rsquo;s start by creating an interface to represent the user:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">IUser</span> <span class="token punctuation">{</span>
    email<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
    password<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>Next, we create a model that will be used to create the form, as shown below. As mentioned earlier, the model must be a writable signal.</p><pre class=" language-ts"><code class="prism  language-ts">user <span class="token punctuation">:</span> WritableSignal<span class="token operator">&lt;</span>IUser<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token function">signal</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    email<span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
    password<span class="token punctuation">:</span> <span class="token string">''</span>  
  <span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre><p>We can create a signal-based form using the <code>form()</code> function from the <code>@angular/forms/signals</code>.</p><pre class=" language-ts"><code class="prism  language-ts">loginForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>user<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>That&rsquo;s it. You have created a signal-based form in Angular.</p><p>The form function returns a <code>FieldTree</code>, where each field is represented as a signal. In other words, the form function produces a data structure in which every field is a signal enriched with signal-based properties such as value, dirty, invalid and others.</p><p>To better understand this structure, let&rsquo;s print the following values:</p><pre class=" language-ts"><code class="prism  language-ts">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>loginForm<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 function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">loginForm</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">valid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>loginForm<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 function">invalid</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>loginForm<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 function">dirty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>loginForm<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 function">touched</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>loginForm<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 function">errors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>As shown in the example above, the email field is a signal, and all of its properties are signals as well. Because both the field and its properties are signals, you can use them directly in the template, as demonstrated below.</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginForm.email<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginForm.password<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
</code></pre><p>To use <code>formField</code>, import it into the component class, as shown below:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> form<span class="token punctuation">,</span> FormField <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/forms/signals'</span><span class="token punctuation">;</span>

@<span class="token function">Component</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  selector<span class="token punctuation">:</span> <span class="token string">'app-login'</span><span class="token punctuation">,</span>
  imports<span class="token punctuation">:</span> <span class="token punctuation">[</span>FormField<span class="token punctuation">]</span><span class="token punctuation">,</span>
  templateUrl<span class="token punctuation">:</span> <span class="token string">'./login.html'</span><span class="token punctuation">,</span>
  styleUrl<span class="token punctuation">:</span> <span class="token string">'./login.css'</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">Login</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</code></pre><p>As discussed earlier, the <code>form()</code> function creates a form from the given model, where the model is the single source of truth, so changes in the form update the model and changes in the model update the form.</p><p>In simple words, the model and the form are connected through two-way data binding. This means that when the model signal changes, the form value updates accordingly, and when the form value changes, the model updates as well.</p><p>To demonstrate this, read the model signal value inside an effect, as shown below:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token function">effect</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 keyword">this</span><span class="token punctuation">.</span><span class="token function">user</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>As soon as you start typing in the form, you can read the updated value in the effect. Similarly, when you change the model signal value, the form value is reflected as well.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">this</span><span class="token punctuation">.</span>user<span class="token punctuation">.</span><span class="token keyword">set</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  email<span class="token punctuation">:</span> <span class="token string">'a@a.com'</span><span class="token punctuation">,</span>
  password<span class="token punctuation">:</span><span class="token string">'abc'</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 keyword">this</span><span class="token punctuation">.</span><span class="token function">loginForm</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// updated value of the form</span>
</code></pre><p>In summary, in signal-based forms, the model used by the form and the form itself are connected via two-way data binding. Updating one automatically updates the other.</p><h3 id="adding-validations">Adding Validations</h3><p>The <code>form()</code> function in Signal Forms makes it simple to add validators to form fields. You can pass a schema function to the <code>form()</code> function to add validators to the form.</p><p>In Signal Forms, validations are defined using a schema function. You pass this schema function as the second parameter to the <code>form()</code> function.</p><p>You can add the required validators to the email and password fields as shown below:</p><pre class=" language-ts"><code class="prism  language-ts">loginForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>userModel<span class="token punctuation">,</span><span class="token punctuation">(</span>schema<span class="token punctuation">)</span><span class="token operator">=&gt;</span><span class="token punctuation">{</span>
  <span class="token function">required</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>email<span class="token punctuation">,</span><span class="token punctuation">{</span>message<span class="token punctuation">:</span><span class="token string">'email is required'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token function">email</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>email<span class="token punctuation">,</span><span class="token punctuation">{</span>message<span class="token punctuation">:</span><span class="token string">'email is not valid'</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token function">required</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>password<span class="token punctuation">,</span><span class="token punctuation">{</span>message<span class="token punctuation">:</span><span class="token string">'password is 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 punctuation">;</span>
</code></pre><p>We have added the required and email built-in validators and used the message option to provide custom error messages.</p><p>As discussed earlier, in a Signal Form, all properties of a field are signals. This allows you to use them directly. For example, to display validation error messages for the email field, as shown below.</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>
  Email
  <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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginForm.email<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>
@if (loginForm.email().invalid() &amp;&amp; loginForm.email().touched()) {
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>error<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    @for (error of loginForm.email().errors(); track error.kind) {
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>{{ error.message }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
    }
  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
}
</code></pre><p>In the same way, error messages for the password field can be displayed as below:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>
        Password
        <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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginForm.password<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>
    @if (loginForm.password().invalid() &amp;&amp; loginForm.password().touched()) {
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>error<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        @for (error of loginForm.password().errors(); track error.kind) {
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>{{ error.message }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        }
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    }
</code></pre><h3 id="submitting-the-form">Submitting the Form</h3><p>To complete the form, you can add submit functionality by importing the <code>submit()</code> function and using that to submit the form.</p><pre class=" language-ts"><code class="prism  language-ts">  <span class="token function">onSubmit</span><span class="token punctuation">(</span>event<span class="token punctuation">:</span> Event<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    event<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 function">submit</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>loginForm<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">const</span> u <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">userModel</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">'Logging in with:'</span><span class="token punctuation">,</span> u<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>submit()</code> function runs the logic only when the form is valid.</p><p>In the template, the form submit event can bind to the <code>onSubmit ()</code> function using event binding, as shown below. You can also disable the submit button when the form is invalid.</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">(submit)</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>onSubmit($event)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    // other code for fields &hellip;..
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">[disabled]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginForm().invalid()<span class="token punctuation">"</span></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 punctuation">&gt;</span></span>Login<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>
</code></pre><h3 id="complete-signal-based-login-form">Complete Signal-based Login Form</h3><p>Putting everything together, the login form using Signal Forms can be implemented as shown below.</p><p>The IUser interface is created as shown below:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">IUser</span> <span class="token punctuation">{</span>
    email<span class="token punctuation">:</span><span class="token keyword">string</span><span class="token punctuation">;</span> 
    password <span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span> 
<span class="token punctuation">}</span>
</code></pre><p>The Login component using a Signal Form is created as shown below.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> Component<span class="token punctuation">,</span> signal<span class="token punctuation">,</span> WritableSignal <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> IUser <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'../user'</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> email<span class="token punctuation">,</span> form<span class="token punctuation">,</span> FormField<span class="token punctuation">,</span> required<span class="token punctuation">,</span> submit <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/forms/signals'</span><span class="token punctuation">;</span>

@<span class="token function">Component</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  selector<span class="token punctuation">:</span> <span class="token string">'app-login'</span><span class="token punctuation">,</span>
  imports<span class="token punctuation">:</span> <span class="token punctuation">[</span>FormField<span class="token punctuation">]</span><span class="token punctuation">,</span>
  templateUrl<span class="token punctuation">:</span> <span class="token string">'./login.html'</span><span class="token punctuation">,</span>
  styleUrl<span class="token punctuation">:</span> <span class="token string">'./login.css'</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">Login</span> <span class="token punctuation">{</span>

  userModel<span class="token punctuation">:</span> WritableSignal<span class="token operator">&lt;</span>IUser<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token function">signal</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    email<span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
    password<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>

  loginForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>userModel<span class="token punctuation">,</span> <span class="token punctuation">(</span>schema<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">required</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'email is required'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">email</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'email is not valid'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">required</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>password<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'password is 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 punctuation">;</span>

  <span class="token function">onSubmit</span><span class="token punctuation">(</span>event<span class="token punctuation">:</span> Event<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    event<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 function">submit</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>loginForm<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">const</span> u <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">userModel</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">'Logging in with:'</span><span class="token punctuation">,</span> u<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre><p>In the template, the form can be created as shown below:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">(submit)</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>onSubmit($event)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>
        Email
        <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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginForm.email<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>
    @if (loginForm.email().invalid() &amp;&amp; loginForm.email().touched()) {
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>error<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        @for (error of loginForm.email().errors(); track error.kind) {
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>{{ error.message }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        }
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    }

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>
        Password
        <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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginForm.password<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>
    @if (loginForm.password().invalid() &amp;&amp; loginForm.password().touched()) {
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>error<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        @for (error of loginForm.password().errors(); track error.kind) {
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>{{ error.message }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        }
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    }
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">[disabled]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginForm().invalid()<span class="token punctuation">"</span></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 punctuation">&gt;</span></span>Login<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>
</code></pre><h3 id="understanding-forms-models">Understanding Forms Models</h3><p>Signal Forms are backed by a model, which is a writable signal. When the model value changes, the form updates automatically. Likewise, when the user interacts with the form, the model is updated. In this way, the model and the form are connected through two-way binding, which means changes in one are reflected in the other.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/model-signal-form.png?sfvrsn=a528e8f8_2" alt="Model and Signal Form exchange change updates" /></p><p>It is always best practice to use an interface to define the model. For example, if we are creating a form to edit a product, we can define a type for it as shown below.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">IProduct</span><span class="token punctuation">{</span>
    id<span class="token punctuation">:</span><span class="token keyword">number</span><span class="token punctuation">;</span>
    name<span class="token punctuation">:</span><span class="token keyword">string</span><span class="token punctuation">;</span>
    price<span class="token punctuation">:</span><span class="token keyword">number</span><span class="token punctuation">;</span>
    description<span class="token punctuation">:</span><span class="token keyword">string</span><span class="token punctuation">;</span>
    category<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">export</span> <span class="token keyword">interface</span> <span class="token class-name">IProductForm</span> <span class="token keyword">extends</span> <span class="token class-name">Omit</span><span class="token operator">&lt;</span>IProduct<span class="token punctuation">,</span> <span class="token string">'id'</span> <span class="token operator">|</span> <span class="token string">'category'</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
</code></pre><p>Now to edit the product, fetch it from the API using the resource API as shown below:</p><pre class=" language-ts"><code class="prism  language-ts">    idToFetch <span class="token operator">=</span> <span class="token function">signal</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    productsResource <span class="token operator">=</span> <span class="token function">resource</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        params<span class="token punctuation">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>idToFetch<span class="token punctuation">,</span>
        loader<span class="token punctuation">:</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> params <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">let</span> id <span class="token operator">=</span> params<span class="token punctuation">;</span>
            <span class="token keyword">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`http://localhost:3000/product/</span><span class="token interpolation"><span class="token interpolation-punctuation 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 punctuation">;</span>
            <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>response<span class="token punctuation">.</span>ok<span class="token punctuation">)</span> <span class="token punctuation">{</span>
                <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token template-string"><span class="token string">`HTTP error! status: </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>response<span class="token punctuation">.</span>status<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">}</span>
            <span class="token keyword">return</span> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> Promise<span class="token operator">&lt;</span>IProductForm<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>
</code></pre><p>We have received the product data to be updated from the API. In the next step, we will create a signal-based form to bind this data and allow the user to update the product.</p><pre class=" language-ts"><code class="prism  language-ts">   productUpdateModel <span class="token operator">=</span> signal<span class="token operator">&lt;</span>IProductForm<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
        name<span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
        price<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
        description<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>

    productForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>productUpdateModel<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>That&rsquo;s it. Now, in the template, use the <code>formField</code> directive to display the fetched product data in the form, as shown below.</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">(submit)</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>onSubmit($event)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>
        Name
        <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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>productForm.name<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>
        Price
        <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>number<span class="token punctuation">"</span></span> <span class="token attr-name">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>productForm.price<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>
        Description
        <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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>productForm.description<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>

    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>button</span> <span class="token attr-name">[disabled]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>productForm().invalid()<span class="token punctuation">"</span></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 punctuation">&gt;</span></span>Update<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>
</code></pre><p>To update the product, let&rsquo;s add an input field and a button to load the product.</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>Update Product Id : &lt;input #productIdInput type="number" [value] ='idToFetch()'/&gt;<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">(click)</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>fetchProduct(+productIdInput.value)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Load Product<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>To fetch the updated product, set the signal accordingly. There are multiple ways to bind the value returned from the API to the <code>productUpdateModel</code>. You can either use <code>productResource.value()</code> directly in the template or assign it as shown below.</p><pre class=" language-ts"><code class="prism  language-ts">   <span class="token function">fetchProduct</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 punctuation">{</span>
        <span class="token keyword">this</span><span class="token punctuation">.</span>idToFetch<span class="token punctuation">.</span><span class="token keyword">set</span><span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>productsResource<span class="token punctuation">.</span><span class="token function">hasValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span>productUpdateModel<span class="token punctuation">.</span><span class="token keyword">set</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>productsResource<span class="token punctuation">.</span><span class="token function">value</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>As you see from the above example, the form model and form fields are in two-way data-binding synchronisation, and changes flow bidirectionally between them.</p><p>The interaction between user input and the model follows these steps:</p><ol><li>The user enters text or selects a value from the dropdown.</li><li>The <code>formField</code> directive detects the change.</li><li>The corresponding <code>FieldState</code> is updated.</li><li>The model signal is updated accordingly.</li></ol><p>This flow is illustrated in the diagram below.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/user-input-model-interaction.png?sfvrsn=1c3ac370_2" alt="user inputs. formField directive detects the change. FieldState updated. Model signal updates" /></p><p>The flow between the model and the user input follows the steps below:</p><ol><li>Use code to <code>set()</code> or <code>update()</code> signal model</li><li>Notifies all subscribers</li><li>Field state updates</li><li><code>formField</code> directive updates the input element</li></ol><p>This flow is illustrated in the diagram below.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/set-update-signal-model.png?sfvrsn=19cf05a3_2" alt="set() or update() signal model. Notifies all subscribers. Field state updates. formField directive updates input element." /></p><p>Two-way data synchronization can be better understood by the following simple code example.</p><pre class=" language-ts"><code class="prism  language-ts">count <span class="token operator">=</span> <span class="token function">signal</span><span class="token punctuation">(</span><span class="token punctuation">{</span> v<span class="token punctuation">:</span> <span class="token number">0</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  countInput <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token function">updateCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>count<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span>c <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> v<span class="token punctuation">:</span> c<span class="token punctuation">.</span>v <span class="token operator">+</span> <span class="token number">1</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
</code></pre><p>And on the template:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>number<span class="token punctuation">"</span></span> <span class="token attr-name">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>countInput.v<span class="token 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">(click)</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>updateCount()<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>update count<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>p</span><span class="token punctuation">&gt;</span></span>Count: {{ count().v }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>p</span><span class="token punctuation">&gt;</span></span>
</code></pre><p>When you run the above code, you will see that the input and model value are in sync with each other.</p><h2 id="optional-fields">Optional Fields</h2><p>Always use explicit TypeScript type to define the model. So to create a login form create a type like below.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">IUser</span> <span class="token punctuation">{</span>
    email<span class="token punctuation">:</span><span class="token keyword">string</span><span class="token punctuation">;</span> 
    password <span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span> 
    rememeberme<span class="token operator">?</span><span class="token punctuation">:</span> <span class="token keyword">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p>You use them to create a signal form as shown below:</p><pre class=" language-ts"><code class="prism  language-ts">  userModel<span class="token punctuation">:</span> WritableSignal<span class="token operator">&lt;</span>IUser<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token function">signal</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    email<span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
    password<span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
    rememberMe<span class="token punctuation">:</span> <span class="token keyword">null</span>
  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

  loginForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>userModel<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>When you define a form using explicit types as shown above, the resulting field tree is fully type-safe. For example, <code>loginForm.email</code> is inferred as <code>FieldTree&lt;string&gt;</code>, and any attempt to access a property that doesn&rsquo;t exist will produce a compile-time error. In the code below, TypeScript will infer type of email as string.</p><pre class=" language-ts"><code class="prism  language-ts">email <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>loginForm<span class="token punctuation">.</span>email<span class="token punctuation">;</span>
</code></pre><p>And to set the optional field:</p><ul><li>Set the value explicitly to null</li><li>For input fields such <code>&lt;input type=&rdquo;text&rdquo; /&gt;</code>, set the value to empty string.</li><li>Do not use undefined</li></ul><p>Fields explicitly set to undefined are omitted from the field tree. A model defined as <code>{ value: undefined }</code> behaves the same as <code>{}</code>. Accessing that field returns undefined instead of a <code>FieldTree</code> instance.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/optional-fields.png?sfvrsn=916d9551_2" alt="Optional fields. Use null for optional fields. Do not use undefined, as it won’t be part of FieldTree." /></p><h2 id="nested-objects-in-model">Nested Objects in Model</h2><p>You can use nested objects to create a form like below:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">IProduct</span> <span class="token punctuation">{</span>
    id<span class="token punctuation">:</span><span class="token keyword">number</span><span class="token punctuation">;</span>
    name<span class="token punctuation">:</span><span class="token keyword">string</span><span class="token punctuation">;</span>
    price<span class="token punctuation">:</span><span class="token keyword">number</span><span class="token punctuation">;</span>
    description<span class="token punctuation">:</span><span class="token keyword">string</span><span class="token punctuation">;</span>
    category<span class="token punctuation">:</span><span class="token keyword">string</span><span class="token punctuation">;</span>
    vendor <span class="token punctuation">:</span> IVendor<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">IVendor</span><span class="token punctuation">{</span>
    id<span class="token punctuation">:</span><span class="token keyword">number</span><span class="token punctuation">;</span>
    name<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>
    phone<span class="token punctuation">:</span><span class="token keyword">string</span><span class="token punctuation">;</span>   
<span class="token punctuation">}</span>
</code></pre><p>Next, you can create the form using the nested object as shown below:</p><pre class=" language-ts"><code class="prism  language-ts">   productModel <span class="token operator">=</span> signal<span class="token operator">&lt;</span>IProduct<span class="token operator">&gt;</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">''</span><span class="token punctuation">,</span>
    price<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
    description<span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
    category<span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">,</span>  
    vendor<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">''</span><span class="token punctuation">,</span>
      email<span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
      phone<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>

   productForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>productModel<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>In the template, you can access nested object properties using dot notation, as shown below.</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>form</span> <span class="token attr-name">(submit)</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>onSubmit($event)<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>productForm.name<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>Vendor 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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>productForm.vendor.name<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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 punctuation">&gt;</span></span>Submit<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>
</code></pre><p>Before moving to the validation section, note that the form model and the domain model are two different concepts and should be treated separately.</p><p>Your API response may map directly to the domain model, but you should define a dedicated form model when working with signal-based forms.</p><p>We will explore this distinction in more detail in a separate article.</p><h2 id="validations">Validations</h2><p>You pass validation rules to the Signal Forms using a schema. You pass schema as second parameter to the form function.</p><pre class=" language-ts"><code class="prism  language-ts">  userModel<span class="token punctuation">:</span> WritableSignal<span class="token operator">&lt;</span>IUser<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token function">signal</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
    email<span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">,</span>
    password<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>


  loginForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>userModel<span class="token punctuation">,</span> <span class="token punctuation">(</span>schema<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">required</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'email is required'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">email</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'email is not valid'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">required</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>password<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'password is 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 punctuation">;</span>
</code></pre><p>As shown above, you provide a schema definition along with validation rules to apply validations to the respective fields of the form.</p><p>The schema function gets a <code>SchemaPathTree</code> object that lets us define the validation rules. The schema function runs at the time of initialization, and validation runs automatically whenever field values change.</p><p>On the template, you can use the validation as shown below:</p><pre class=" language-html"><code class="prism  language-html"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>label</span><span class="token punctuation">&gt;</span></span>
        Email
        <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">[formField]</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>loginForm.email<span class="token punctuation">"</span></span> <span class="token punctuation">/&gt;</span></span>
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>label</span><span class="token punctuation">&gt;</span></span>
    @if (loginForm.email().invalid() &amp;&amp; loginForm.email().touched()) {
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>error<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
        @for (error of loginForm.email().errors(); track error.kind) {
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span><span class="token punctuation">&gt;</span></span>{{ error.message }}<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        }
    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
    }
</code></pre><p>Validation works in these steps:</p><ol><li>Define validation rules in schema</li><li>Validation rules runs when filed value changes</li><li>Validation errors are propagated as signals</li><li>UI automatically updates when validation state changes</li></ol><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/define-validation-rules-flow.png?sfvrsn=d710c039_2" alt="Define validation rules. Automatic execution of validation rules. Validation error propagates as Signals. Reactive update of UI on validation state changes" /></p><p>There are two types of validations:</p><ol><li>Synchronous validation &ndash; <code>required()</code>, <code>email()</code>, etc.</li><li>Asynchronous validation &ndash; <code>validateHttp()</code></li></ol><p>When a field value changes, Angular runs validation in the sequence:</p><ol><li>First it runs all synchronous validations.</li><li>If all synchronous validation pass, then it runs asynchronous validation.</li><li>After that, it updates the field state signal values such as <code>valid()</code>, <code>invalid()</code>, <code>errors()</code> and <code>pending()</code>.</li></ol><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/sync-validations-flow.png?sfvrsn=d7f77ce4_2" alt="Flow chart. Run synchronous validations. Passed – no or Run async validation. Update field state." /></p><p>Angular provides these built-in validation rules:</p><ol><li><code>required()</code></li><li><code>email()</code></li><li><code>min()</code></li><li><code>max()</code></li><li><code>minLength()</code></li><li><code>maxLength()</code></li><li><code>pattern()</code></li></ol><p>You can apply <code>required()</code> and <code>email()</code> validation on email field as shown below:</p><pre class=" language-ts"><code class="prism  language-ts">  loginForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>userModel<span class="token punctuation">,</span> <span class="token punctuation">(</span>schema<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">required</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'email is required'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">email</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'email is not valid'</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>When you have multiple validation rules applied, each rule runs independently and can cause form to be invalid.</p><p>Angular Signal Forms make it straightforward to implement custom validations. For example, if you want to verify that the email belongs to the nomadcoder domain, you can easily add a custom validator as shown below.</p><pre class=" language-ts"><code class="prism  language-ts">  loginForm <span class="token operator">=</span> <span class="token function">form</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>userModel<span class="token punctuation">,</span> <span class="token punctuation">(</span>schema<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
    <span class="token function">required</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'email is required'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token function">email</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">{</span> message<span class="token punctuation">:</span> <span class="token string">'email is not valid'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
     <span class="token function">validate</span><span class="token punctuation">(</span>schema<span class="token punctuation">.</span>email<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">{</span> value <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
      <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">endsWith</span><span class="token punctuation">(</span><span class="token string">'@nomadcoder.ai'</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>
          kind<span class="token punctuation">:</span> <span class="token string">'emailDomain'</span><span class="token punctuation">,</span>
          message<span class="token punctuation">:</span> <span class="token string">'Email must end with @nomadcoder.ai'</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 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 upcoming articles, we will get into details of custom validation and asynchronous validation.</p><h2 id="form-field-and-its-signal-properties">Form Field and Its Signal Properties</h2><p>The <code>form</code> function returns a <code>FieldTree</code> that represents all properties of the model. Each property is exposed as a signal, and each property signal includes a set of attributes that are also implemented as signals.</p><p>When you access a field from the field tree, as shown below, you can retrieve its associated attributes as illustrated:</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-06/field-tree-attributes.png?sfvrsn=d73e4df6_2" alt="After  the next line has  and a list of attributes comes up" /></p><p>The various signal attributes are as below:</p><ol><li><code>value()</code></li><li><code>dirty()</code></li><li><code>hidden()</code></li><li><code>errors</code></li><li><code>errorSummary</code></li><li><code>valid</code></li><li><code>invalid</code></li><li><code>pending</code></li><li><code>submitting</code></li><li>Etc.</li></ol><p>As all these are <code>signals()</code>, they can be directly used on the template. I hope now you understand Angular Signal Forms. In coming articles, we will go deeper into:</p><ol><li><a href="https://www.telerik.com/blogs/async-validation-angular-signal-forms" target="_blank">asynchronous validators</a></li><li>custom validators</li></ol><p>I hope you find this article useful. Thanks for reading.</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">Angular Signals: Building a Reactive Countdown Timer</h4></div><div class="col-8"><p class="u-fs16 u-mb0">A comprehensive tutorial for experienced Angular developers to learn <a target="_blank" href="https://www.telerik.com/blogs/angular-signals-building-reactive-countdown-timer">Angular Signals by building a practical countdown timer</a> application with computed signals and effects.</p></div></div></aside>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:c92d89d9-25b7-41e4-bb56-1df47761789e</id>
    <title type="text">AI Can’t Solve It All: Here’s What 120+ Frontend Developers Say They Still Hate Working On</title>
    <summary type="text">What still causes the most friction when building modern web applications? 120+ developers at JSNation and React Summit weigh in.</summary>
    <published>2026-07-06T14:05:08Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Kathryn Grayson Nanz </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/ai-cant-solve-all-what-120-frontend-developers-say-they-still-hate-working"/>
    <content type="text"><![CDATA[<p><span class="featured">What still causes the most friction when building modern web applications? 120+ developers at JSNation and React Summit weigh in.</span></p><p>While we were at JSNation and React Summit (<a href="https://www.telerik.com/blogs/check-out-amazing-projects-progress-gitnation-hackathon" target="_blank">read about our hackathon there</a>!), we wanted to take advantage of our booth space to do a little user research. We asked folks who stopped by to answer a simple question for us: <strong>What still causes the most friction when building modern web applications?</strong></p><p>Instead of running a formal survey, we set up a whiteboard, handed folks a sticky note and a sharpie, and invited them to air their grievances. Over the two days of the combined conferences, we collected 120+ responses across two prompts:</p><ul><li><strong>Day 1 (JS Nation):</strong> <em>What&rsquo;s the most annoying frontend task or component to build from scratch?</em></li><li><strong>Day 2 (React Summit):</strong> <em>What does AI still get wrong when building UI in React?</em></li></ul><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/1781254429274.jpg?sfvrsn=45fc2fa8_2" /></p><p>The responses were (at least to me) surprisingly consistent. While frameworks, tooling and AI assistants continue to evolve, many of the age-old pain points haven&rsquo;t disappeared. Turns out, it sucked to build a date picker 10 years ago and it still sucks today. </p><h2 id="building-ui-components-is-still-a-challenge">Building UI Components Is Still a Challenge</h2><p>We left the first question open-ended, allowing folks to pick between tasks and components that they found annoying. Personally, I was expecting to see more tasks than components&mdash;especially now that we can AI-generate components fairly quickly&mdash;but that&rsquo;s not what happened. In fact, the split between individual components and tasks was a fairly even split. </p><p><strong>Date picker components</strong> topped the list, with developers calling out everything from timezone handling to accessibility and date ranges. Unsurprisingly, <strong>AI</strong> is next up&mdash;adopting any new technology is going to come with some pain points and process adjustments. <strong>Accessibility</strong> was in third place with several callouts about struggling with compliance and testing, specifically. </p><table><style>table,
 th,
    td {
      border: 1px;
      border-color: #bdbdba;
      border-style: dotted;
      border-collapse: collapse;
      padding: 0in 5.4pt 0in 5.4pt;
    }
  </style>
 <thead><tr><th>Theme</th><th>Mentions</th></tr></thead><tbody><tr><td>Date pickers</td><td>9</td></tr><tr><td>AI-related work (including prompt engineering, dealing with AI-generated designs, AI features in components, skill writing and chatbot components)</td><td>8</td></tr><tr><td>Accessibility (including WCAG compliance, testing and mentions of specific challenging components such as calendars and comboboxes)</td><td>7</td></tr><tr><td>Requirements &amp; development process (including scope creep, non-technical coworkers, client management)</td><td>6</td></tr><tr><td>Data grids &amp; tables</td><td>5</td></tr><tr><td>Rich text editors</td><td>4</td></tr><tr><td>Design systems &amp; theming</td><td>4</td></tr><tr><td>Comboboxes</td><td>4</td></tr></tbody></table><br /><h2 id="ai-has-become-part-of-the-workflow-and-part-of-the-friction">AI Has Become Part of the Workflow&mdash;and Part of the Friction</h2><p>As you can see in the results above, even before we introduced an AI-specific discussion on Day 2 developers were already talking about AI. But when we asked specifically about AI-generated React code, the conversation shifted from whether AI is useful to where it still struggles<em>.</em></p><table><thead><tr><th>Theme</th><th>Mentions</th></tr></thead><tbody><tr><td>React architecture</td><td>6</td></tr><tr><td>CSS &amp; styling</td><td>4</td></tr><tr><td>Effects &amp; async logic</td><td>2</td></tr><tr><td>Animation</td><td>2</td></tr></tbody></table><br /><p>The top complaint was related to React architecture. Developers repeatedly mentioned AI-generated code that:</p><ul><li>Violates the rules of hooks</li><li>Creates unnecessary <code>useState</code> calls</li><li>Places state outside components</li><li>Generates new components instead of reusing existing ones</li><li>Produces code without understanding project requirements</li><li>Includes poor quality CSS</li></ul><h2 id="some-pain-points-refuse-to-go-away">Some Pain Points Refuse to Go Away</h2><p>Across both days, several themes kept resurfacing. Accessibility appeared on both walls, suggesting that AI hasn&rsquo;t eliminated many of the challenges developers face when building inclusive interfaces. Developers also repeatedly mentioned:</p><ul><li>Design systems</li><li>Figma-to-code workflows</li><li>Localization</li><li>Testing</li><li>State management</li><li>Performance</li><li>CSS</li></ul><p>These aren&rsquo;t new problems&mdash;and many of them have to do with very human aspects of software development such as coordination across teams, inclusivity, creating work for a global audience and user testing.</p><p><img sf-image-responsive="true" src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/.net-maui-aiprompt/screenshot-2026-06-30-at-2-00-50-pm.png?sfvrsn=c0402f64_2" height="700" style="max-width:100%;height:auto;" title="Screenshot 2026-06-30 at 2.00.50 PM" width="728" alt="" sf-size="942727" /></p><h2 id="so-what-does-all-this-mean">So? What Does All This Mean</h2><p>The difficulty now (as it always has been) is in the integration of tools into cohesive systems. The tools have changed, but the challenge has not. Frontend developers today are spending their time wrestling with accessibility, architecture, complex UI components, evolving requirements and figuring out how to integrate AI into real production workflows.</p><p>AI is clearly changing how developers build software, but it hasn&rsquo;t eliminated the need for thoughtful engineering. In many cases, it&rsquo;s shifted developers&rsquo; attention toward reviewing, refining and improving generated code rather than writing every line themselves. Whether that&rsquo;s an improvement or not is something only time will tell. </p><p>The good news is that many of the component-centric frustrations that developers shared at our booth&mdash;date pickers, data grids, accessibility, design systems and more&mdash;all have something in common: they&rsquo;re problems with existing solutions.</p><p>Modern component libraries exist so developers can focus on building the unique parts of their applications instead of spending weeks recreating foundational UI. Likewise, AI tools are most effective when paired with production-ready building blocks that help developers generate better code, faster. </p><p>Investing in the right UI foundation can remove much of the friction developers told us they&rsquo;re still experiencing today. We may not have the answer to <em>every</em> frontend development struggle, but there are plenty that the <a href="https://www.telerik.com/home.aspx" target="_blank">Progress Telerik and Kendo UI</a> libraries can help address today.</p>]]></content>
  </entry>
  <entry>
    <id>urn:uuid:c7659abf-70b7-4ba4-a7d1-5ece28dc3d4a</id>
    <title type="text">A Developer’s Guide to State Management with Effector</title>
    <summary type="text">Learn to build a simple app that shows how Effector handles state, async operations and UI updates. The app will allow users add, update, delete and load todos from an API.</summary>
    <published>2026-07-06T12:53:56Z</published>
    <updated>2026-07-20T16:30:05Z</updated>
    <author>
      <name>Christian Nwamba </name>
    </author>
    <link rel="alternate" href="https://www.telerik.com/blogs/developers-guide-state-management-effector"/>
    <content type="text"><![CDATA[<p>Learn to build a simple app that shows how Effector handles state, async operations and UI updates. The app will allow users add, update, delete and load todos from an API.</p><p>State management in React can get complicated quickly. Most libraries make you choose between simple and powerful. Simple tools break as your app grows or become difficult to manage. Powerful tools require you to write a lot of setup/config code before anything works. And when you need to handle async operations or derive state from multiple sources, you&rsquo;re writing custom logic from scratch all over again.</p><p><a target="_blank" href="https://effector.dev/">Effector</a> works differently. It is event-driven in the sense that you tell the app what happened (an event), the data updates in the store and the UI updates accordingly. Side effects are handled cleanly, and TypeScript works without extra setup. Instead of dispatching actions and writing reducers, you simply fire events that your stores react to.</p><h2 id="prerequisites">Prerequisites</h2><ul><li>Understanding of JavaScript/<a target="_blank" href="https://www.typescriptlang.org/">TypeScript</a></li><li>Basic familiarity with <a target="_blank" href="https://react.dev/">React</a></li></ul><h2 id="project-setup">Project Setup</h2><p>We&rsquo;re going to build a to-do app to see how Effector handles state, async operations and UI updates. The app will let users add, update, delete and load todos from an API. We&rsquo;ll use Vite to set up a React + TypeScript project.</p><p>Open your terminal and run the following command:</p><pre class=" language-shell"><code class="prism  language-shell">npm create vite@latest effector-todo -- --template react-ts 
</code></pre><p>Once that is done, run the following command to navigate into your project:</p><pre class=" language-shell"><code class="prism  language-shell">cd effector-todo
npm install
</code></pre><p>Run this command to install Effector dependencies:</p><pre class=" language-shell"><code class="prism  language-shell">npm install effector effector-react
</code></pre><p>Effector works with any framework, but we need <code>effector-react</code> to connect it to React. The core <code>effector</code> package handles state management, while <code>effector-react</code> provides hooks like <code>useUnit</code> to access state in components.</p><h2 id="what-is-effector">What Is Effector?</h2><p>Effector is built around events. All you need to do is describe what happened and the store updates, and the UI will follow. If we compare that to Redux, you need an action, a reducer and a type constant to update a single value in a component.</p><p><a target="_blank" href="https://zustand.docs.pmnd.rs/learn/getting-started/introduction">Zustand</a> made this simpler by letting us call functions directly on the store, which felt like a relief. But async operations are where things get complicated. Suppose we want to save a todo to a database before updating the screen. The fetch call, the loading flag and error handling will all end up in one place, making the function grow messy.</p><p>Effector handles async operations differently. We wrap them in an effect, and the loading, success and failure states are all tracked automatically. With this, there is no need for manual flag management.</p><p>Then we also have <a target="_blank" href="https://react.dev/learn/passing-data-deeply-with-context">React Context</a>. If you change anything in a provider, your entire component tree re-renders. Effector avoids this by only updating the specific components subscribed to the store that actually changed. TypeScript also works out of the box. If you define types once in the store, they are known everywhere in the codebase.</p><p>The powerful thing about Effector is that it isn&rsquo;t tied to React alone. It gives you the flexibility to work with <a target="_blank" href="https://vuejs.org/">Vue</a>, <a target="_blank" href="https://svelte.dev/">Svelte</a> or any other framework.</p><h3 id="the-3-building-blocks">The 3 Building Blocks</h3><p>As mentioned earlier, Effector is built around three primitives called units: stores, events and effects. Everything in your app is one of these three things.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/effector-building-blocks.png?sfvrsn=d749548a_2" title="Effector’s building blocks" alt="Effector’s building blocks" /></p><ul><li><strong>A store</strong> holds your data, and it is a reactive container. When it changes, every component subscribed to it updates automatically. More precisely, every part of your UI that depends on it updates automatically. You never have to tell React that something has changed or trigger a re-render. The store handles that.</li><li><strong>Events</strong> are signals. They represent things that happen in your app, such as a button being clicked, a form being submitted or a user typing something. An event doesn&rsquo;t contain logic itself, it just announces what happened and carries the relevant data (the payload). This separation is intentional. Because an event is just a signal, you can wire it to any store or effect without the UI caring. The React button just triggers the event, so it doesn&rsquo;t need to know what happens next. That&rsquo;s the store&rsquo;s job.</li><li><strong>Effects</strong> are where async operations live. Anytime your app needs to talk to the outside world (fetch data, write to an API or read from localStorage), that goes in an effect. The reason effects exist as a separate primitive is that async operations have a lifecycle. They start, they either succeed or fail, and the UI needs to reflect each of those states. In most libraries, you manage this yourself with <code>useState</code> hooks and <code>try/catch</code> blocks scattered across your component. Effector handles it automatically. Every effect gives you a <code>loading</code> state, a <code>success</code> state and an <code>error</code> state out of the box. All you need to do is connect them, and you&rsquo;re done.</li></ul><h2 id="building-the-todo-store">Building the Todo Store</h2><p>Create a src/store.ts 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> createStore <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"effector"</span><span class="token punctuation">;</span>

<span class="token keyword">type</span> Todo <span class="token operator">=</span> <span class="token punctuation">{</span>
  id<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
  text<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  done<span class="token punctuation">:</span> <span class="token keyword">boolean</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> $todos <span class="token operator">=</span> createStore<span class="token operator">&lt;</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 punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>In the code above, we have a <code>Todo</code> type for TypeScript, which is just the shape of one item in our todos array, a single todo. The <code>$</code> prefix is Effector&rsquo;s convention for signaling that this is a <code>store</code>, not a regular <code>variable</code>. Because we passed <code>Todo[]</code> as the type, everywhere we use <code>$todos</code>, TypeScript already knows exactly what&rsquo;s inside it. The code above is our entire data layer for todos.</p><h2 id="creating-the-events">Creating the Events</h2><p>A store on its own doesn&rsquo;t do much. We mentioned earlier that events are one of the three building blocks in Effector, and this is where we create them. Update your <code>src/store.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> createStore<span class="token punctuation">,</span> createEvent <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"effector"</span><span class="token punctuation">;</span>

<span class="token keyword">export</span> <span class="token keyword">const</span> addTodo <span class="token operator">=</span> createEvent<span class="token operator">&lt;</span><span class="token keyword">string</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">export</span> <span class="token keyword">const</span> toggleTodo <span class="token operator">=</span> createEvent<span class="token operator">&lt;</span><span class="token keyword">number</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">export</span> <span class="token keyword">const</span> deleteTodo <span class="token operator">=</span> createEvent<span class="token operator">&lt;</span><span class="token keyword">number</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>Here we have three events that can happen in the app. <code>addTodo</code> carries a string, which is the text of the new todo. <code>toggleTodo</code> and <code>deleteTodo</code> carry a number which is the ID of the todo to act on.</p><p>One thing to notice here is that there&rsquo;s no logic. <code>addTodo</code> doesn&rsquo;t know how todos are stored or what happens when it fires. It&rsquo;s just a signal. This is what we described earlier when we said that the button that fires the event doesn&rsquo;t need to know what happens next.</p><h2 id="connecting-events-to-the-store">Connecting Events to the Store</h2><p>Right now, the store and the events exist independently, nothing connects them. That&rsquo;s what <code>.on()</code> does. Update your s<code>rc/store.ts</code> with the following:</p><pre class=" language-ts"><code class="prism  language-ts">$todos
<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span>addTodo<span class="token punctuation">,</span> <span class="token punctuation">(</span>state<span class="token punctuation">,</span> text<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">[</span>
  <span class="token operator">...</span>state<span class="token punctuation">,</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> done<span class="token punctuation">:</span> <span class="token keyword">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span>toggleTodo<span class="token punctuation">,</span> <span class="token punctuation">(</span>state<span class="token punctuation">,</span> id<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
  state<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> 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 function">on</span><span class="token punctuation">(</span>deleteTodo<span class="token punctuation">,</span> <span class="token punctuation">(</span>state<span class="token punctuation">,</span> id<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
  state<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 punctuation">;</span>
</code></pre><p>Each <code>.on()</code> method takes two parameters: the event to listen for, and a function that describes how the state should change. The function receives the current state and the event payload, then returns a new state. It never mutates the previous state; instead, it always returns a new value.</p><p>In the code above, when <code>addTodo</code> fires, we spread the existing todos and add a new one. When <code>toggleTodo</code> fires, we find the todo with that ID and toggle its <code>done</code> value. When <code>deleteTodo</code> fires, we filter that todo out of the array.</p><p>That&rsquo;s the complete data layer for our todo app. There are no classes and no reducers. Just a store, three events and three <code>.on()</code> handlers describing exactly what changes when each event fires.</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">KendoReact vs. OSS: What You Can Actually Get for Free (and What You Can&rsquo;t)</h4></div><div class="col-8"><p class="u-fs16 u-mb0">If you&rsquo;ve reached the point where <a target="_blank" href="https://www.telerik.com/blogs/kendoreact-vs-oss-what-actually-get-free-what-you-cant">free UI libraries are starting to cause friction</a>, it may be time to consider whether they&rsquo;re still the right choice for your team&mdash;and your app.</p></div></div><hr class="u-mb3" /></aside><h2 id="connecting-to-react">Connecting to React</h2><p>Our store exists, the events are wired up, but our UI doesn&rsquo;t know any of that yet. This is where <code>effector-react</code> comes in. It gives you a single hook, <code>useUnit</code>, that connects your components to your stores.</p><h3 id="reading-state-with-useunit">Reading State with useUnit</h3><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> useUnit <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"effector-react"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> $todos <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./store"</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">const</span> todos <span class="token operator">=</span> <span class="token function">useUnit</span><span class="token punctuation">(</span>$todos<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre><p><code>useUnit</code> subscribes the component to <code>$todos</code>. When the store changes, the component re-renders with the new value. If nothing changes, it doesn&rsquo;t re-render.</p><p>We don&rsquo;t have context providers wrapping our app here. Compare this to how you would do the same with Context:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token operator">&lt;</span>TodoContext<span class="token punctuation">.</span>Provider value<span class="token operator">=</span><span class="token punctuation">{</span><span class="token punctuation">{</span> todos<span class="token punctuation">,</span> setTodos <span class="token punctuation">}</span><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>TodoContext<span class="token punctuation">.</span>Provider<span class="token operator">&gt;</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> todos <span class="token punctuation">}</span> <span class="token operator">=</span> <span class="token function">useContext</span><span class="token punctuation">(</span>TodoContext<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Every time <code>setTodos</code> is called, every component inside that provider re-renders. As you can see, Effector gives us a much cleaner approach. With Effector, there&rsquo;s no provider. All we have to do is just import the store and call <code>useUnit</code>.</p><p>Create an <code>App.tsx</code> file in your <code>src</code> directory if you haven&rsquo;t already, 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>
<span class="token keyword">import</span> <span class="token punctuation">{</span> useUnit <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"effector-react"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> $todos<span class="token punctuation">,</span> addTodo<span class="token punctuation">,</span> toggleTodo<span class="token punctuation">,</span> deleteTodo <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./store"</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">const</span> todos <span class="token operator">=</span> <span class="token function">useUnit</span><span class="token punctuation">(</span>$todos<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>input<span class="token punctuation">,</span> setInput<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">handleAdd</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">if</span> <span class="token punctuation">(</span>input<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">addTodo</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token function">setInput</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 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>min-h-screen bg-gray-50 p-8<span class="token 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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>max-w-2xl mx-auto<span class="token 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 text-gray-900 mb-8<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
          Effector Todo
        <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>bg-white rounded-lg shadow p-6 mb-6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex gap-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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>input<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">setInput</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">onKeyPress</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> e<span class="token punctuation">.</span>key <span class="token operator">===</span> <span class="token string">"Enter"</span> <span class="token operator">&amp;&amp;</span> <span class="token function">handleAdd</span><span class="token punctuation">(</span><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>Add a new todo...<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>flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500<span class="token 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">onClick</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>handleAdd<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>px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors<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>div</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>bg-white rounded-lg shadow divide-y<span class="token punctuation">"</span></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>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 text-gray-500 text-center<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
              No todos yet<span class="token punctuation">.</span> Add one above<span class="token operator">!</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>
            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 tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<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>p-4 flex items-center gap-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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">checked</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>done<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">toggleTodo</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<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>w-5 h-5 text-blue-600 rounded<span class="token punctuation">"</span></span>
                <span class="token punctuation">/&gt;</span></span>
                <span class="token operator">&lt;</span>span className<span class="token operator">=</span><span class="token punctuation">{</span><span class="token template-string"><span class="token string">`flex-1 </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>todo<span class="token punctuation">.</span>done <span class="token operator">?</span> <span class="token string">"line-through text-gray-400"</span> <span class="token punctuation">:</span> <span class="token string">"text-gray-900"</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>todo<span class="token punctuation">.</span>text<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>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">deleteTodo</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<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>px-3 py-1 text-sm text-red-600 hover:bg-red-50 rounded<span class="token punctuation">"</span></span>
                <span class="token punctuation">&gt;</span></span>
                  Delete
                <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><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 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>In the code above, you&rsquo;ll notice we&rsquo;re not attaching any state logic in our <code>App</code> component, all thanks to Effector. It doesn&rsquo;t care how todos are stored, added or deleted. All it does is read <code>$todos</code> and trigger events when something happens.</p><p>The only place we&rsquo;re using <code>useState</code> is for the <code>input</code> field, which makes sense since that&rsquo;s just local UI state which doesn&rsquo;t need to live in the store.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/todo-app-using-effector-stores.gif?sfvrsn=3054f22d_2" title="Todo app using Effector stores" alt="Todo app using Effector stores" /></p><h3 id="derived-store-with-.map">Derived Store with .map()</h3><p>Our store works, and the UI is connected, but most apps aren&rsquo;t as straightforward as our todo app currently is. At some point, you might want to show only completed todos in one place, count remaining tasks somewhere else or filter by priority in another component. You can already see where this is heading.</p><p>What comes to mind first is filtering inside each component. That works until you need to change how something works and realize your logic is scattered across the app, where changing one thing breaks another.</p><p>Effector handles this with derived states. Instead of rewriting the same logic or recomputing values across components, you derive them once.</p><p>Let&rsquo;s update our <code>store.ts</code> file with these derived stores:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">export</span> <span class="token keyword">const</span> $completedTodos <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>todos<span class="token punctuation">)</span> <span class="token operator">=&gt;</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>done<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> $activeTodos <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>todos<span class="token punctuation">)</span> <span class="token operator">=&gt;</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> <span class="token operator">!</span>todo<span class="token punctuation">.</span>done<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> $remainingCount <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>todos<span class="token punctuation">)</span> <span class="token operator">=&gt;</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> <span class="token operator">!</span>todo<span class="token punctuation">.</span>done<span class="token punctuation">)</span><span class="token punctuation">.</span>length
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p><code>$todos.map()</code> is a method we call on a store. This might look familiar, but it&rsquo;s not the same as JavaScript&rsquo;s <code>array.map()</code>. When this one is called on a store, it returns a new store.</p><p>In Effector, when you call <code>.map()</code> on a store, it creates a new store that watches the original one. Whenever <code>$todos</code> updates, the derived store runs your transformation function and updates itself. This way, everything stays in sync automatically.</p><p>Effector also follows immutability. Nothing gets changed in place. Every time you derive something, you&rsquo;re creating a new value instead of modifying the existing one, and the transformation function handles that for you.</p><p>Let&rsquo;s see this in action by updating our <code>App.tsx</code> file to look like so:</p><pre class=" language-jsx"><code class="prism  language-jsx"><span class="token comment">//App.tsx</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> useUnit <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"effector-react"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>
  $activeTodos<span class="token punctuation">,</span>
  $completedTodos<span class="token punctuation">,</span>
  $remainingCount<span class="token punctuation">,</span>
  addTodo<span class="token punctuation">,</span>
  toggleTodo<span class="token punctuation">,</span>
  deleteTodo<span class="token punctuation">,</span>
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./store"</span><span class="token punctuation">;</span>

<span class="token keyword">export</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">const</span> activeTodos <span class="token operator">=</span> <span class="token function">useUnit</span><span class="token punctuation">(</span>$activeTodos<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> completedTodos <span class="token operator">=</span> <span class="token function">useUnit</span><span class="token punctuation">(</span>$completedTodos<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> remainingCount <span class="token operator">=</span> <span class="token function">useUnit</span><span class="token punctuation">(</span>$remainingCount<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> <span class="token punctuation">[</span>input<span class="token punctuation">,</span> setInput<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">handleAdd</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">if</span> <span class="token punctuation">(</span>input<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">addTodo</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token function">setInput</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 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>min-h-screen bg-gray-50 p-8<span class="token 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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>max-w-2xl mx-auto<span class="token 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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex items-center justify-between mb-8<span class="token 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 text-gray-900<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Effector Todo<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>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-gray-500 text-sm<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token punctuation">{</span>remainingCount<span class="token punctuation">}</span> <span class="token punctuation">{</span>remainingCount <span class="token operator">===</span> <span class="token number">1</span> <span class="token operator">?</span> <span class="token string">"task"</span> <span class="token punctuation">:</span> <span class="token string">"tasks"</span><span class="token punctuation">}</span> remaining
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>span</span><span class="token punctuation">&gt;</span></span>
        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">&gt;</span></span>

        <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>bg-white rounded-lg shadow p-6 mb-6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex gap-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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>input<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">setInput</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">onKeyPress</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> e<span class="token punctuation">.</span>key <span class="token operator">===</span> <span class="token string">"Enter"</span> <span class="token operator">&amp;&amp;</span> <span class="token function">handleAdd</span><span class="token punctuation">(</span><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>Add a new todo...<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>flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500<span class="token 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">onClick</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>handleAdd<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>px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors<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>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>activeTodos<span class="token punctuation">.</span>length <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</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>bg-white rounded-lg shadow divide-y mb-6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token punctuation">{</span>activeTodos<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 tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<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>p-4 flex items-center gap-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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">checked</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>done<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">toggleTodo</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<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>w-5 h-5 text-blue-600 rounded<span class="token punctuation">"</span></span>
                <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex-1 text-gray-900<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 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>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">deleteTodo</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<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>px-3 py-1 text-sm text-red-600 hover:bg-red-50 rounded<span class="token punctuation">"</span></span>
                <span class="token punctuation">&gt;</span></span>
                  Delete
                <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>
          <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>completedTodos<span class="token punctuation">.</span>length <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</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>h2</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-500 mb-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
              Completed
            <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 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>bg-white rounded-lg shadow divide-y opacity-60<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
              <span class="token punctuation">{</span>completedTodos<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 tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<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>p-4 flex items-center gap-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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">checked</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>done<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">toggleTodo</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<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>w-5 h-5 text-blue-600 rounded<span class="token punctuation">"</span></span>
                  <span class="token punctuation">/&gt;</span></span>
                  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex-1 line-through text-gray-400<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 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>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">deleteTodo</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<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>px-3 py-1 text-sm text-red-600 hover:bg-red-50 rounded<span class="token punctuation">"</span></span>
                  <span class="token punctuation">&gt;</span></span>
                    Delete
                  <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>
            <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 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>As seen above, the app component stays clean and just does what it is supposed to do.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/todo-app-with-derived-stores.gif?sfvrsn=f18cac48_2" title="A basic todo app with derived stores" alt="A basic todo app with derived stores" /></p><h2 id="async-operations-with-effects">Async Operations with Effects</h2><p>Every app eventually needs to talk to the outside world, and it may be through fetching data, saving something to a server or reading from storage. This is where most state management solutions make you manually create a loading flag, set it to true before the request, set it back to false when it finishes, handle the error case and clean up if the component unmounts. You end up writing the same boilerplate every time.</p><p>The good thing about Effector is that it has a dedicated concept for this called Effects.</p><h2 id="creating-an-effect">Creating an Effect</h2><p>Let&rsquo;s update our <code>store.ts</code> file by first adding a new import called <code>createEffect</code>. This handles fetching data and, more generally, any kind of side effect.</p><p>We&rsquo;ll also use a dummy API to fetch some todos and define the type of each todo we expect back.</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> createStore<span class="token punctuation">,</span> createEvent<span class="token punctuation">,</span> createEffect <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"effector"</span><span class="token punctuation">;</span>

<span class="token keyword">type</span> ApiTodo <span class="token operator">=</span> <span class="token punctuation">{</span>
  id<span class="token punctuation">:</span> <span class="token keyword">number</span><span class="token punctuation">;</span>
  todo<span class="token punctuation">:</span> <span class="token keyword">string</span><span class="token punctuation">;</span>
  completed<span class="token punctuation">:</span> <span class="token keyword">boolean</span><span class="token punctuation">;</span>
  userId<span class="token punctuation">:</span> <span class="token keyword">number</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> loadTodosFx <span class="token operator">=</span> <span class="token function">createEffect</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">const</span> response <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">"https://dummyjson.com/todos?limit=5"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token keyword">await</span> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">return</span> data<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> ApiTodo<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">(</span><span class="token punctuation">{</span>
    id<span class="token punctuation">:</span> todo<span class="token punctuation">.</span>id<span class="token punctuation">,</span>
    text<span class="token punctuation">:</span> todo<span class="token punctuation">.</span>todo<span class="token punctuation">,</span>
    done<span class="token punctuation">:</span> todo<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 punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>In the code above, <code>createEffect</code> wraps an async function. The <code>Fx</code> suffix is an Effector convention in the sense that just as <code>$</code> signals a store, <code>Fx</code> signals an effect. Anyone reading the code immediately knows what they&rsquo;re dealing with.</p><p>The function inside is just a regular async function that fetches data, transforms it and returns it.</p><p>Effector offers three things out of the box:</p><ul><li><code>loadTodosFx.pending</code>: A store that is true while the effect is running and false when it finishes</li><li><code>loadTodosFx.doneData</code>: An event that fires with the return value when the effect succeeds</li><li><code>loadTodosFx.fail</code>: An event that fires with the error if something goes wrong</li></ul><p>We don&rsquo;t write any of this, and you&rsquo;ll see it in action when we connect the effects to the store.</p><h2 id="connecting-the-effect-to-the-store">Connecting the Effect to the Store</h2><p>We&rsquo;ll wire <code>doneData</code> to <code>$todos</code> the same way we wired events with <code>.on()</code>.</p><p>In your store.ts file, update the <code>$todos</code> connection to include the effect:</p><pre class=" language-ts"><code class="prism  language-ts">$todos
  <span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span>addTodo<span class="token punctuation">,</span> <span class="token punctuation">(</span>state<span class="token punctuation">,</span> text<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">[</span>
    <span class="token operator">...</span>state<span class="token punctuation">,</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>
      done<span class="token punctuation">:</span> <span class="token keyword">false</span><span class="token punctuation">,</span>
    <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token punctuation">]</span><span class="token punctuation">)</span>
  <span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span>toggleTodo<span class="token punctuation">,</span> <span class="token punctuation">(</span>state<span class="token punctuation">,</span> id<span class="token punctuation">)</span> <span class="token operator">=&gt;</span>
    state<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> 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 function">on</span><span class="token punctuation">(</span>deleteTodo<span class="token punctuation">,</span> <span class="token punctuation">(</span>state<span class="token punctuation">,</span> id<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> state<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 punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span>loadTodosFx<span class="token punctuation">.</span>doneData<span class="token punctuation">,</span> <span class="token punctuation">(</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> todos<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>The only new thing in the code above is the last <code>.on()</code> call. Before now, the store was reacting to events we triggered manually. Now it&rsquo;s also reacting to the result of an async operation.</p><p>When <code>loadTodosFx.doneData</code> fires (which happens when the request succeeds), the store replaces the current todos with whatever came back from the API.</p><h2 id="using-the-fx-in-the-component">Using the Fx in the Component</h2><p>Now update your App.tsx to import <code>loadTodosFx</code> and read the loading state:</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>
<span class="token keyword">import</span> <span class="token punctuation">{</span> useUnit <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"effector-react"</span><span class="token punctuation">;</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span>
  $activeTodos<span class="token punctuation">,</span>
  $completedTodos<span class="token punctuation">,</span>
  $remainingCount<span class="token punctuation">,</span>
  addTodo<span class="token punctuation">,</span>
  toggleTodo<span class="token punctuation">,</span>
  deleteTodo<span class="token punctuation">,</span>
  loadTodosFx<span class="token punctuation">,</span>
<span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"./store"</span><span class="token punctuation">;</span>

<span class="token keyword">export</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">const</span> activeTodos <span class="token operator">=</span> <span class="token function">useUnit</span><span class="token punctuation">(</span>$activeTodos<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> completedTodos <span class="token operator">=</span> <span class="token function">useUnit</span><span class="token punctuation">(</span>$completedTodos<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> remainingCount <span class="token operator">=</span> <span class="token function">useUnit</span><span class="token punctuation">(</span>$remainingCount<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 function">useUnit</span><span class="token punctuation">(</span>loadTodosFx<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> isLoading <span class="token operator">=</span> <span class="token function">useUnit</span><span class="token punctuation">(</span>loadTodosFx<span class="token punctuation">.</span>pending<span class="token punctuation">)</span><span class="token punctuation">;</span>

  <span class="token keyword">const</span> <span class="token punctuation">[</span>input<span class="token punctuation">,</span> setInput<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">handleAdd</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">if</span> <span class="token punctuation">(</span>input<span class="token punctuation">.</span><span class="token function">trim</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      <span class="token function">addTodo</span><span class="token punctuation">(</span>input<span class="token punctuation">)</span><span class="token punctuation">;</span>
      <span class="token function">setInput</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 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>min-h-screen bg-gray-50 p-8<span class="token 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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>max-w-2xl mx-auto<span class="token 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">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex items-center justify-between mb-8<span class="token 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 text-gray-900<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>Effector Todo<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 items-center 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>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-teal-700 text-2xl font-extrabold<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
              <span class="token punctuation">{</span>remainingCount<span class="token punctuation">}</span> <span class="token punctuation">{</span>remainingCount <span class="token operator">===</span> <span class="token number">1</span> <span class="token operator">?</span> <span class="token string">"task"</span> <span class="token punctuation">:</span> <span class="token string">"tasks"</span><span class="token punctuation">}</span><span class="token punctuation">{</span><span class="token string">" "</span><span class="token punctuation">}</span>
              remaining
            <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>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">loadTodos</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span></span>
              <span class="token attr-name">disabled</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>isLoading<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>px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 disabled:opacity-50<span class="token punctuation">"</span></span>
            <span class="token punctuation">&gt;</span></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">"Fetch Todos"</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 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>bg-white rounded-lg shadow p-6 mb-6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
          <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex gap-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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>input<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">setInput</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">onKeyPress</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> e<span class="token punctuation">.</span>key <span class="token operator">===</span> <span class="token string">"Enter"</span> <span class="token operator">&amp;&amp;</span> <span class="token function">handleAdd</span><span class="token punctuation">(</span><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>Add a new todo...<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>flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500<span class="token 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">onClick</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>handleAdd<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>px-6 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors<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>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>activeTodos<span class="token punctuation">.</span>length <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</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>bg-white rounded-lg shadow divide-y mb-6<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
            <span class="token punctuation">{</span>activeTodos<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 tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<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>p-4 flex items-center gap-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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">checked</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>done<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">toggleTodo</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<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>w-5 h-5 text-blue-600 rounded<span class="token punctuation">"</span></span>
                <span class="token punctuation">/&gt;</span></span>
                <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex-1 text-gray-900<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 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>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">deleteTodo</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<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>px-3 py-1 text-sm text-red-600 hover:bg-red-50 rounded<span class="token punctuation">"</span></span>
                <span class="token punctuation">&gt;</span></span>
                  Delete
                <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>
          <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>completedTodos<span class="token punctuation">.</span>length <span class="token operator">&gt;</span> <span class="token number">0</span> <span class="token operator">&amp;&amp;</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>h2</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-500 mb-2<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
              Completed
            <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 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>bg-white rounded-lg shadow divide-y opacity-60<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
              <span class="token punctuation">{</span>completedTodos<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 tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span> <span class="token attr-name">key</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>id<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>p-4 flex items-center gap-3<span class="token punctuation">"</span></span><span class="token punctuation">&gt;</span></span>
                  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>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">checked</span><span class="token script language-javascript"><span class="token punctuation">=</span><span class="token punctuation">{</span>todo<span class="token punctuation">.</span>done<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">toggleTodo</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<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>w-5 h-5 text-blue-600 rounded<span class="token punctuation">"</span></span>
                  <span class="token punctuation">/&gt;</span></span>
                  <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>span</span> <span class="token attr-name">className</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>flex-1 line-through text-gray-400<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 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>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">deleteTodo</span><span class="token punctuation">(</span>todo<span class="token punctuation">.</span>id<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>px-3 py-1 text-sm text-red-600 hover:bg-red-50 rounded<span class="token punctuation">"</span></span>
                  <span class="token punctuation">&gt;</span></span>
                    Delete
                  <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>
            <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 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>Two things changed in this component. First, we imported <code>loadTodosFx</code>, which is now called when the &ldquo;Load Todos&rdquo; button is clicked. Second, <code>loadTodosFx.pending</code> is read with <code>useUnit</code>, which automatically gives us the loading state. The button disables itself while loading, the label switches and todos appear when the fetch is complete.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/fetching-todos-from-api.gif?sfvrsn=2f979803_2" title="Fetching todos from an API" alt="Fetching todos from an API" /></p><h2 id="connecting-units-with-sample">Connecting Units with Sample</h2><p>We have stores, events and effects, but sometimes you need them to talk to each other. When one thing happens, it triggers something else. That&rsquo;s what <code>sample</code> is for. The idea here is straightforward: when a clock fires, read from a source, optionally transform the data and send it to a target.</p><p>Before we look at the code, let&rsquo;s break down what each of those terms actually means:</p><ul><li><strong>clock</strong> &ndash; The trigger. This is what starts everything. It can be an event, an effect or even another store. When it fires, everything else follows.</li><li><strong>source</strong> &ndash; The data you want to read at that moment. Usually a store.</li><li><strong>fn</strong> &ndash; An optional function to transform the data before it goes anywhere. If you don&rsquo;t need to transform anything, you can skip it.</li><li><strong>target</strong> &ndash; Where the result goes. Can be a store, an event or an effect.</li></ul><p>Here is an example:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span> sample <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"effector"</span><span class="token punctuation">;</span>

<span class="token function">sample</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  clock<span class="token punctuation">:</span> someEvent<span class="token punctuation">,</span>    <span class="token comment">// when this fires...</span>
  source<span class="token punctuation">:</span> $someStore<span class="token punctuation">,</span>  <span class="token comment">// grab the current value of this store...</span>
  fn<span class="token punctuation">:</span> <span class="token punctuation">(</span>sourceValue<span class="token punctuation">,</span> clockPayload<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token operator">...</span><span class="token punctuation">,</span> <span class="token comment">// optionally transform...</span>
  target<span class="token punctuation">:</span> anotherEvent <span class="token comment">// and send it here</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Now, let&rsquo;s go through a real example from our todo app. Right now, when you add a todo, the list updates locally. But in a real app, you&rsquo;d want to reload from the server after adding so everything is in sync. Without <code>sample</code>, you&rsquo;d handle that inside the component by calling <code>addTodo</code>, then manually calling <code>loadTodosFx</code> right after. That works, but now that logic is tied to one specific button in one specific component.</p><p>Add this to your store.ts file:</p><pre class=" language-ts"><code class="prism  language-ts">Import <span class="token punctuation">{</span>sample<span class="token punctuation">}</span> <span class="token keyword">from</span> &ldquo;effector&rdquo;<span class="token punctuation">;</span> 
<span class="token function">sample</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
  clock<span class="token punctuation">:</span> addTodo<span class="token punctuation">,</span>
  target<span class="token punctuation">:</span> loadTodosFx<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre><p>Now, instead of completely replacing the todos when the request finishes, we&rsquo;ll merge the data from the API with what we already have locally.</p><p>In your <code>store.ts</code> file, update the last <code>.on()</code> call to look like this:</p><pre class=" language-ts"><code class="prism  language-ts"><span class="token punctuation">.</span><span class="token function">on</span><span class="token punctuation">(</span>loadTodosFx<span class="token punctuation">.</span>doneData<span class="token punctuation">,</span> <span class="token punctuation">(</span>state<span class="token punctuation">,</span> apiTodos<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>
    <span class="token keyword">const</span> apiIds <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Set</span><span class="token punctuation">(</span>apiTodos<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> t<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token keyword">const</span> localTodos <span class="token operator">=</span> state<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> <span class="token operator">!</span>apiIds<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>t<span class="token punctuation">.</span>id<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">...</span>apiTodos<span class="token punctuation">,</span> <span class="token operator">...</span>localTodos<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>Every time <code>addTodo</code> fires from anywhere in our app, from any component, <code>loadTodosFx</code> runs automatically. The component has no idea this is happening. It just calls <code>addTodo</code>, and Effector handles the rest.</p><p><img src="https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2026/2026-07/connecting-units-to-sample.gif?sfvrsn=73c69da4_2" title="Connecting Units to Sample" alt="Connecting Units to Sample" /></p><h2 id="conclusion">Conclusion</h2><p>Honestly, Effector isn&rsquo;t the right tool for every project. If you&rsquo;re building something small with just a few components and basic state, stick with <code>useState</code> and <code>useContext</code>. Save Effector for when your app genuinely starts getting messy.</p><p>It shines in specific scenarios like when you&rsquo;re dealing with async operations that need proper loading and error handling, when multiple unrelated components need to access the same state, when you&rsquo;re computing the same derived data all over the place, or when you catch yourself copy-pasting the same async logic everywhere.</p><p>We&rsquo;ve only scratched the surface here. Effector can do much more, but we&rsquo;ve covered the basics and core concepts, which are the foundation for everything else. If you want to go deeper, check out the <a target="_blank" href="https://effector.dev/en/introduction/get-started/">Effector documentation</a>.</p>]]></content>
  </entry>
</feed>
