<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ReactChallenges</title>
    <description>The latest articles on DEV Community by ReactChallenges (@reactchallenges).</description>
    <link>https://dev.to/reactchallenges</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F854942%2Fe450946a-3098-4715-8d06-0892ab9ac221.png</url>
      <title>DEV Community: ReactChallenges</title>
      <link>https://dev.to/reactchallenges</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/reactchallenges"/>
    <language>en</language>
    <item>
      <title>🚀 New React Challenge: GitHub Contribution Heatmap</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Wed, 05 Aug 2026 08:24:32 +0000</pubDate>
      <link>https://dev.to/reactchallenges/new-react-challenge-github-contribution-heatmap-3im1</link>
      <guid>https://dev.to/reactchallenges/new-react-challenge-github-contribution-heatmap-3im1</guid>
      <description>&lt;p&gt;Fetching data in React without a library means juggling useEffect, abort controllers, isLoading booleans, error state, and cache invalidation — all by hand. SWR, TanStack Query, and RTK Query solve all of that out of the box. This challenge has the grid and components pre-built — your job is to wire up SWR and bring the GitHub-style heatmap to life.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;Fetch contribution data for a GitHub-style heatmap using SWR. The grid and components are already built — you wire up data fetching with loading skeletons, error handling, and year navigation to make it work.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.reactchallenges.com/challenges/github-contribution-heatmap" rel="noopener noreferrer"&gt;https://www.reactchallenges.com/challenges/github-contribution-heatmap&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fetch contribution data from &lt;code&gt;https://example-heatmap?year={year}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Write a custom fetcher that checks &lt;code&gt;res.ok&lt;/code&gt;, parses the error body, and throws on failure — SWR won't know a request failed unless the fetcher throws&lt;/li&gt;
&lt;li&gt;Display a loading skeleton while data is being fetched&lt;/li&gt;
&lt;li&gt;Display an error skeleton with the server's error message when the request fails&lt;/li&gt;
&lt;li&gt;Display the heatmap grid with flattened contributions on success&lt;/li&gt;
&lt;li&gt;Allow switching between years (2023–2026) with interactive buttons&lt;/li&gt;
&lt;li&gt;Highlight the currently selected year button&lt;/li&gt;
&lt;li&gt;Calculate the offset so January 1st aligns with the correct weekday column&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;contributions&lt;/code&gt; is a 2D array (weeks × days) — flatten it with &lt;code&gt;.flat()&lt;/code&gt; before passing to &lt;code&gt;HeatmapGrid&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The mock API has a random 300-500ms delay and fails ~20% of the time&lt;/li&gt;
&lt;li&gt;The custom fetcher pattern is universal: SWR, TanStack Query, and RTK Query all require you to throw on non-ok responses&lt;/li&gt;
&lt;li&gt;The data is from Linus Torvalds' real GitHub contributions — every cell reflects actual commits, merges, and pull requests&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧪 Tests
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Renders the app title&lt;/li&gt;
&lt;li&gt;Shows skeleton while loading&lt;/li&gt;
&lt;li&gt;Displays heatmap grid with contributions when data loads&lt;/li&gt;
&lt;li&gt;Shows skeleton with error when request fails&lt;/li&gt;
&lt;li&gt;Highlights 2026 as the selected year button&lt;/li&gt;
&lt;li&gt;Fetches new data when switching to a different year&lt;/li&gt;
&lt;li&gt;Aligns January 1st to the correct weekday for each year&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Whether you're new to SWR or already using TanStack Query, this challenge reinforces the fetch-then-render pattern that every production React app relies on. Plus, you get to see Linus Torvalds' actual commit history rendered as a heatmap — pretty cool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.reactchallenges.com/challenges/github-contribution-heatmap" rel="noopener noreferrer"&gt;🔥 Start the Challenge Now&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>frontendchallenge</category>
    </item>
    <item>
      <title>Typing Polymorphic React Components in TypeScript</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Wed, 29 Jul 2026 13:33:00 +0000</pubDate>
      <link>https://dev.to/reactchallenges/typing-polymorphic-react-components-in-typescript-cl6</link>
      <guid>https://dev.to/reactchallenges/typing-polymorphic-react-components-in-typescript-cl6</guid>
      <description>&lt;p&gt;I recently came across an interview question about advanced TypeScript patterns, specifically how to type a &lt;code&gt;Button&lt;/code&gt; component that can also render as a link. These are the kinds of concepts that are easy to forget, especially if you don't write as much code by hand anymore because AI handles a lot of it.&lt;/p&gt;

&lt;p&gt;That's why it's worth revisiting them from time to time. One of the best ways to check whether you truly understand a concept is to explain it to yourself and honestly ask whether someone else could understand that explanation. So here we are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything optional
&lt;/h2&gt;

&lt;p&gt;Everything is optional, so TypeScript can't infer whether this component should behave as a button or a link. Since there is no relationship between the props, invalid combinations are allowed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;target&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ✅ No TypeScript error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Union types
&lt;/h2&gt;

&lt;p&gt;TypeScript says:&lt;/p&gt;

&lt;p&gt;"I have a value whose type is &lt;code&gt;LinkProps | ButtonProps&lt;/code&gt;. I'll check whether it matches one of the union members."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LinkProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;target&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;LinkProps&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;href&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ❌ Property 'disabled' does not exist on type 'LinkProps'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Discriminated unions
&lt;/h2&gt;

&lt;p&gt;Sometimes you don't want to infer the rendered element from the props. Instead, you want the user to choose it explicitly.&lt;/p&gt;

&lt;p&gt;TypeScript says:&lt;/p&gt;

&lt;p&gt;"I have a value whose type is &lt;code&gt;LinkProps | ButtonProps&lt;/code&gt;. I'll use the &lt;code&gt;as&lt;/code&gt; property to determine which one it is."&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LinkProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;target&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reset&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;LinkProps&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ❌ Property 'href' does not exist on type 'ButtonProps'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generic polymorphic components
&lt;/h2&gt;

&lt;p&gt;In React 19+, the recommended utility type is &lt;code&gt;React.ComponentProps&amp;lt;T&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It gives you all the props supported by the element or component represented by &lt;code&gt;T&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ElementType&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ComponentProps&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ElementType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; In production code you'll often see &lt;code&gt;Omit&amp;lt;React.ComponentProps&amp;lt;T&amp;gt;, "as"&amp;gt;&lt;/code&gt; instead of &lt;code&gt;React.ComponentProps&amp;lt;T&amp;gt;&lt;/code&gt;. This removes any existing &lt;code&gt;as&lt;/code&gt; prop from the rendered component. Since HTML elements like &lt;code&gt;"button"&lt;/code&gt; and &lt;code&gt;"a"&lt;/code&gt; don't define an &lt;code&gt;as&lt;/code&gt; prop, the simplified version used here is sufficient.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now TypeScript automatically infers the correct props based on the value of &lt;code&gt;as&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// ✅&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// ✅&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"#"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// ❌&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// ❌&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you're using React 18 or earlier, use &lt;code&gt;React.ComponentPropsWithoutRef&amp;lt;T&amp;gt;&lt;/code&gt; instead. Before React 19, &lt;code&gt;ref&lt;/code&gt; wasn't treated as a normal prop for function components, so excluding it was usually the correct choice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;React.ComponentProps&amp;lt;T&amp;gt;&lt;/code&gt; is equivalent to saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Give me all the props that the element or component &lt;code&gt;T&lt;/code&gt; accepts."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Unlike union types, there's no need to manually define every possible combination (&lt;code&gt;LinkProps | ButtonProps | ...&lt;/code&gt;). The props are computed directly from the generic type &lt;code&gt;T&lt;/code&gt;, making the component scalable to any HTML element or custom React component.&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>🚀 New React Challenge: useEffect vs useLayoutEffect</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Wed, 22 Jul 2026 16:16:37 +0000</pubDate>
      <link>https://dev.to/reactchallenges/new-react-challenge-useeffect-vs-uselayouteffect-1e0g</link>
      <guid>https://dev.to/reactchallenges/new-react-challenge-useeffect-vs-uselayouteffect-1e0g</guid>
      <description>&lt;p&gt;You've used useEffect a thousand times. But did you know it runs &lt;em&gt;after&lt;/em&gt; the browser paints? That means if you're measuring the DOM to position an element, users will see a flicker — the element renders at the wrong position, then snaps into place. useLayoutEffect fixes that by running &lt;em&gt;before&lt;/em&gt; paint. This challenge proves it with a tooltip.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;A tooltip component needs to center itself by measuring its own width. The starter uses useEffect, causing a visible flicker — the tooltip appears uncentered for a moment before snapping to the middle. Replace it with useLayoutEffect and the measurement happens before the browser paints, giving you a perfect first frame.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.reactchallenges.com/challenges/use-effect-vs-use-layout-effect" rel="noopener noreferrer"&gt;https://www.reactchallenges.com/challenges/use-effect-vs-use-layout-effect&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;useEffect&lt;/code&gt; with &lt;code&gt;useLayoutEffect&lt;/code&gt; in the TooltipContent component so the tooltip is centered before the browser paints, avoiding the flicker.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;useLayoutEffect&lt;/code&gt; runs before paint — use it for DOM measurements.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useEffect&lt;/code&gt; runs after paint — use it for logging, subscriptions, and side effects that don't affect layout.&lt;/li&gt;
&lt;li&gt;Pro tip: if the flicker is too fast to see, enable CPU throttling (4x or 6x) in DevTools → Performance tab.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧪 Tests
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Uses useLayoutEffect for the DOM measurement&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;This is one of those hooks that doesn't come up often — but when it does, knowing the difference between useEffect and useLayoutEffect is the difference between a polished UI and a janky one. A single line change, a world of difference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.reactchallenges.com/challenges/use-effect-vs-use-layout-effect" rel="noopener noreferrer"&gt;🔥 Start the Challenge Now&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>frontendchallenge</category>
    </item>
    <item>
      <title>🚀 New React Challenge: Invoker modal</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Tue, 14 Jul 2026 16:40:48 +0000</pubDate>
      <link>https://dev.to/reactchallenges/new-react-challenge-invoker-modal-3000</link>
      <guid>https://dev.to/reactchallenges/new-react-challenge-invoker-modal-3000</guid>
      <description>&lt;p&gt;Tired of writing &lt;code&gt;useState(false)&lt;/code&gt; and &lt;code&gt;onClick&lt;/code&gt; handlers every time you need a modal? The browser already has a declarative API for that.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;Learn how to open and close a &lt;code&gt;&amp;lt;dialog&amp;gt;&lt;/code&gt; using only HTML attributes — no React state, no &lt;code&gt;useState&lt;/code&gt;, no event handlers at all. The Invoker Commands API lets buttons control dialogs natively.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 &lt;a href="https://www.reactchallenges.com/challenges/invoker-modal" rel="noopener noreferrer"&gt;https://www.reactchallenges.com/challenges/invoker-modal&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ✅ Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Wire the "Contact Us" button to open the dialog using &lt;code&gt;command&lt;/code&gt; and &lt;code&gt;commandfor&lt;/code&gt; attributes&lt;/li&gt;
&lt;li&gt;Wire the "Close" button to close the dialog&lt;/li&gt;
&lt;li&gt;The dialog must start closed&lt;/li&gt;
&lt;li&gt;Keep the existing form fields and &lt;code&gt;data-testid&lt;/code&gt; attributes intact&lt;/li&gt;
&lt;li&gt;Do not remove the TypeScript module augmentation block&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A button uses &lt;code&gt;command&lt;/code&gt; and &lt;code&gt;commandfor&lt;/code&gt; to tell the browser to call &lt;code&gt;showModal()&lt;/code&gt; or &lt;code&gt;close()&lt;/code&gt; — no JavaScript needed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useId()&lt;/code&gt; from React is the idiomatic way to generate a unique id for linking button to dialog.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;declare module "react"&lt;/code&gt; block already adds &lt;code&gt;command&lt;/code&gt; and &lt;code&gt;commandfor&lt;/code&gt; to React's button types.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧪 Tests
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Renders the app title&lt;/li&gt;
&lt;li&gt;Renders the open button with command attributes&lt;/li&gt;
&lt;li&gt;Matches the button commandfor with the dialog id&lt;/li&gt;
&lt;li&gt;Dialog does not have the open attribute initially&lt;/li&gt;
&lt;li&gt;Close button has command close with matching commandfor&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This challenge is perfect for developers who want to rely more on the platform and write less JavaScript. You'll learn a native browser API that makes modals simpler, more accessible, and more resilient.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.reactchallenges.com/challenges/invoker-modal" rel="noopener noreferrer"&gt;🔥 Start the Challenge Now&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>frontendchallenge</category>
      <category>frontend</category>
    </item>
    <item>
      <title>🚀 New React Challenge: Tier List</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Mon, 06 Jul 2026 11:44:05 +0000</pubDate>
      <link>https://dev.to/reactchallenges/new-react-challenge-tier-list-3n4g</link>
      <guid>https://dev.to/reactchallenges/new-react-challenge-tier-list-3n4g</guid>
      <description>&lt;p&gt;Ever built a drag-and-drop UI with just the native browser API? No &lt;code&gt;react-beautiful-dnd&lt;/code&gt;, no &lt;code&gt;dnd-kit&lt;/code&gt; — just &lt;code&gt;dragStart&lt;/code&gt;, &lt;code&gt;dragOver&lt;/code&gt;, and &lt;code&gt;drop&lt;/code&gt; events. In this challenge, you'll build a Tier List app where users upload images and rank them A through F by dragging, mastering the HTML5 Drag and Drop API along the way.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.reactchallenges.com/challenges/tier-list" rel="noopener noreferrer"&gt;https://www.reactchallenges.com/challenges/tier-list&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;Build a tier maker app where users upload images and drag them into ranked tiers (A through F) — practicing HTML5 Drag and Drop and file uploads in React.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Render a "Tier List" title at the top of the page&lt;/li&gt;
&lt;li&gt;Render six tier rows labeled A, B, C, D, E, F with the correct background colors&lt;/li&gt;
&lt;li&gt;Include two action buttons: Add (file picker) and Reset (moves all images back)&lt;/li&gt;
&lt;li&gt;Provide a selector area at the bottom where unassigned images appear&lt;/li&gt;
&lt;li&gt;Start with an empty selector and empty tier rows&lt;/li&gt;
&lt;li&gt;File upload: selecting images via file picker adds them to the selector; dragging files from desktop onto the selector also works&lt;/li&gt;
&lt;li&gt;Drag and drop: drag images from selector to tiers, between tiers, and back to the selector&lt;/li&gt;
&lt;li&gt;Highlight the drop zone while dragging over it&lt;/li&gt;
&lt;li&gt;Reset moves all images from tier rows back into the selector area&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you've never worked with the native Drag and Drop API before, this is the perfect challenge to get comfortable with it. You'll walk away knowing how to handle file uploads, drag events, and complex UI state — all inside a custom hook.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.reactchallenges.com/challenges/tier-list" rel="noopener noreferrer"&gt;🔥 Start the Challenge Now&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>frontendchallenge</category>
    </item>
    <item>
      <title>Cognitive Surrender and Why You Should Still Write Code by Hand</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Mon, 22 Jun 2026 08:47:32 +0000</pubDate>
      <link>https://dev.to/reactchallenges/cognitive-surrender-and-why-you-should-still-write-code-by-hand-29ab</link>
      <guid>https://dev.to/reactchallenges/cognitive-surrender-and-why-you-should-still-write-code-by-hand-29ab</guid>
      <description>&lt;p&gt;A study from the University of Pennsylvania introduced a term that should make every developer pause: &lt;strong&gt;cognitive surrender&lt;/strong&gt;. Across 9,500 trials with over 1,300 participants, researchers found that when an AI provided an answer — even a wrong one — people accepted it without scrutiny 73.2% of the time. Not because the answers were convincing. Because the mere presence of a confident AI response was enough to shut down the part of the brain that says &lt;em&gt;"wait, let me think about this."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you write code for a living, you already know where this is going.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 Try it in practice: &lt;a href="https://www.reactchallenges.com/challenges/055-simple-pagination" rel="noopener noreferrer"&gt;Simple pagination&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The shortcut that becomes the habit
&lt;/h2&gt;

&lt;p&gt;The researchers describe a shift from two systems of thinking to a third: &lt;strong&gt;artificial cognition&lt;/strong&gt;. Instead of reasoning through a problem, you ask the machine. Instead of verifying, you paste and move on.&lt;/p&gt;

&lt;p&gt;For developers, this is now routine. You need a pagination component. You type a prompt. You get a &lt;code&gt;fetch&lt;/code&gt; inside a &lt;code&gt;useEffect&lt;/code&gt;, some &lt;code&gt;useState&lt;/code&gt;, a loading skeleton. It works. You ship it. You don't think about it again.&lt;/p&gt;

&lt;p&gt;But the study found something revealing: participants with higher &lt;strong&gt;fluid IQ&lt;/strong&gt; — the ability to solve novel problems — were far less likely to be misled by faulty AI. The skill that protected them wasn't knowing more. It was &lt;strong&gt;the habit of reasoning things through&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where AI belongs (and where it doesn't)
&lt;/h2&gt;

&lt;p&gt;None of this means you should ditch AI. It means you should be intentional about where you deploy it.&lt;/p&gt;

&lt;p&gt;AI is great for &lt;strong&gt;mechanical, repetitive work that requires zero reasoning&lt;/strong&gt;. Generating Storybook stories for every component variant. Writing boilerplate unit tests that follow a predictable pattern. Scaffolding a folder structure. Tasks where the thinking is already done and what's left is typing.&lt;/p&gt;

&lt;p&gt;But when the task involves a decision — &lt;em&gt;how should this state be structured? what does the user need when this errors? should this be optimistic or not?&lt;/em&gt; — that's where you want the friction. That's the muscle you don't want to atrophy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use it or lose it
&lt;/h2&gt;

&lt;p&gt;Writing a pagination component is not hard. Fetch data, track the page, handle loading, surface errors. An LLM can do it in three seconds.&lt;/p&gt;

&lt;p&gt;But the point of writing it yourself is not the result. It's the friction.&lt;/p&gt;

&lt;p&gt;When you write a &lt;code&gt;fetch&lt;/code&gt; call manually, you remember how &lt;code&gt;AbortController&lt;/code&gt; works. When you toggle loading and error states by hand, you internalize async UI patterns. When you decide what the retry button looks like, you think about the user. None of that happens when you accept a generated block and move on.&lt;/p&gt;

&lt;p&gt;The experiment design was telling: participants used a modified LLM that gave wrong answers half the time. When the AI was accurate, 93% accepted it. When it was faulty, that only dropped to 80%. Even when the machine was &lt;strong&gt;demonstrably wrong&lt;/strong&gt;, four out of five went along with it. The AI didn't just provide answers — it &lt;strong&gt;lowered the threshold for scrutiny&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is not about rejecting AI tools. It's about recognizing that reasoning ability — like any other skill — &lt;strong&gt;atrophies without use&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the muscle alive
&lt;/h2&gt;

&lt;p&gt;The fix is mechanically simple: &lt;strong&gt;practice without the net&lt;/strong&gt;. Not every day, not for every task. But regularly enough that the mental pathways stay warm.&lt;/p&gt;

&lt;p&gt;Pick a problem you could solve with a prompt and solve it manually. Write the pagination from an empty file. Handle the errors. Wire up the loading states. The value is not the code — it's staying fluent enough to know when the AI is wrong, and sharp enough to fix it without asking the AI again.&lt;/p&gt;

&lt;p&gt;Delegate the boilerplate. Protect the thinking.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 Try it in practice: &lt;a href="https://www.reactchallenges.com/challenges/055-simple-pagination" rel="noopener noreferrer"&gt;Simple pagination&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>development</category>
    </item>
    <item>
      <title>🚀 New React Challenge: Simple Pagination</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Sun, 21 Jun 2026 10:35:57 +0000</pubDate>
      <link>https://dev.to/reactchallenges/new-react-challenge-simple-pagination-5am4</link>
      <guid>https://dev.to/reactchallenges/new-react-challenge-simple-pagination-5am4</guid>
      <description>&lt;p&gt;In a time where AI writes most of our boilerplate, manually wiring a fetch with loading, error, and pagination states is exactly the kind of thing that slips through the cracks — and exactly what keeps coming up in job interviews.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;A plain fetch with pagination and manual loading and error handling. No libraries, no abstractions — just &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, and a mock API that delays, paginates, and randomly fails. The kind of challenge that keeps your fundamentals sharp.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://www.reactchallenges.com/challenges/simple-pagination" rel="noopener noreferrer"&gt;https://www.reactchallenges.com/challenges/simple-pagination&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fetch users from &lt;code&gt;https://example/users?page={page}&lt;/code&gt; and display them in a table (ID, Name, Last name). The API returns up to 10 users per page.&lt;/li&gt;
&lt;li&gt;Show a loading skeleton while fetching.&lt;/li&gt;
&lt;li&gt;Display page info: &lt;code&gt;Page {page} of {totalPages} ({count} users)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Pagination buttons:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First&lt;/strong&gt;: goes to page 0. Disabled when on page 0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Previous&lt;/strong&gt;: goes to the previous page. Disabled when on page 0.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next&lt;/strong&gt;: goes to the next page. Disabled when on the last page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Last&lt;/strong&gt;: goes to the last page. Disabled when on the last page.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;All buttons disabled when count is 0 or while loading.&lt;/li&gt;
&lt;li&gt;Display an error message when the request fails.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you've been leaning on AI for the boilerplate lately, this one will remind you how much muscle memory matters. Simple fetch, clean state, solid edge cases — exactly what separates a working prototype from interview-ready code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.reactchallenges.com/challenges/simple-pagination" rel="noopener noreferrer"&gt;🔥 Start the Challenge Now&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>frontendchallenge</category>
    </item>
    <item>
      <title>🚀 New React Challenge: Build a Spreadsheet with Formula Evaluation</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Thu, 11 Jun 2026 09:55:42 +0000</pubDate>
      <link>https://dev.to/reactchallenges/new-react-challenge-build-a-spreadsheet-with-formula-evaluation-3hic</link>
      <guid>https://dev.to/reactchallenges/new-react-challenge-build-a-spreadsheet-with-formula-evaluation-3hic</guid>
      <description>&lt;p&gt;You've built todo apps, counters, and forms. But can you handle a grid of 50 cells that reference each other through formulas? This challenge pushes your state management skills into real spreadsheet territory — formula evaluation, two-way cell bindings, and an interface that juggles editing, selection, and keyboard shortcuts all at once.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.reactchallenges.com/challenges/spreadsheet" rel="noopener noreferrer"&gt;🔥 Start the Challenge Now&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;Build a spreadsheet with real-time formula evaluation. You'll wire up a 10-row × 5-column grid where cells support basic values and Excel-style formulas (like =A1+B2), column and row selection, and a formula bar that mirrors what you're typing.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Render a spreadsheet with column headers A through E and rows 1 through 10&lt;/li&gt;
&lt;li&gt;Each cell uses an &lt;code&gt;&amp;lt;output&amp;gt;&lt;/code&gt; element for the computed value and an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; overlaid for editing&lt;/li&gt;
&lt;li&gt;Click a cell to edit; press Enter or blur to commit the change&lt;/li&gt;
&lt;li&gt;Formulas starting with &lt;code&gt;=&lt;/code&gt; must be evaluated:

&lt;ul&gt;
&lt;li&gt;Arithmetic: &lt;code&gt;=1+1&lt;/code&gt; → &lt;code&gt;2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Cell references: A1=&lt;code&gt;5&lt;/code&gt; and B1=&lt;code&gt;=A1+3&lt;/code&gt; → &lt;code&gt;8&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click a column header to select/deselect that column&lt;/li&gt;
&lt;li&gt;Click a row number to select/deselect that row&lt;/li&gt;
&lt;li&gt;Selecting a column deselects any row and vice-versa&lt;/li&gt;
&lt;li&gt;Backspace clears the selected column or row&lt;/li&gt;
&lt;li&gt;Click outside the table deselects everything&lt;/li&gt;
&lt;li&gt;A formula bar (&lt;code&gt;fx&lt;/code&gt;) mirrors the editing cell's value&lt;/li&gt;
&lt;li&gt;Cells must start empty — no default values&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;useState&lt;/code&gt; for cells, selected column, and selected row. No need for useReducer here.&lt;/li&gt;
&lt;li&gt;Each cell uses two overlapping layers: a visible &lt;code&gt;&amp;lt;output&amp;gt;&lt;/code&gt; for the computed value and an invisible &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; for the raw formula. Toggle with &lt;code&gt;opacity-0&lt;/code&gt; / &lt;code&gt;opacity-100&lt;/code&gt; so the input stays mounted.&lt;/li&gt;
&lt;li&gt;Evaluate formulas with &lt;code&gt;eval&lt;/code&gt;: generate JS &lt;code&gt;const&lt;/code&gt; declarations from all cell values, wrap them in an IIFE, and evaluate. Recompute &lt;strong&gt;every&lt;/strong&gt; cell on any change — cells can reference each other.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧪 Tests
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;renders the app title&lt;/li&gt;
&lt;li&gt;renders the spreadsheet with column headers A-E and rows 1-10&lt;/li&gt;
&lt;li&gt;renders cells with initial empty values&lt;/li&gt;
&lt;li&gt;allows editing a cell and displays the new computed value&lt;/li&gt;
&lt;li&gt;evaluates a simple formula&lt;/li&gt;
&lt;li&gt;evaluates a formula with cell references&lt;/li&gt;
&lt;li&gt;re-evaluates formulas when a referenced cell is cleared&lt;/li&gt;
&lt;li&gt;selects a column when clicking the column header&lt;/li&gt;
&lt;li&gt;deselects column when clicking the same header again&lt;/li&gt;
&lt;li&gt;selects a row when clicking the row number&lt;/li&gt;
&lt;li&gt;deselects row when clicking the same row number again&lt;/li&gt;
&lt;li&gt;deselects column when selecting a row and vice-versa&lt;/li&gt;
&lt;li&gt;clears the column when Backspace is pressed with a column selected&lt;/li&gt;
&lt;li&gt;clears the row when Backspace is pressed with a row selected&lt;/li&gt;
&lt;li&gt;deselects column when clicking outside the spreadsheet&lt;/li&gt;
&lt;/ol&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.reactchallenges.com/challenges/spreadsheet" rel="noopener noreferrer"&gt;🔥 Start the Challenge Now&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This one will stretch your understanding of React state in ways a simple counter or todo list never could. Formula engines, overlay editing, and cascading recomputation — all with just hooks and vanilla React.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>frontendchallenge</category>
    </item>
    <item>
      <title>Your tests pass. Your linter doesn't.</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Mon, 08 Jun 2026 13:02:34 +0000</pubDate>
      <link>https://dev.to/reactchallenges/your-tests-pass-your-linter-doesnt-4mga</link>
      <guid>https://dev.to/reactchallenges/your-tests-pass-your-linter-doesnt-4mga</guid>
      <description>&lt;p&gt;Every React developer has written code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setList&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;showClearCompleted&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setShowClearCompleted&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setShowClearCompleted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isComplete&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app works. The tests pass. The feature is shipped. And nobody notices that this code has a bug — not a functional bug, but a &lt;strong&gt;structural one&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The silent lie of derived state
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;setShowClearCompleted&lt;/code&gt; inside a &lt;code&gt;useEffect&lt;/code&gt; that watches &lt;code&gt;list&lt;/code&gt; is the React equivalent of buying a notebook, writing down the result of &lt;code&gt;2 + 2&lt;/code&gt;, and then, every time one of the numbers changes, erasing it and writing &lt;code&gt;4&lt;/code&gt; again — instead of just asking "what is &lt;code&gt;2 + 2&lt;/code&gt;?" when you need the answer.&lt;/p&gt;

&lt;p&gt;The value of &lt;code&gt;showClearCompleted&lt;/code&gt; is &lt;strong&gt;derived&lt;/strong&gt; from &lt;code&gt;list&lt;/code&gt;. It is not a separate piece of state. It is a computation over state you already have. Yet the code above treats it as if it were independent — storing it, syncing it, and triggering an extra render to apply it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 Try it yourself: &lt;a href="https://www.reactchallenges.com/challenges/best-practices" rel="noopener noreferrer"&gt;Challenge #53 — Best Practices&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is what the corrected version looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setList&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;showClearCompleted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isComplete&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No effect. No extra state variable. No cascading render. Just a value computed from state you already own. The component renders, the computation runs, the UI updates — all in a single pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters: cascading renders
&lt;/h2&gt;

&lt;p&gt;When you call &lt;code&gt;setState&lt;/code&gt; inside a &lt;code&gt;useEffect&lt;/code&gt;, React has to do &lt;strong&gt;two renders&lt;/strong&gt; instead of one. The first render computes the effect dependencies. The effect fires. It calls &lt;code&gt;setState&lt;/code&gt;. React schedules a second render with the new state.&lt;/p&gt;

&lt;p&gt;In a small component, you won't feel it. In a page with dozens of components, each with their own derived-state effects, the cascade compounds. You get flickers, stale intermediate states, and a performance profile that is needlessly heavy for no reason at all.&lt;/p&gt;

&lt;p&gt;And the worst part? If you only test the output — the DOM, the rendered text, the visible behavior — &lt;strong&gt;you will never catch this&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Most platforms don't check. We do.
&lt;/h2&gt;

&lt;p&gt;Go to any coding challenge site. Pick a React challenge. Write the version with &lt;code&gt;useEffect&lt;/code&gt;. Run the tests. They pass. Congratulations, you solved it.&lt;/p&gt;

&lt;p&gt;But did you write &lt;em&gt;good React&lt;/em&gt;? The answer depends on what "good" means. If it means "the tests pass," then yes. If it means "idiomatic, performant, maintainable React that your coworkers won't silently judge during code review," then no.&lt;/p&gt;

&lt;p&gt;Challenge #53 on React Challenges is different. When you hit &lt;strong&gt;Run Tests&lt;/strong&gt;, two things happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your code runs against a suite of behavioral tests (does the todo list add, delete, toggle, and clear items?)&lt;/li&gt;
&lt;li&gt;Your code is checked against an &lt;strong&gt;ESLint configuration&lt;/strong&gt; that enforces React best practices&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you use &lt;code&gt;useEffect&lt;/code&gt; to sync derived state, the linter catches it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ✗ App.tsx:29:5  error
   react-hooks/set-state-in-effect
   Calling setState synchronously within an effect causes cascading renders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the challenge is &lt;strong&gt;not considered complete&lt;/strong&gt; until all lint rules pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  The test-passing trap
&lt;/h2&gt;

&lt;p&gt;There is a quiet assumption in the coding challenge world: if the tests pass, you are done. This assumption trains developers into habits that are fine for a green checkmark and dangerous for a production codebase.&lt;/p&gt;

&lt;p&gt;You can write a component that works and is wrong. You can render the correct UI with code that silently burns through render cycles, hides derived data behind state machines, and uses effects as event handlers because the mental model of React's render cycle never fully clicked.&lt;/p&gt;

&lt;p&gt;Most platforms let you walk away with the green checkmark and a false sense of mastery. We wanted to build one that tells you the second thing: &lt;strong&gt;yes, it works — but it is not right yet&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And for that, you need a linter.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 Try it yourself: &lt;a href="https://www.reactchallenges.com/challenges/best-practices" rel="noopener noreferrer"&gt;Challenge #53 — Best Practices&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>react</category>
      <category>testing</category>
      <category>useeffect</category>
      <category>javascript</category>
    </item>
    <item>
      <title>🚀 New React Challenge: Best Practices</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Mon, 08 Jun 2026 11:25:53 +0000</pubDate>
      <link>https://dev.to/reactchallenges/new-challenge-best-practices-280e</link>
      <guid>https://dev.to/reactchallenges/new-challenge-best-practices-280e</guid>
      <description>&lt;p&gt;Your todo list passes all tests. You ship it. But here's the thing no one tells you about interviews — reviewers don't just check that your code works. They look for the 3 anti-patterns hiding in plain sight. This challenge puts ESLint in the reviewer's seat and asks you to clean up the code without breaking a single test.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 &lt;a href="https://www.reactchallenges.com/challenges/best-practices" rel="noopener noreferrer"&gt;https://www.reactchallenges.com/challenges/best-practices&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;In a real technical interview, solving the logic is only half the battle. Reviewers are trained to spot lazy &lt;code&gt;useState&lt;/code&gt; initializers, derived state hidden inside &lt;code&gt;useEffect&lt;/code&gt;, and impure calls during render. This challenge gives you a working todo list and surfaces exactly those violations through automated linting. Fix all 3 warnings without changing how the app behaves.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fix all ESLint warnings without changing app behavior&lt;/li&gt;
&lt;li&gt;All 6 tests must keep passing after your changes&lt;/li&gt;
&lt;li&gt;Resolve these 3 violations:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@eslint-react/use-state&lt;/code&gt; — pass a lazy initializer instead of calling the function eagerly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;react-hooks/set-state-in-effect&lt;/code&gt; — derive &lt;code&gt;showClearCompleted&lt;/code&gt; directly from state, skip the &lt;code&gt;useEffect&lt;/code&gt; workaround&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@eslint-react/purity&lt;/code&gt; — move &lt;code&gt;new Date()&lt;/code&gt; out of the render path&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;p&gt;Three small fixes, three big "aha" moments. Whether you're prepping for React interviews or just want to stop shipping code that silently wastes renders, this one will sharpen the way you read your own components. No new features — just cleaner, faster React.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.reactchallenges.com/challenges/best-practices" rel="noopener noreferrer"&gt;🔥 Start the Challenge Now&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>frontendchallenge</category>
    </item>
    <item>
      <title>What useOptimistic Actually Saves You</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Tue, 26 May 2026 17:20:19 +0000</pubDate>
      <link>https://dev.to/reactchallenges/what-useoptimistic-actually-saves-you-1hm</link>
      <guid>https://dev.to/reactchallenges/what-useoptimistic-actually-saves-you-1hm</guid>
      <description>&lt;p&gt;A checkbox toggle should feel instant. But when that toggle needs to persist to a server, you face a choice: wait for the response and feel sluggish, or update immediately and handle the fallout. The second option — optimistic UI — is better for users, but the manual implementation adds state, flags, and try/catch blocks that pile up fast. For a simple checkbox the difference is small — a few lines. For anything more complex, the gap widens quickly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 Try it in practice: &lt;a href="https://www.reactchallenges.com/challenges/use-optimistic" rel="noopener noreferrer"&gt;useOptimistic&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The manual way: state + pending + rollback
&lt;/h2&gt;

&lt;p&gt;Here's a typical TODO checkbox that optimistically updates the UI before the server responds. No libraries — just React's built-in hooks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onToggle&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setChecked&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPending&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleToggle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setChecked&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setPending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;onToggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setChecked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setPending&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;
        &lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleToggle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;26 lines for one checkbox. The &lt;code&gt;try/catch&lt;/code&gt; handles a server error, but what if the component unmounts while the request is in flight? What if the parent updates &lt;code&gt;todo.completed&lt;/code&gt; from a different source while your toggle is pending — the local &lt;code&gt;checked&lt;/code&gt; state and the prop drift apart, and the catch handler restores a stale value. Each edge case adds more state and more branches.&lt;/p&gt;

&lt;h2&gt;
  
  
  With useOptimistic
&lt;/h2&gt;

&lt;p&gt;Now the same feature with &lt;code&gt;useOptimistic&lt;/code&gt; and &lt;code&gt;startTransition&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;TodoItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onToggle&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;optimisticChecked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addOptimistic&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useOptimistic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleToggle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;startTransition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&gt;addOptimistic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;onToggle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;
        &lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;optimisticChecked&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleToggle&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;23 lines. Not a dramatic difference in raw line count. The savings aren't in how many lines you type — they're in what you no longer have to think about.&lt;/p&gt;

&lt;p&gt;Here's why: &lt;code&gt;startTransition&lt;/code&gt; wraps the async work in a React Transition. When the transition ends (on success or error), React re-renders the component. At that point, &lt;code&gt;useOptimistic&lt;/code&gt; simply renders whatever &lt;code&gt;todo.completed&lt;/code&gt; is. If the parent updated it — success, the checkbox stays checked. If the parent didn't update it — failure, the checkbox goes back to how it was. The rollback isn't "built-in error handling." It's just a consequence of the passthrough prop not changing.&lt;/p&gt;

&lt;p&gt;The manual version needs &lt;code&gt;try/catch&lt;/code&gt; for a different reason: it has a separate &lt;code&gt;pending&lt;/code&gt; flag and a separate &lt;code&gt;checked&lt;/code&gt; state. If &lt;code&gt;onToggle&lt;/code&gt; throws, both are stuck in the wrong value unless you manually reset them. With &lt;code&gt;useOptimistic&lt;/code&gt;, there's nothing to reset — the prop is the only source of truth.&lt;/p&gt;

&lt;p&gt;For a checkbox, it's a few lines. For anything more complex — adding to a list, toggling related fields, or managing multiple optimistic values — the manual approach balloons while the &lt;code&gt;useOptimistic&lt;/code&gt; version barely grows. That's where the real savings live.&lt;/p&gt;

&lt;p&gt;This doesn't mean you can skip error handling entirely. The UI reverts on its own, but you still want to show an error toast, log the failure, or offer a retry — all of which happen outside the optimistic state logic. &lt;code&gt;useOptimistic&lt;/code&gt; handles what the checkbox shows; you still handle what the user should know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep complexity at the edge
&lt;/h2&gt;

&lt;p&gt;There's a pattern here that goes beyond this checkbox.&lt;/p&gt;

&lt;p&gt;Any time your code has to deal with something external — a server response, a user action, an API call — you have two options. You can let that uncertainty spread through your functions and components, or you can contain it at the boundary where it enters.&lt;/p&gt;

&lt;p&gt;The Zod rule &lt;strong&gt;validate at system boundaries&lt;/strong&gt; does exactly this for server-side data. Validate the request body at the endpoint, and every function downstream receives clean, typed data. No more checking "is this field a string?" ten layers deep.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useOptimistic&lt;/code&gt; does the same thing for UI state. The user clicks, and instead of spraying &lt;code&gt;pending&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; flags across your component, you declare the optimistic change right where the user acted — inside &lt;code&gt;startTransition&lt;/code&gt;. The hook keeps the UI in sync with the prop. Everything downstream just renders.&lt;/p&gt;

&lt;p&gt;One boundary, one place to think about it, one thing to debug.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;👉 Try it in practice: &lt;a href="https://www.reactchallenges.com/challenges/use-optimistic" rel="noopener noreferrer"&gt;useOptimistic&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>useoptimistic</category>
      <category>react19</category>
    </item>
    <item>
      <title>🚀 New React Challenge: Instant UI with useOptimistic</title>
      <dc:creator>ReactChallenges</dc:creator>
      <pubDate>Sat, 23 May 2026 10:37:07 +0000</pubDate>
      <link>https://dev.to/reactchallenges/new-react-challenge-instant-ui-with-useoptimistic-16lc</link>
      <guid>https://dev.to/reactchallenges/new-react-challenge-instant-ui-with-useoptimistic-16lc</guid>
      <description>&lt;p&gt;Ever clicked a like button and watched it stall for a second while the server thinks about it? That tiny delay makes your app feel sluggish. React 19 ships &lt;code&gt;useOptimistic&lt;/code&gt; to fix exactly that — the UI changes &lt;em&gt;before&lt;/em&gt; the server responds, and snaps back automatically if something goes wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.reactchallenges.com/challenges/use-optimistic" rel="noopener noreferrer"&gt;🔥 Start the Challenge Now&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Overview
&lt;/h2&gt;

&lt;p&gt;Learn React's &lt;code&gt;useOptimistic&lt;/code&gt; hook by building instant heart toggles. Click the heart, see it turn red immediately, and let React handle the server in the background. If the request fails, the heart reverts on its own — no manual rollback code.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Requirements
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Render two cards side by side: one labeled "Successful toggle" and another "Error toggle".&lt;/li&gt;
&lt;li&gt;Both cards start with the heart in a neutral (not liked) state.&lt;/li&gt;
&lt;li&gt;Clicking the heart on the &lt;strong&gt;successful toggle&lt;/strong&gt; card must:

&lt;ul&gt;
&lt;li&gt;Turn the heart red &lt;strong&gt;immediately&lt;/strong&gt;, before the server responds.&lt;/li&gt;
&lt;li&gt;Keep the heart red after the server confirms the change.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Clicking the heart on the &lt;strong&gt;error toggle&lt;/strong&gt; card must:

&lt;ul&gt;
&lt;li&gt;Turn the heart red &lt;strong&gt;immediately&lt;/strong&gt;, before the server responds.&lt;/li&gt;
&lt;li&gt;Revert the heart to neutral after the server responds with an error.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Use the &lt;code&gt;useOptimistic&lt;/code&gt; hook together with &lt;code&gt;startTransition&lt;/code&gt; to manage the optimistic state.&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 Notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;addOptimistic&lt;/code&gt; must be called inside &lt;code&gt;startTransition&lt;/code&gt; — otherwise React warns even though the UI still updates.&lt;/li&gt;
&lt;li&gt;The passthrough state only needs to be a &lt;code&gt;boolean&lt;/code&gt; (just the like value). The rest of &lt;code&gt;data&lt;/code&gt; can stay static.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;toggleFavoriteError&lt;/code&gt; always throws. A try/catch with &lt;code&gt;console.error&lt;/code&gt; prevents the red error overlay in dev without affecting the optimistic revert.&lt;/li&gt;
&lt;li&gt;The data-testid attributes in the starter are already wired to the tests — don't rename or remove them.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧪 Tests
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;renders the app title&lt;/li&gt;
&lt;li&gt;renders both cards with labels&lt;/li&gt;
&lt;li&gt;starts with hearts not liked&lt;/li&gt;
&lt;li&gt;optimistically toggles the heart immediately and persists on success&lt;/li&gt;
&lt;li&gt;reverts the heart on server error&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;If you've been writing &lt;code&gt;setState(optimisticValue)&lt;/code&gt;, then &lt;code&gt;try/catch&lt;/code&gt; with manual rollbacks, &lt;code&gt;useOptimistic&lt;/code&gt; deletes all that boilerplate. Give it a try and feel how snappy your UI can be.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.reactchallenges.com/challenges/use-optimistic" rel="noopener noreferrer"&gt;🔥 Start the Challenge Now&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>frontendchallenge</category>
    </item>
  </channel>
</rss>
