<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Blog - Sai Hari]]></title><description><![CDATA[I'm a full-stack developer with a focus on the front end. Discover tips on software development, improving developer experience, and other thoughts.]]></description><link>https://blog.thesshguy.com</link><generator>RSS for Node</generator><lastBuildDate>Sun, 06 Sep 2026 17:53:27 GMT</lastBuildDate><atom:link href="https://blog.thesshguy.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[React useEffect Explained: When Effects Execute]]></title><description><![CDATA[React's useEffect hook is an important tool for managing side effects in your functional components.
In this post, we'll look at when your effects run.
Effects run on the initial render
Every effect runs at least once when your component first render...]]></description><link>https://blog.thesshguy.com/useeffect-executes</link><guid isPermaLink="true">https://blog.thesshguy.com/useeffect-executes</guid><category><![CDATA[React]]></category><category><![CDATA[ReactHooks]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Tue, 11 Jun 2024 14:03:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1718086008938/95b849be-81a1-4aaa-8533-78e262aee0da.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>React's <code>useEffect</code> hook is an important tool for managing side effects in your functional components.</p>
<p>In this post, we'll look at <em>when</em> your effects run.</p>
<h2 id="heading-effects-run-on-the-initial-render">Effects run on the initial render</h2>
<p>Every effect runs <em>at least</em> once when your component first renders. In the example below, the log statement in the <code>useEffect</code> hook executes without you having to do anything:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3N4bzZra3B0dTdoNjd1em05YTNjZWZyYyIsIm5hbWUiOiJSZWFjdCB1c2VFZmZlY3QgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG59XG5cbmgxIHtcbiAgZm9udC1zaXplOiAxLjVyZW07XG59In0sIi9BcHAuanMiOnsiY29kZSI6ImltcG9ydCB7IHVzZUVmZmVjdCB9IGZyb20gXCJyZWFjdFwiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHVzZUVmZmVjdCgoKSA9PiB7XG4gICAgY29uc29sZS5sb2coXCJJbml0aWFsIFJlbmRlclwiKTtcbiAgfSwgW10pO1xuXG4gIHJldHVybiA8aDE+SGVsbG8gdXNlRWZmZWN0ITwvaDE+O1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKDxBcHAgLz4pO1xuIn0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifX0sInRlbXBsYXRlIjoicmVhY3QifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6dHJ1ZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6dHJ1ZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6dHJ1ZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19LCJDb25zb2xlIjp7InR5cGUiOiJjb25zb2xlIiwiZGF0YSI6eyJzaG93SGVhZGVyIjpmYWxzZSwic2hvd1N5bnRheEVycm9yIjpmYWxzZSwic2hvd1NldHVwUHJvZ3Jlc3MiOmZhbHNlLCJtYXhNZXNzYWdlQ291bnQiOjEwLCJyZXNldE9uUHJldmlld1Jlc3RhcnQiOmZhbHNlLCJzdGFuZGFsb25lIjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvciBFZGl0b3InXG4nUHJldmlldyBDb25zb2xlJyJ9fX0=" height="750px" width="100%"></iframe>

<h2 id="heading-effects-run-every-render-if-there-are-no-dependencies">Effects run every render if there are no dependencies</h2>
<p>In the previous example, we passed an empty array as the second argument to the <code>useEffect</code> hook. This argument is called the <strong>dependency array</strong>. The array controls <em>when</em> the effect runs after the first render.</p>
<p>If you don't pass a second argument to the <code>useEffect</code> hook, your effect will run on <em>every</em> render. In the example below, the log statement runs every time you click the <strong>Force Rerender</strong> button:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3N4bzZra3B0dTdoNjd1em05YTNjZWZyYyIsIm5hbWUiOiJSZWFjdCB1c2VFZmZlY3QgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG59XG5cbmJ1dHRvbiB7XG4gIC0tYjogM3B4OyAvKiBib3JkZXIgdGhpY2tuZXNzICovXG4gIC0tczogMC40NWVtOyAvKiBzaXplIG9mIHRoZSBjb3JuZXIgKi9cbiAgLS1jb2xvcjogIzM3M2I0NDtcblxuICBjdXJzb3I6IHBvaW50ZXI7XG4gIHBhZGRpbmc6IGNhbGMoMC41ZW0gKyB2YXIoLS1zKSkgY2FsYygwLjllbSArIHZhcigtLXMpKTtcbiAgY29sb3I6IHZhcigtLWNvbG9yKTtcbiAgLS1fcDogdmFyKC0tcyk7XG4gIGJhY2tncm91bmQ6IGNvbmljLWdyYWRpZW50KFxuICAgICAgZnJvbSA5MGRlZyBhdCB2YXIoLS1iKSB2YXIoLS1iKSxcbiAgICAgICMwMDAwIDkwZGVnLFxuICAgICAgdmFyKC0tY29sb3IpIDBcbiAgICApXG4gICAgdmFyKC0tX3ApIHZhcigtLV9wKSAvIGNhbGMoMTAwJSAtIHZhcigtLWIpIC0gMiAqIHZhcigtLV9wKSlcbiAgICBjYWxjKDEwMCUgLSB2YXIoLS1iKSAtIDIgKiB2YXIoLS1fcCkpO1xuICB0cmFuc2l0aW9uOlxuICAgIDAuM3MgbGluZWFyLFxuICAgIGNvbG9yIDBzLFxuICAgIGJhY2tncm91bmQtY29sb3IgMHM7XG4gIG91dGxpbmU6IHZhcigtLWIpIHNvbGlkICMwMDAwO1xuICBvdXRsaW5lLW9mZnNldDogMC42ZW07XG4gIGZvbnQtc2l6ZTogMTZweDtcblxuICBib3JkZXI6IDA7XG5cbiAgdXNlci1zZWxlY3Q6IG5vbmU7XG4gIC13ZWJraXQtdXNlci1zZWxlY3Q6IG5vbmU7XG4gIHRvdWNoLWFjdGlvbjogbWFuaXB1bGF0aW9uO1xufVxuXG5idXR0b246aG92ZXIsXG5idXR0b246Zm9jdXMtdmlzaWJsZSB7XG4gIC0tX3A6IDBweDtcbiAgb3V0bGluZS1jb2xvcjogdmFyKC0tY29sb3IpO1xuICBvdXRsaW5lLW9mZnNldDogMC4wNWVtO1xufVxuXG5idXR0b246YWN0aXZlIHtcbiAgYmFja2dyb3VuZDogdmFyKC0tY29sb3IpO1xuICBjb2xvcjogI2ZmZjtcbn1cbiJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJpbXBvcnQgeyB1c2VFZmZlY3QgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IHVzZVJlcmVuZGVyIH0gZnJvbSBcIi4vdXRpbHNcIjtcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICBjb25zdCByZXJlbmRlciA9IHVzZVJlcmVuZGVyKCk7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBjb25zb2xlLmxvZyhcIkV2ZXJ5IFJlbmRlclwiKTtcbiAgfSk7XG5cbiAgcmV0dXJuIDxidXR0b24gb25DbGljaz17cmVyZW5kZXJ9PkZvcmNlIFJlcmVuZGVyPC9idXR0b24+O1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKDxBcHAgLz4pO1xuIn0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifSwiL3V0aWxzLmpzIjp7ImNvZGUiOiJpbXBvcnQgeyB1c2VTdGF0ZSB9IGZyb20gXCJyZWFjdFwiO1xuXG5leHBvcnQgY29uc3QgdXNlUmVyZW5kZXIgPSAoKSA9PiB7XG4gIGNvbnN0IFtzdGF0ZSwgc2V0U3RhdGVdID0gdXNlU3RhdGUoMCk7XG4gIHJldHVybiAoKSA9PiBzZXRTdGF0ZShzdGF0ZSArIDEpO1xufTtcbiJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX0sIkNvbnNvbGUiOnsidHlwZSI6ImNvbnNvbGUiLCJkYXRhIjp7InNob3dIZWFkZXIiOmZhbHNlLCJzaG93U3ludGF4RXJyb3IiOmZhbHNlLCJzaG93U2V0dXBQcm9ncmVzcyI6ZmFsc2UsIm1heE1lc3NhZ2VDb3VudCI6MTAsInJlc2V0T25QcmV2aWV3UmVzdGFydCI6ZmFsc2UsInN0YW5kYWxvbmUiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yIEVkaXRvcidcbidQcmV2aWV3IENvbnNvbGUnIn19fQ==" height="750px" width="100%"></iframe>

<h2 id="heading-effects-run-when-dependencies-change">Effects run when dependencies change</h2>
<p>If you want to be more selective about when your effect runs, you need to use the dependency array argument. The way it works is that the values in the dependency array are compared <em>every</em> render. If a value at a given position changes, the effect is executed again.</p>
<p>Let's take a look at an example:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// First render</span>
useEffect(<span class="hljs-function">() =&gt;</span> {}, [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>]);

<span class="hljs-comment">// Second render</span>
useEffect(<span class="hljs-function">() =&gt;</span> {}, [<span class="hljs-number">1</span>, <span class="hljs-number">3</span>]);
</code></pre>
<ol>
<li><p>Dependency array on the first render: <code>[1, 2]</code></p>
</li>
<li><p>Dependency array on the second render: <code>[1, 3]</code></p>
</li>
</ol>
<p>The first element (<code>1</code>) is the same between renders, but the second element (<code>2</code> and <code>3</code>) is different. The effect is re-executed because the value changed for the second element in the dependency array.</p>
<p>In the example below, we have a <code>count</code> variable managed by React state. Every time you click the Count button, the value is incremented, and the component re-renders.</p>
<p>Since the <code>count</code> variable passed to the <code>useEffect</code> dependency array changes between renders, the log statement will run.</p>
<p>Now what if we click the <strong>Force Rerender</strong> button instead?</p>
<p>This button <em>doesn't</em> update the <code>count</code> variable. Even though the component re-renders, the value passed to the <code>useEffect</code> dependency array is the same. In this case, the log statement <em>won't</em> run.</p>
<p>Try it out!</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3N4bzZra3B0dTdoNjd1em05YTNjZWZyYyIsIm5hbWUiOiJSZWFjdCB1c2VFZmZlY3QgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG59XG5cbmRpdiB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGp1c3RpZnktY29udGVudDogc3BhY2UtYXJvdW5kO1xufVxuXG5idXR0b24ge1xuICAtLWI6IDNweDsgLyogYm9yZGVyIHRoaWNrbmVzcyAqL1xuICAtLXM6IDAuNDVlbTsgLyogc2l6ZSBvZiB0aGUgY29ybmVyICovXG4gIC0tY29sb3I6ICMzNzNiNDQ7XG5cbiAgY3Vyc29yOiBwb2ludGVyO1xuICBwYWRkaW5nOiBjYWxjKDAuNWVtICsgdmFyKC0tcykpIGNhbGMoMC45ZW0gKyB2YXIoLS1zKSk7XG4gIGNvbG9yOiB2YXIoLS1jb2xvcik7XG4gIC0tX3A6IHZhcigtLXMpO1xuICBiYWNrZ3JvdW5kOiBjb25pYy1ncmFkaWVudChcbiAgICAgIGZyb20gOTBkZWcgYXQgdmFyKC0tYikgdmFyKC0tYiksXG4gICAgICAjMDAwMCA5MGRlZyxcbiAgICAgIHZhcigtLWNvbG9yKSAwXG4gICAgKVxuICAgIHZhcigtLV9wKSB2YXIoLS1fcCkgLyBjYWxjKDEwMCUgLSB2YXIoLS1iKSAtIDIgKiB2YXIoLS1fcCkpXG4gICAgY2FsYygxMDAlIC0gdmFyKC0tYikgLSAyICogdmFyKC0tX3ApKTtcbiAgdHJhbnNpdGlvbjpcbiAgICAwLjNzIGxpbmVhcixcbiAgICBjb2xvciAwcyxcbiAgICBiYWNrZ3JvdW5kLWNvbG9yIDBzO1xuICBvdXRsaW5lOiB2YXIoLS1iKSBzb2xpZCAjMDAwMDtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuNmVtO1xuICBmb250LXNpemU6IDE2cHg7XG5cbiAgYm9yZGVyOiAwO1xuXG4gIHVzZXItc2VsZWN0OiBub25lO1xuICAtd2Via2l0LXVzZXItc2VsZWN0OiBub25lO1xuICB0b3VjaC1hY3Rpb246IG1hbmlwdWxhdGlvbjtcbn1cblxuYnV0dG9uOmhvdmVyLFxuYnV0dG9uOmZvY3VzLXZpc2libGUge1xuICAtLV9wOiAwcHg7XG4gIG91dGxpbmUtY29sb3I6IHZhcigtLWNvbG9yKTtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuMDVlbTtcbn1cblxuYnV0dG9uOmFjdGl2ZSB7XG4gIGJhY2tncm91bmQ6IHZhcigtLWNvbG9yKTtcbiAgY29sb3I6ICNmZmY7XG59XG4ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VTdGF0ZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgdXNlUmVyZW5kZXIgfSBmcm9tIFwiLi91dGlsc1wiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIGNvbnN0IHJlcmVuZGVyID0gdXNlUmVyZW5kZXIoKTtcbiAgY29uc3QgW2NvdW50LCBzZXRDb3VudF0gPSB1c2VTdGF0ZSgwKTtcblxuICB1c2VFZmZlY3QoKCkgPT4ge1xuICAgIGNvbnNvbGUubG9nKGBDb3VudDogJHtjb3VudH1gKTtcbiAgfSwgW2NvdW50XSk7XG5cbiAgcmV0dXJuIChcbiAgICA8PlxuICAgICAgPGJ1dHRvbiBvbkNsaWNrPXtyZXJlbmRlcn0+Rm9yY2UgUmVyZW5kZXI8L2J1dHRvbj5cbiAgICAgIDxidXR0b24gb25DbGljaz17KCkgPT4gc2V0Q291bnQoY291bnQgKyAxKX0+Q291bnQge2NvdW50fTwvYnV0dG9uPlxuICAgIDwvPlxuICApO1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKDxBcHAgLz4pO1xuIn0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifSwiL3V0aWxzLmpzIjp7ImNvZGUiOiJpbXBvcnQgeyB1c2VTdGF0ZSB9IGZyb20gXCJyZWFjdFwiO1xuXG5leHBvcnQgY29uc3QgdXNlUmVyZW5kZXIgPSAoKSA9PiB7XG4gIGNvbnN0IFtzdGF0ZSwgc2V0U3RhdGVdID0gdXNlU3RhdGUoMCk7XG4gIHJldHVybiAoKSA9PiBzZXRTdGF0ZShzdGF0ZSArIDEpO1xufTtcbiJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX0sIkNvbnNvbGUiOnsidHlwZSI6ImNvbnNvbGUiLCJkYXRhIjp7InNob3dIZWFkZXIiOmZhbHNlLCJzaG93U3ludGF4RXJyb3IiOmZhbHNlLCJzaG93U2V0dXBQcm9ncmVzcyI6ZmFsc2UsIm1heE1lc3NhZ2VDb3VudCI6MTAsInJlc2V0T25QcmV2aWV3UmVzdGFydCI6ZmFsc2UsInN0YW5kYWxvbmUiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yIEVkaXRvcidcbidQcmV2aWV3IENvbnNvbGUnIn19fQ==" height="750px" width="100%"></iframe>

<h3 id="heading-non-primitive-data-types-are-compared-by-reference">Non-primitive data types are compared by reference</h3>
<p>One thing to keep in mind is that any non-primitive data type passed to a dependency array is compared by its reference.</p>
<p>This means that even if a value <em>looks</em> the same, if the underlying reference changes, React will execute the effect.</p>
<p>For example, the two functions below may <em>look</em> the same, but from JavaScript's perspective, they have two different function references:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fnOne = <span class="hljs-function">() =&gt;</span> <span class="hljs-literal">true</span>;
<span class="hljs-keyword">const</span> fnTwo = <span class="hljs-function">() =&gt;</span> <span class="hljs-literal">true</span>;
fnOne === fnTwo <span class="hljs-comment">// false (different references)</span>
</code></pre>
<p>Here are a few data types where this applies:</p>
<ul>
<li><p>Functions</p>
</li>
<li><p>Objects</p>
</li>
<li><p>Arrays</p>
</li>
</ul>
<p>We can see this play out in the example below.</p>
<p>Since the <code>logger</code> function is recreated <em>every</em> render, the underlying reference will be different.</p>
<p>This time, since we pass the <code>logger</code> in the dependency array, the <strong>Force Rerender</strong> button <em>will</em> cause the log statement to run again:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3N4bzZra3B0dTdoNjd1em05YTNjZWZyYyIsIm5hbWUiOiJSZWFjdCB1c2VFZmZlY3QgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG59XG5cbmRpdiB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGp1c3RpZnktY29udGVudDogc3BhY2UtYXJvdW5kO1xufVxuXG5idXR0b24ge1xuICAtLWI6IDNweDsgLyogYm9yZGVyIHRoaWNrbmVzcyAqL1xuICAtLXM6IDAuNDVlbTsgLyogc2l6ZSBvZiB0aGUgY29ybmVyICovXG4gIC0tY29sb3I6ICMzNzNiNDQ7XG5cbiAgY3Vyc29yOiBwb2ludGVyO1xuICBwYWRkaW5nOiBjYWxjKDAuNWVtICsgdmFyKC0tcykpIGNhbGMoMC45ZW0gKyB2YXIoLS1zKSk7XG4gIGNvbG9yOiB2YXIoLS1jb2xvcik7XG4gIC0tX3A6IHZhcigtLXMpO1xuICBiYWNrZ3JvdW5kOiBjb25pYy1ncmFkaWVudChcbiAgICAgIGZyb20gOTBkZWcgYXQgdmFyKC0tYikgdmFyKC0tYiksXG4gICAgICAjMDAwMCA5MGRlZyxcbiAgICAgIHZhcigtLWNvbG9yKSAwXG4gICAgKVxuICAgIHZhcigtLV9wKSB2YXIoLS1fcCkgLyBjYWxjKDEwMCUgLSB2YXIoLS1iKSAtIDIgKiB2YXIoLS1fcCkpXG4gICAgY2FsYygxMDAlIC0gdmFyKC0tYikgLSAyICogdmFyKC0tX3ApKTtcbiAgdHJhbnNpdGlvbjpcbiAgICAwLjNzIGxpbmVhcixcbiAgICBjb2xvciAwcyxcbiAgICBiYWNrZ3JvdW5kLWNvbG9yIDBzO1xuICBvdXRsaW5lOiB2YXIoLS1iKSBzb2xpZCAjMDAwMDtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuNmVtO1xuICBmb250LXNpemU6IDE2cHg7XG5cbiAgYm9yZGVyOiAwO1xuXG4gIHVzZXItc2VsZWN0OiBub25lO1xuICAtd2Via2l0LXVzZXItc2VsZWN0OiBub25lO1xuICB0b3VjaC1hY3Rpb246IG1hbmlwdWxhdGlvbjtcbn1cblxuYnV0dG9uOmhvdmVyLFxuYnV0dG9uOmZvY3VzLXZpc2libGUge1xuICAtLV9wOiAwcHg7XG4gIG91dGxpbmUtY29sb3I6IHZhcigtLWNvbG9yKTtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuMDVlbTtcbn1cblxuYnV0dG9uOmFjdGl2ZSB7XG4gIGJhY2tncm91bmQ6IHZhcigtLWNvbG9yKTtcbiAgY29sb3I6ICNmZmY7XG59XG4ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VTdGF0ZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgdXNlUmVyZW5kZXIgfSBmcm9tIFwiLi91dGlsc1wiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIGNvbnN0IHJlcmVuZGVyID0gdXNlUmVyZW5kZXIoKTtcbiAgY29uc3QgW2NvdW50LCBzZXRDb3VudF0gPSB1c2VTdGF0ZSgwKTtcblxuICBjb25zdCBsb2dnZXIgPSAoKSA9PiBjb25zb2xlLmxvZyhgQ291bnQ6ICR7Y291bnR9YCk7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBsb2dnZXIoKTtcbiAgfSwgW2NvdW50LCBsb2dnZXJdKTtcblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8YnV0dG9uIG9uQ2xpY2s9e3JlcmVuZGVyfT5Gb3JjZSBSZXJlbmRlcjwvYnV0dG9uPlxuICAgICAgPGJ1dHRvbiBvbkNsaWNrPXsoKSA9PiBzZXRDb3VudChjb3VudCArIDEpfT5Db3VudCB7Y291bnR9PC9idXR0b24+XG4gICAgPC8+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QgZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoPEFwcCAvPik7XG4ifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9LCIvdXRpbHMuanMiOnsiY29kZSI6ImltcG9ydCB7IHVzZVN0YXRlIH0gZnJvbSBcInJlYWN0XCI7XG5cbmV4cG9ydCBjb25zdCB1c2VSZXJlbmRlciA9ICgpID0+IHtcbiAgY29uc3QgW3N0YXRlLCBzZXRTdGF0ZV0gPSB1c2VTdGF0ZSgwKTtcbiAgcmV0dXJuICgpID0+IHNldFN0YXRlKHN0YXRlICsgMSk7XG59O1xuIn19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOnRydWUsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOnRydWUsIndyYXBDb250ZW50Ijp0cnVlLCJjbG9zYWJsZVRhYnMiOnRydWUsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOnRydWUsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2V9fSwiQ29uc29sZSI6eyJ0eXBlIjoiY29uc29sZSIsImRhdGEiOnsic2hvd0hlYWRlciI6ZmFsc2UsInNob3dTeW50YXhFcnJvciI6ZmFsc2UsInNob3dTZXR1cFByb2dyZXNzIjpmYWxzZSwibWF4TWVzc2FnZUNvdW50IjoxMCwicmVzZXRPblByZXZpZXdSZXN0YXJ0IjpmYWxzZSwic3RhbmRhbG9uZSI6ZmFsc2V9fX0sImdlbmVyYWwiOnsidmlzaWJsZUZpbGVzIjpbIi9BcHAuanMiXSwidGhlbWUiOiJrZW1wb0RhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3IgRWRpdG9yJ1xuJ1ByZXZpZXcgQ29uc29sZScifX19" height="750px" width="100%"></iframe>

<h3 id="heading-create-stable-references-to-prevent-effects-from-running">Create stable references to prevent effects from running</h3>
<p>We can pass stable references to our dependency arrays to solve this problem.</p>
<p>For functions that are created in a React component, this usually means wrapping it with the <code>useCallback</code> hook.</p>
<p>The <code>useCallback</code> hook takes a function as its first argument and a dependency array as the second argument. On the initial render, a new function is created. However, every other render will return the <strong>same</strong> function if the elements in the dependency array are the same.</p>
<p>The <code>useCallback</code> dependency array follows the same rules as the <code>useEffect</code> dependency array.</p>
<p>This time the <code>logger</code> function is wrapped with the <code>useCallback</code> hook, so we get a stable reference between renders and the <strong>Force Rerender</strong> button <em>won't</em> cause the log statement to run again:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3N4bzZra3B0dTdoNjd1em05YTNjZWZyYyIsIm5hbWUiOiJSZWFjdCB1c2VFZmZlY3QgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG59XG5cbmRpdiB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGp1c3RpZnktY29udGVudDogc3BhY2UtYXJvdW5kO1xufVxuXG5idXR0b24ge1xuICAtLWI6IDNweDsgLyogYm9yZGVyIHRoaWNrbmVzcyAqL1xuICAtLXM6IDAuNDVlbTsgLyogc2l6ZSBvZiB0aGUgY29ybmVyICovXG4gIC0tY29sb3I6ICMzNzNiNDQ7XG5cbiAgY3Vyc29yOiBwb2ludGVyO1xuICBwYWRkaW5nOiBjYWxjKDAuNWVtICsgdmFyKC0tcykpIGNhbGMoMC45ZW0gKyB2YXIoLS1zKSk7XG4gIGNvbG9yOiB2YXIoLS1jb2xvcik7XG4gIC0tX3A6IHZhcigtLXMpO1xuICBiYWNrZ3JvdW5kOiBjb25pYy1ncmFkaWVudChcbiAgICAgIGZyb20gOTBkZWcgYXQgdmFyKC0tYikgdmFyKC0tYiksXG4gICAgICAjMDAwMCA5MGRlZyxcbiAgICAgIHZhcigtLWNvbG9yKSAwXG4gICAgKVxuICAgIHZhcigtLV9wKSB2YXIoLS1fcCkgLyBjYWxjKDEwMCUgLSB2YXIoLS1iKSAtIDIgKiB2YXIoLS1fcCkpXG4gICAgY2FsYygxMDAlIC0gdmFyKC0tYikgLSAyICogdmFyKC0tX3ApKTtcbiAgdHJhbnNpdGlvbjpcbiAgICAwLjNzIGxpbmVhcixcbiAgICBjb2xvciAwcyxcbiAgICBiYWNrZ3JvdW5kLWNvbG9yIDBzO1xuICBvdXRsaW5lOiB2YXIoLS1iKSBzb2xpZCAjMDAwMDtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuNmVtO1xuICBmb250LXNpemU6IDE2cHg7XG5cbiAgYm9yZGVyOiAwO1xuXG4gIHVzZXItc2VsZWN0OiBub25lO1xuICAtd2Via2l0LXVzZXItc2VsZWN0OiBub25lO1xuICB0b3VjaC1hY3Rpb246IG1hbmlwdWxhdGlvbjtcbn1cblxuYnV0dG9uOmhvdmVyLFxuYnV0dG9uOmZvY3VzLXZpc2libGUge1xuICAtLV9wOiAwcHg7XG4gIG91dGxpbmUtY29sb3I6IHZhcigtLWNvbG9yKTtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuMDVlbTtcbn1cblxuYnV0dG9uOmFjdGl2ZSB7XG4gIGJhY2tncm91bmQ6IHZhcigtLWNvbG9yKTtcbiAgY29sb3I6ICNmZmY7XG59XG4ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlQ2FsbGJhY2ssIHVzZUVmZmVjdCwgdXNlU3RhdGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IHVzZVJlcmVuZGVyIH0gZnJvbSBcIi4vdXRpbHNcIjtcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICBjb25zdCByZXJlbmRlciA9IHVzZVJlcmVuZGVyKCk7XG4gIGNvbnN0IFtjb3VudCwgc2V0Q291bnRdID0gdXNlU3RhdGUoMCk7XG5cbiAgY29uc3QgbG9nZ2VyID0gdXNlQ2FsbGJhY2soKCkgPT4ge1xuICAgIGNvbnNvbGUubG9nKGBDb3VudDogJHtjb3VudH1gKTtcbiAgfSwgW2NvdW50XSk7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBsb2dnZXIoKTtcbiAgfSwgW2NvdW50LCBsb2dnZXJdKTtcblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8YnV0dG9uIG9uQ2xpY2s9e3JlcmVuZGVyfT5Gb3JjZSBSZXJlbmRlcjwvYnV0dG9uPlxuICAgICAgPGJ1dHRvbiBvbkNsaWNrPXsoKSA9PiBzZXRDb3VudChjb3VudCArIDEpfT5Db3VudCB7Y291bnR9PC9idXR0b24+XG4gICAgPC8+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QgZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoPEFwcCAvPik7XG4ifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9LCIvdXRpbHMuanMiOnsiY29kZSI6ImltcG9ydCB7IHVzZVN0YXRlIH0gZnJvbSBcInJlYWN0XCI7XG5cbmV4cG9ydCBjb25zdCB1c2VSZXJlbmRlciA9ICgpID0+IHtcbiAgY29uc3QgW3N0YXRlLCBzZXRTdGF0ZV0gPSB1c2VTdGF0ZSgwKTtcbiAgcmV0dXJuICgpID0+IHNldFN0YXRlKHN0YXRlICsgMSk7XG59O1xuIn19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOnRydWUsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOnRydWUsIndyYXBDb250ZW50Ijp0cnVlLCJjbG9zYWJsZVRhYnMiOnRydWUsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOnRydWUsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2V9fSwiQ29uc29sZSI6eyJ0eXBlIjoiY29uc29sZSIsImRhdGEiOnsic2hvd0hlYWRlciI6ZmFsc2UsInNob3dTeW50YXhFcnJvciI6ZmFsc2UsInNob3dTZXR1cFByb2dyZXNzIjpmYWxzZSwibWF4TWVzc2FnZUNvdW50IjoxMCwicmVzZXRPblByZXZpZXdSZXN0YXJ0IjpmYWxzZSwic3RhbmRhbG9uZSI6ZmFsc2V9fX0sImdlbmVyYWwiOnsidmlzaWJsZUZpbGVzIjpbIi9BcHAuanMiXSwidGhlbWUiOiJrZW1wb0RhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3IgRWRpdG9yJ1xuJ1ByZXZpZXcgQ29uc29sZScifX19" height="750px" width="100%"></iframe>

<h2 id="heading-effects-can-have-cleanup-functions">Effects can have cleanup functions</h2>
<p>Another important part about effects is that they have optional <em>cleanup</em> functions. If you return a function in an effect, React will execute it before executing the next effect. In other words, it'll look something like this:</p>
<ol>
<li><p>The effect runs on the initial render</p>
</li>
<li><p>A value in the dependency array changes</p>
</li>
<li><p>The cleanup function from the first effect runs</p>
</li>
<li><p>The <em>next</em> effect runs and returns a new cleanup function</p>
</li>
</ol>
<p>In the example below, you'll notice that a <code>Clean up Count</code> log statement runs before the next <code>Count</code> log statement every time you click the Count button.</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3N4bzZra3B0dTdoNjd1em05YTNjZWZyYyIsIm5hbWUiOiJSZWFjdCB1c2VFZmZlY3QgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG59XG5cbmRpdiB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGp1c3RpZnktY29udGVudDogc3BhY2UtYXJvdW5kO1xufVxuXG5idXR0b24ge1xuICAtLWI6IDNweDsgLyogYm9yZGVyIHRoaWNrbmVzcyAqL1xuICAtLXM6IDAuNDVlbTsgLyogc2l6ZSBvZiB0aGUgY29ybmVyICovXG4gIC0tY29sb3I6ICMzNzNiNDQ7XG5cbiAgY3Vyc29yOiBwb2ludGVyO1xuICBwYWRkaW5nOiBjYWxjKDAuNWVtICsgdmFyKC0tcykpIGNhbGMoMC45ZW0gKyB2YXIoLS1zKSk7XG4gIGNvbG9yOiB2YXIoLS1jb2xvcik7XG4gIC0tX3A6IHZhcigtLXMpO1xuICBiYWNrZ3JvdW5kOiBjb25pYy1ncmFkaWVudChcbiAgICAgIGZyb20gOTBkZWcgYXQgdmFyKC0tYikgdmFyKC0tYiksXG4gICAgICAjMDAwMCA5MGRlZyxcbiAgICAgIHZhcigtLWNvbG9yKSAwXG4gICAgKVxuICAgIHZhcigtLV9wKSB2YXIoLS1fcCkgLyBjYWxjKDEwMCUgLSB2YXIoLS1iKSAtIDIgKiB2YXIoLS1fcCkpXG4gICAgY2FsYygxMDAlIC0gdmFyKC0tYikgLSAyICogdmFyKC0tX3ApKTtcbiAgdHJhbnNpdGlvbjpcbiAgICAwLjNzIGxpbmVhcixcbiAgICBjb2xvciAwcyxcbiAgICBiYWNrZ3JvdW5kLWNvbG9yIDBzO1xuICBvdXRsaW5lOiB2YXIoLS1iKSBzb2xpZCAjMDAwMDtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuNmVtO1xuICBmb250LXNpemU6IDE2cHg7XG5cbiAgYm9yZGVyOiAwO1xuXG4gIHVzZXItc2VsZWN0OiBub25lO1xuICAtd2Via2l0LXVzZXItc2VsZWN0OiBub25lO1xuICB0b3VjaC1hY3Rpb246IG1hbmlwdWxhdGlvbjtcbn1cblxuYnV0dG9uOmhvdmVyLFxuYnV0dG9uOmZvY3VzLXZpc2libGUge1xuICAtLV9wOiAwcHg7XG4gIG91dGxpbmUtY29sb3I6IHZhcigtLWNvbG9yKTtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuMDVlbTtcbn1cblxuYnV0dG9uOmFjdGl2ZSB7XG4gIGJhY2tncm91bmQ6IHZhcigtLWNvbG9yKTtcbiAgY29sb3I6ICNmZmY7XG59XG4ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlRWZmZWN0LCB1c2VTdGF0ZSB9IGZyb20gXCJyZWFjdFwiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIGNvbnN0IFtjb3VudCwgc2V0Q291bnRdID0gdXNlU3RhdGUoMCk7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBjb25zb2xlLmxvZyhgQ291bnQ6ICR7Y291bnR9YCk7XG4gICAgcmV0dXJuICgpID0+IHtcbiAgICAgIGNvbnNvbGUubG9nKGBDbGVhbiB1cCBDb3VudDogJHtjb3VudH1gKTtcbiAgICB9O1xuICB9LCBbY291bnRdKTtcblxuICByZXR1cm4gPGJ1dHRvbiBvbkNsaWNrPXsoKSA9PiBzZXRDb3VudChjb3VudCArIDEpfT5Db3VudCB7Y291bnR9PC9idXR0b24+O1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKDxBcHAgLz4pO1xuIn0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifSwiL3V0aWxzLmpzIjp7ImNvZGUiOiJpbXBvcnQgeyB1c2VTdGF0ZSB9IGZyb20gXCJyZWFjdFwiO1xuXG5leHBvcnQgY29uc3QgdXNlUmVyZW5kZXIgPSAoKSA9PiB7XG4gIGNvbnN0IFtzdGF0ZSwgc2V0U3RhdGVdID0gdXNlU3RhdGUoMCk7XG4gIHJldHVybiAoKSA9PiBzZXRTdGF0ZShzdGF0ZSArIDEpO1xufTtcbiJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX0sIkNvbnNvbGUiOnsidHlwZSI6ImNvbnNvbGUiLCJkYXRhIjp7InNob3dIZWFkZXIiOmZhbHNlLCJzaG93U3ludGF4RXJyb3IiOmZhbHNlLCJzaG93U2V0dXBQcm9ncmVzcyI6ZmFsc2UsIm1heE1lc3NhZ2VDb3VudCI6MTAsInJlc2V0T25QcmV2aWV3UmVzdGFydCI6ZmFsc2UsInN0YW5kYWxvbmUiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yIEVkaXRvcidcbidQcmV2aWV3IENvbnNvbGUnIn19fQ==" height="750px" width="100%"></iframe>

<p>And that's it!</p>
<p>A quick look at when React's <code>useEffect</code> hook runs.</p>
<p>Understanding when the <code>useEffect</code> hook runs will help you manage side effects predictably and efficiently, ensuring that your React components behave as expected!</p>
]]></content:encoded></item><item><title><![CDATA[Everything You Need to Know About The Sandpack Preview Component]]></title><description><![CDATA[Sandpack is a lightweight code playground built by the CodeSandbox team.
With Sandpack, you can compose built-in and custom React components to create unique experiences for your end users.
In this post, we're going to focus on the built-in SandpackP...]]></description><link>https://blog.thesshguy.com/sandpack-preview-component</link><guid isPermaLink="true">https://blog.thesshguy.com/sandpack-preview-component</guid><category><![CDATA[Frontend Development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Mon, 10 Jun 2024 14:13:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1717955319521/5e15c577-64e2-4ee7-83bf-dc25a9dd3e38.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p><a target="_blank" href="https://blog.thesshguy.com/sandpack">Sandpack is a lightweight code playground</a> built by the CodeSandbox team.</p>
<p>With Sandpack, you can compose built-in and custom React components to create unique experiences for your end users.</p>
<p>In this post, we're going to focus on the built-in <code>SandpackPreview</code> component.</p>
<h2 id="heading-the-sandpack-preview-component">The Sandpack Preview Component</h2>
<p>The Preview component is your window into Sandpack's bundling process. It shows you how your UI looks. Not only that, but it also supports live updates when you make changes in an Editor component:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2dmMTdueHNoN3drZXRzdndyd3E2MXVnciIsIm5hbWUiOiJTYW5kcGFjayBDb21wb25lbnQgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG59XG5cbi5idG4td3JhcHBlciB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGZsZXgtZGlyZWN0aW9uOiByb3c7XG4gIHdpZHRoOiAxMDAlO1xuICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWFyb3VuZDtcbiAgcGFkZGluZzogOHB4O1xufVxuIn0sIi9BcHAuanMiOnsiY29kZSI6ImltcG9ydCB7XG4gIFNhbmRwYWNrUHJvdmlkZXIsXG4gIFNhbmRwYWNrQ29kZUVkaXRvcixcbiAgU2FuZHBhY2tMYXlvdXQsXG4gIFNhbmRwYWNrUHJldmlldyxcbn0gZnJvbSBcIkBjb2Rlc2FuZGJveC9zYW5kcGFjay1yZWFjdFwiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiAoXG4gICAgPFNhbmRwYWNrUHJvdmlkZXIgdGVtcGxhdGU9XCJyZWFjdFwiPlxuICAgICAgPFNhbmRwYWNrTGF5b3V0PlxuICAgICAgICA8U2FuZHBhY2tDb2RlRWRpdG9yIC8+XG4gICAgICAgIDxTYW5kcGFja1ByZXZpZXcgLz5cbiAgICAgIDwvU2FuZHBhY2tMYXlvdXQ+XG4gICAgPC9TYW5kcGFja1Byb3ZpZGVyPlxuICApO1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IFwiLi9zdHlsZXMuY3NzXCI7XG5cbmltcG9ydCBBcHAgZnJvbSBcIi4vQXBwXCI7XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+XG4pOyJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjUuMC4wXCIsXG4gICAgXCJAY29kZXNhbmRib3gvc2FuZHBhY2stcmVhY3RcIjogXCIyLjE0LjBcIixcbiAgICBcIkBjb2Rlc2FuZGJveC9zYW5kcGFjay10aGVtZXNcIjogXCIyLjAuMjFcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifX0sInRlbXBsYXRlIjoicmVhY3QifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6dHJ1ZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6dHJ1ZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6dHJ1ZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0=" height="750px" width="100%"></iframe>

<h3 id="heading-how-to-support-browser-navigation">How to support browser navigation.</h3>
<p>Just like a regular browser, the Preview component has a search bar. You can enable it with the <code>showNavigator</code> prop. You don't <em>need</em> to use the search bar, but it can be useful when building a demo that involves routing:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2dmMTdueHNoN3drZXRzdndyd3E2MXVnciIsIm5hbWUiOiJTYW5kcGFjayBDb21wb25lbnQgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG59XG5cbi5idG4td3JhcHBlciB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGZsZXgtZGlyZWN0aW9uOiByb3c7XG4gIHdpZHRoOiAxMDAlO1xuICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWFyb3VuZDtcbiAgcGFkZGluZzogOHB4O1xufVxuIn0sIi9BcHAuanMiOnsiY29kZSI6ImltcG9ydCB7XG4gIFNhbmRwYWNrUHJvdmlkZXIsXG4gIFNhbmRwYWNrQ29kZUVkaXRvcixcbiAgU2FuZHBhY2tMYXlvdXQsXG4gIFNhbmRwYWNrUHJldmlldyxcbn0gZnJvbSBcIkBjb2Rlc2FuZGJveC9zYW5kcGFjay1yZWFjdFwiO1xuaW1wb3J0IHsgZmlsZXMgfSBmcm9tIFwiLi91dGlsc1wiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiAoXG4gICAgPFNhbmRwYWNrUHJvdmlkZXIgdGVtcGxhdGU9XCJyZWFjdFwiIGZpbGVzPXtmaWxlc30+XG4gICAgICA8U2FuZHBhY2tMYXlvdXQ+XG4gICAgICAgIDxTYW5kcGFja0NvZGVFZGl0b3IgLz5cbiAgICAgICAgPFNhbmRwYWNrUHJldmlldyBzaG93TmF2aWdhdG9yIC8+XG4gICAgICA8L1NhbmRwYWNrTGF5b3V0PlxuICAgIDwvU2FuZHBhY2tQcm92aWRlcj5cbiAgKTtcbn1cbiJ9LCIvaW5kZXguanMiOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiLFxuICAgIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXJlYWN0XCI6IFwiMi4xNC4wXCIsXG4gICAgXCJAY29kZXNhbmRib3gvc2FuZHBhY2stdGhlbWVzXCI6IFwiMi4wLjIxXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In0sIi91dGlscy50cyI6eyJjb2RlIjoiZXhwb3J0IGNvbnN0IGZpbGVzID0ge1xuICBcIi9wYWNrYWdlLmpzb25cIjogYFxue1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIixcbiAgICBcInJlYWN0LXJvdXRlci1kb21cIjogXCI2LjIzLjFcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn1cbiAgYCxcbiAgXCIvQXBwLmpzXCI6IGBcbmltcG9ydCB7IGNyZWF0ZUJyb3dzZXJSb3V0ZXIsIFJvdXRlclByb3ZpZGVyIH0gZnJvbSAncmVhY3Qtcm91dGVyLWRvbSdcblxuY29uc3Qgcm91dGVyID0gY3JlYXRlQnJvd3NlclJvdXRlcihbXG4gIHsgcGF0aDogXCIvXCIsIGVsZW1lbnQ6IDxkaXY+SGVsbG8gd29ybGQhPC9kaXY+IH0sXG4gIHsgcGF0aDogXCIvc2VjcmV0XCIsIGVsZW1lbnQ6IDxkaXY+SGVsbG8gU2VjcmV0ITwvZGl2PiB9LFxuXSlcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICByZXR1cm4gPFJvdXRlclByb3ZpZGVyIHJvdXRlcj17cm91dGVyfSAvPlxufVxuICBgLnRyaW0oKSxcbn07XG4ifX0sInRlbXBsYXRlIjoicmVhY3QifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6dHJ1ZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6dHJ1ZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6dHJ1ZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0=" height="750px" width="100%"></iframe>

<p>In the example above, we're using React Router. We've defined a root route at <code>/</code> and another route at <code>/secret</code>. By default, the app is on the <code>/</code> route which shows a page with the text "<strong>Hello world!</strong>".</p>
<p>Try using the search bar to navigate to the <code>/secret</code> route. The page should update and show the text "<strong>Hello Secret!</strong>" now!</p>
<p>Great! But what if we want the app to <em>start</em> on the <code>/secret</code> route instead of the <code>/</code> route?</p>
<p>Well, that's easy too!</p>
<p>You can use the <code>startRoute</code> prop like below:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2dmMTdueHNoN3drZXRzdndyd3E2MXVnciIsIm5hbWUiOiJTYW5kcGFjayBDb21wb25lbnQgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG59XG5cbi5idG4td3JhcHBlciB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGZsZXgtZGlyZWN0aW9uOiByb3c7XG4gIHdpZHRoOiAxMDAlO1xuICBqdXN0aWZ5LWNvbnRlbnQ6IHNwYWNlLWFyb3VuZDtcbiAgcGFkZGluZzogOHB4O1xufVxuIn0sIi9BcHAuanMiOnsiY29kZSI6ImltcG9ydCB7XG4gIFNhbmRwYWNrUHJvdmlkZXIsXG4gIFNhbmRwYWNrQ29kZUVkaXRvcixcbiAgU2FuZHBhY2tMYXlvdXQsXG4gIFNhbmRwYWNrUHJldmlldyxcbn0gZnJvbSBcIkBjb2Rlc2FuZGJveC9zYW5kcGFjay1yZWFjdFwiO1xuaW1wb3J0IHsgZmlsZXMgfSBmcm9tIFwiLi91dGlsc1wiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiAoXG4gICAgPFNhbmRwYWNrUHJvdmlkZXIgdGVtcGxhdGU9XCJyZWFjdFwiIGZpbGVzPXtmaWxlc30+XG4gICAgICA8U2FuZHBhY2tMYXlvdXQ+XG4gICAgICAgIDxTYW5kcGFja0NvZGVFZGl0b3IgLz5cbiAgICAgICAgPFNhbmRwYWNrUHJldmlldyBzaG93TmF2aWdhdG9yIHN0YXJ0Um91dGU9XCIvc2VjcmV0XCIgLz5cbiAgICAgIDwvU2FuZHBhY2tMYXlvdXQ+XG4gICAgPC9TYW5kcGFja1Byb3ZpZGVyPlxuICApO1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IFwiLi9zdHlsZXMuY3NzXCI7XG5cbmltcG9ydCBBcHAgZnJvbSBcIi4vQXBwXCI7XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+XG4pOyJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjUuMC4wXCIsXG4gICAgXCJAY29kZXNhbmRib3gvc2FuZHBhY2stcmVhY3RcIjogXCIyLjE0LjBcIixcbiAgICBcIkBjb2Rlc2FuZGJveC9zYW5kcGFjay10aGVtZXNcIjogXCIyLjAuMjFcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifSwiL3V0aWxzLnRzIjp7ImNvZGUiOiJleHBvcnQgY29uc3QgZmlsZXMgPSB7XG4gIFwiL3BhY2thZ2UuanNvblwiOiBgXG57XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiLFxuICAgIFwicmVhY3Qtcm91dGVyLWRvbVwiOiBcIjYuMjMuMVwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufVxuICBgLFxuICBcIi9BcHAuanNcIjogYFxuaW1wb3J0IHsgY3JlYXRlQnJvd3NlclJvdXRlciwgUm91dGVyUHJvdmlkZXIgfSBmcm9tICdyZWFjdC1yb3V0ZXItZG9tJ1xuXG5jb25zdCByb3V0ZXIgPSBjcmVhdGVCcm93c2VyUm91dGVyKFtcbiAgeyBwYXRoOiBcIi9cIiwgZWxlbWVudDogPGRpdj5IZWxsbyB3b3JsZCE8L2Rpdj4gfSxcbiAgeyBwYXRoOiBcIi9zZWNyZXRcIiwgZWxlbWVudDogPGRpdj5IZWxsbyBTZWNyZXQhPC9kaXY+IH0sXG5dKVxuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiA8Um91dGVyUHJvdmlkZXIgcm91dGVyPXtyb3V0ZXJ9IC8+XG59XG4gIGAudHJpbSgpLFxufTtcbiJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="750px" width="100%"></iframe>

<h3 id="heading-you-can-add-custom-actions-to-your-preview-component">You can add custom actions to your Preview component.</h3>
<p>The Preview component supports custom actions. The idea is that you can use a custom action to provide additional functionality to your playground. Let's say I wanted a way to toggle the search bar in the previous example.</p>
<p>We can combine React state with a basic toggle button to accomplish this:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2dmMTdueHNoN3drZXRzdndyd3E2MXVnciIsIm5hbWUiOiJTYW5kcGFjayBDb21wb25lbnQgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG59XG4ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHtcbiAgU2FuZHBhY2tQcm92aWRlcixcbiAgU2FuZHBhY2tDb2RlRWRpdG9yLFxuICBTYW5kcGFja0xheW91dCxcbiAgU2FuZHBhY2tQcmV2aWV3LFxufSBmcm9tIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXJlYWN0XCI7XG5pbXBvcnQgeyB1c2VTdGF0ZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgZmlsZXMgfSBmcm9tIFwiLi91dGlsc1wiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIGNvbnN0IFtzaG93TmF2aWdhdG9yLCBzZXRTaG93TmF2aWdhdG9yXSA9IHVzZVN0YXRlKHRydWUpO1xuXG4gIHJldHVybiAoXG4gICAgPFNhbmRwYWNrUHJvdmlkZXIgdGVtcGxhdGU9XCJyZWFjdFwiIGZpbGVzPXtmaWxlc30+XG4gICAgICA8U2FuZHBhY2tMYXlvdXQ+XG4gICAgICAgIDxTYW5kcGFja0NvZGVFZGl0b3IgLz5cbiAgICAgICAgPFNhbmRwYWNrUHJldmlld1xuICAgICAgICAgIHNob3dOYXZpZ2F0b3I9e3Nob3dOYXZpZ2F0b3J9XG4gICAgICAgICAgc3RhcnRSb3V0ZT1cIi9zZWNyZXRcIlxuICAgICAgICAgIGFjdGlvbnNDaGlsZHJlbj17XG4gICAgICAgICAgICA8YnV0dG9uIG9uQ2xpY2s9eygpID0+IHNldFNob3dOYXZpZ2F0b3IoIXNob3dOYXZpZ2F0b3IpfT5cbiAgICAgICAgICAgICAgVG9nZ2xlIFNlYXJjaCBCYXJcbiAgICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICAgIH1cbiAgICAgICAgLz5cbiAgICAgIDwvU2FuZHBhY2tMYXlvdXQ+XG4gICAgPC9TYW5kcGFja1Byb3ZpZGVyPlxuICApO1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IFwiLi9zdHlsZXMuY3NzXCI7XG5cbmltcG9ydCBBcHAgZnJvbSBcIi4vQXBwXCI7XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+XG4pOyJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjUuMC4wXCIsXG4gICAgXCJAY29kZXNhbmRib3gvc2FuZHBhY2stcmVhY3RcIjogXCIyLjE0LjBcIixcbiAgICBcIkBjb2Rlc2FuZGJveC9zYW5kcGFjay10aGVtZXNcIjogXCIyLjAuMjFcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifSwiL3V0aWxzLnRzIjp7ImNvZGUiOiJleHBvcnQgY29uc3QgZmlsZXMgPSB7XG4gIFwiL3BhY2thZ2UuanNvblwiOiBgXG57XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiLFxuICAgIFwicmVhY3Qtcm91dGVyLWRvbVwiOiBcIjYuMjMuMVwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufVxuICBgLFxuICBcIi9BcHAuanNcIjogYFxuaW1wb3J0IHsgY3JlYXRlQnJvd3NlclJvdXRlciwgUm91dGVyUHJvdmlkZXIgfSBmcm9tICdyZWFjdC1yb3V0ZXItZG9tJ1xuXG5jb25zdCByb3V0ZXIgPSBjcmVhdGVCcm93c2VyUm91dGVyKFtcbiAgeyBwYXRoOiBcIi9cIiwgZWxlbWVudDogPGRpdj5IZWxsbyB3b3JsZCE8L2Rpdj4gfSxcbiAgeyBwYXRoOiBcIi9zZWNyZXRcIiwgZWxlbWVudDogPGRpdj5IZWxsbyBTZWNyZXQhPC9kaXY+IH0sXG5dKVxuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiA8Um91dGVyUHJvdmlkZXIgcm91dGVyPXtyb3V0ZXJ9IC8+XG59XG4gIGAudHJpbSgpLFxufTtcbiJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="750px" width="100%"></iframe>

<h3 id="heading-you-can-use-refs-to-interact-with-the-sandpack-client">You can use refs to interact with the Sandpack client.</h3>
<p>Let's say I'm building a demo to showcase Tailwind CSS.</p>
<p>I want to include a button to add or remove Tailwind from the page. This will make it easier to compare the UI with and without Tailwind. An easy way to add Tailwind to a Sandpack instance is to <a target="_blank" href="https://sandpack.codesandbox.io/docs/getting-started/usage#static-external-resources">pass the CDN to Sandpack as an external resource</a>.</p>
<p>But how do you update the external resources?</p>
<p>We can't do this with just a custom action. Instead, we can pass a React ref to the Preview component. The Preview component uses React's <a target="_blank" href="https://react.dev/reference/react/useImperativeHandle">useImperativeHandle</a> hook to attach a handler function that returns the underlying Sandpack client to the ref.</p>
<p>We can use the Sandpack client to update the external resources and refresh the Preview:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2dmMTdueHNoN3drZXRzdndyd3E2MXVnciIsIm5hbWUiOiJTYW5kcGFjayBDb21wb25lbnQgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG59XG4ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHtcbiAgU2FuZHBhY2tQcm92aWRlcixcbiAgU2FuZHBhY2tDb2RlRWRpdG9yLFxuICBTYW5kcGFja0xheW91dCxcbiAgU2FuZHBhY2tQcmV2aWV3LFxufSBmcm9tIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXJlYWN0XCI7XG5pbXBvcnQgeyB1c2VTdGF0ZSwgdXNlUmVmIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBmaWxlcyB9IGZyb20gXCIuL3V0aWxzXCI7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpIHtcbiAgY29uc3QgcHJldmlld1JlZiA9IHVzZVJlZihudWxsKTtcblxuICByZXR1cm4gKFxuICAgIDxTYW5kcGFja1Byb3ZpZGVyIHRlbXBsYXRlPVwicmVhY3RcIiBmaWxlcz17ZmlsZXN9PlxuICAgICAgPFNhbmRwYWNrTGF5b3V0PlxuICAgICAgICA8U2FuZHBhY2tDb2RlRWRpdG9yIC8+XG4gICAgICAgIDxTYW5kcGFja1ByZXZpZXdcbiAgICAgICAgICByZWY9e3ByZXZpZXdSZWZ9XG4gICAgICAgICAgYWN0aW9uc0NoaWxkcmVuPXtcbiAgICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgICAgb25DbGljaz17KCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGNsaWVudCA9IHByZXZpZXdSZWYuY3VycmVudD8uZ2V0Q2xpZW50KCk7XG4gICAgICAgICAgICAgICAgaWYgKCFjbGllbnQpIHJldHVybjtcblxuICAgICAgICAgICAgICAgIGNvbnN0IHRhaWx3aW5kQ2RuID0gXCJodHRwczovL2Nkbi50YWlsd2luZGNzcy5jb21cIjtcbiAgICAgICAgICAgICAgICBjb25zdCBleHRlcm5hbFJlc291cmNlcyA9XG4gICAgICAgICAgICAgICAgICBjbGllbnQub3B0aW9ucy5leHRlcm5hbFJlc291cmNlcz8uaW5jbHVkZXModGFpbHdpbmRDZG4pXG4gICAgICAgICAgICAgICAgICAgID8gW11cbiAgICAgICAgICAgICAgICAgICAgOiBbXCJodHRwczovL2Nkbi50YWlsd2luZGNzcy5jb21cIl07XG5cbiAgICAgICAgICAgICAgICBjbGllbnQudXBkYXRlT3B0aW9ucyh7IC4uLmNsaWVudC5vcHRpb25zLCBleHRlcm5hbFJlc291cmNlcyB9KTtcbiAgICAgICAgICAgICAgICBjbGllbnQuZGlzcGF0Y2goeyB0eXBlOiBcInJlZnJlc2hcIiB9KTtcbiAgICAgICAgICAgICAgfX1cbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgVG9nZ2xlIFRhaWx3aW5kXG4gICAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgICB9XG4gICAgICAgIC8+XG4gICAgICA8L1NhbmRwYWNrTGF5b3V0PlxuICAgIDwvU2FuZHBhY2tQcm92aWRlcj5cbiAgKTtcbn1cbiJ9LCIvaW5kZXguanMiOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiLFxuICAgIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXJlYWN0XCI6IFwiMi4xNC4wXCIsXG4gICAgXCJAY29kZXNhbmRib3gvc2FuZHBhY2stdGhlbWVzXCI6IFwiMi4wLjIxXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In0sIi91dGlscy50cyI6eyJjb2RlIjoiZXhwb3J0IGNvbnN0IGZpbGVzID0ge1xuICBcIi9BcHAuanNcIjogYFxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICByZXR1cm4gKFxuICAgIDxkaXZcbiAgICAgIGNsYXNzTmFtZT1cImgtZnVsbCB3LWZ1bGwgdGV4dC1za3ktNjAwIHRleHQtY2VudGVyXCJcbiAgICA+XG4gICAgICBIZWxsbyBXb3JsZCFcbiAgICA8L2Rpdj5cbiAgKTtcbn1cbiAgYC50cmltKCksXG59O1xuIn19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOnRydWUsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOnRydWUsIndyYXBDb250ZW50Ijp0cnVlLCJjbG9zYWJsZVRhYnMiOnRydWUsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOnRydWUsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2V9fX0sImdlbmVyYWwiOnsidmlzaWJsZUZpbGVzIjpbIi9BcHAuanMiXSwidGhlbWUiOiJrZW1wb0RhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nUHJldmlldycifX19" height="750px" width="100%"></iframe>

<p>This pattern is useful because it automatically refreshes the Preview with the new resource.</p>
<p>In fact, this is a pattern I follow in <a target="_blank" href="https://kempo.io">Kempo</a>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1717912693421/6f0d469c-129b-4678-9f34-648e05ffb17e.gif" alt="A video showing how to add external resources in Kempo which is a web applicate to configure Sandpack instances." class="image--center mx-auto" /></p>
<blockquote>
<p>Note: <a target="_blank" href="https://hashnode.com/post/clw6s792g000n0al02sqahcy4">Kempo lets you build embeddable Sandpack instances.</a></p>
</blockquote>
<p>And that's it!</p>
<p>Hopefully, this helps you use Sandpack's Preview component more effectively!</p>
]]></content:encoded></item><item><title><![CDATA[useState vs useRef: How to Choose the Right React Hook]]></title><description><![CDATA[State management is a critical part of most front-end applications. In React, you have a few mechanisms to track state, but two common ones are the useState and useRef hooks.
If you're not familiar with these hooks, it can be confusing to know when t...]]></description><link>https://blog.thesshguy.com/usestate-vs-useref</link><guid isPermaLink="true">https://blog.thesshguy.com/usestate-vs-useref</guid><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[useState]]></category><category><![CDATA[useRef]]></category><category><![CDATA[ReactHooks]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Tue, 04 Jun 2024 18:58:38 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1717527451740/dd1dcc5a-f58f-41b5-a230-3b66acd7567d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>State management is a critical part of most front-end applications. In React, you have a few mechanisms to track state, but two common ones are the <code>useState</code> and <code>useRef</code> hooks.</p>
<p>If you're not familiar with these hooks, it can be confusing to know when to use each one. In this post, we'll compare the <code>useState</code> and <code>useRef</code> hooks and build some basic intuition around when to choose each one.</p>
<h2 id="heading-the-usestate-hook-lets-you-add-state-to-functional-components">The useState hook lets you add state to functional components.</h2>
<p>The <code>useState</code> hook is the basic building block to manage state for functional components. The hook returns a tuple where the first element is the state and the second element is a function to update that state.</p>
<p>The key part of the updater function is that it's designed to trigger a re-render. When a React component re-renders, the <a target="_blank" href="https://blog.thesshguy.com/react-jsx-to-html">JSX is recalculated and the underlying HTML is updated</a> to reflect any state changes.</p>
<p>As you click the button in the example below, you'll notice that:</p>
<ol>
<li><p>The count variable is incremented</p>
</li>
<li><p>The component re-renders</p>
</li>
<li><p>The HTML changes</p>
</li>
</ol>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2Zodnk1eXlqd2pnOHFiYzAzeXdyY2JvbSIsIm5hbWUiOiJ1c2VTdGF0ZSB2cyB1c2VSZWYgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG4gIGhlaWdodDogMTAwdmg7XG59XG5cbmRpdiB7XG4gIGhlaWdodDogMTAwJTtcbiAgd2lkdGg6IDEwMCU7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xufVxuXG5idXR0b24ge1xuICAtLWI6IDNweDsgLyogYm9yZGVyIHRoaWNrbmVzcyAqL1xuICAtLXM6IDAuNDVlbTsgLyogc2l6ZSBvZiB0aGUgY29ybmVyICovXG4gIC0tY29sb3I6ICMzNzNiNDQ7XG5cbiAgY3Vyc29yOiBwb2ludGVyO1xuICBwYWRkaW5nOiBjYWxjKDAuNWVtICsgdmFyKC0tcykpIGNhbGMoMC45ZW0gKyB2YXIoLS1zKSk7XG4gIGNvbG9yOiB2YXIoLS1jb2xvcik7XG4gIC0tX3A6IHZhcigtLXMpO1xuICBiYWNrZ3JvdW5kOiBjb25pYy1ncmFkaWVudChcbiAgICAgIGZyb20gOTBkZWcgYXQgdmFyKC0tYikgdmFyKC0tYiksXG4gICAgICAjMDAwMCA5MGRlZyxcbiAgICAgIHZhcigtLWNvbG9yKSAwXG4gICAgKVxuICAgIHZhcigtLV9wKSB2YXIoLS1fcCkgLyBjYWxjKDEwMCUgLSB2YXIoLS1iKSAtIDIgKiB2YXIoLS1fcCkpXG4gICAgY2FsYygxMDAlIC0gdmFyKC0tYikgLSAyICogdmFyKC0tX3ApKTtcbiAgdHJhbnNpdGlvbjpcbiAgICAwLjNzIGxpbmVhcixcbiAgICBjb2xvciAwcyxcbiAgICBiYWNrZ3JvdW5kLWNvbG9yIDBzO1xuICBvdXRsaW5lOiB2YXIoLS1iKSBzb2xpZCAjMDAwMDtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuNmVtO1xuICBmb250LXNpemU6IDE2cHg7XG5cbiAgYm9yZGVyOiAwO1xuXG4gIHVzZXItc2VsZWN0OiBub25lO1xuICAtd2Via2l0LXVzZXItc2VsZWN0OiBub25lO1xuICB0b3VjaC1hY3Rpb246IG1hbmlwdWxhdGlvbjtcbn1cblxuYnV0dG9uOmhvdmVyLFxuYnV0dG9uOmZvY3VzLXZpc2libGUge1xuICAtLV9wOiAwcHg7XG4gIG91dGxpbmUtY29sb3I6IHZhcigtLWNvbG9yKTtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuMDVlbTtcbn1cblxuYnV0dG9uOmFjdGl2ZSB7XG4gIGJhY2tncm91bmQ6IHZhcigtLWNvbG9yKTtcbiAgY29sb3I6ICNmZmY7XG59XG4ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlU3RhdGUgfSBmcm9tIFwicmVhY3RcIjtcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICBjb25zdCBbY291bnQsIHNldENvdW50XSA9IHVzZVN0YXRlKDApO1xuICByZXR1cm4gPGJ1dHRvbiBvbkNsaWNrPXsoKSA9PiBzZXRDb3VudChjb3VudCArIDEpfT57Y291bnR9PC9idXR0b24+O1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IFwiLi9zdHlsZXMuY3NzXCI7XG5cbmltcG9ydCBBcHAgZnJvbSBcIi4vQXBwXCI7XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+XG4pOyJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjUuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOnRydWUsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOnRydWUsIndyYXBDb250ZW50Ijp0cnVlLCJjbG9zYWJsZVRhYnMiOnRydWUsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOmZhbHNlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7ImFjdGl2ZUZpbGUiOiIvQXBwLmpzIiwidmlzaWJsZUZpbGVzIjpbIi9BcHAuanMiXSwidGhlbWUiOiJrZW1wb0RhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nUHJldmlldycifX19" height="750px" width="100%"></iframe>

<p>Great!</p>
<p>We can manage state in React now, but how does <code>useRef</code> come into play?</p>
<h2 id="heading-the-useref-hook-lets-you-track-state-that-isnt-needed-for-rendering">The useRef hook lets you track state that isn't needed for rendering.</h2>
<p>Just like the <code>useState</code> hook, the <code>useRef</code> hook lets you track state. The hook returns an object with a <code>current</code> property that contains the state. To update the state, you just need to update the <code>current</code> property.</p>
<p><strong>The key difference is that any update to this state <em>won't</em> trigger a re-render.</strong></p>
<p>Here's a similar example as above, but with the <code>useRef</code> hook instead. There's also a button to <em>force</em> the component to re-render.</p>
<p>When you click the count button, notice how the state doesn't update. This is because updates to <code>useRef</code> values <em>don't</em> trigger re-renders.</p>
<p>That said, the underlying state is still updating.</p>
<p>But how do we prove that?</p>
<p>Well, if we use the other button to <em>force</em> a re-render, you'll notice that the button's value changes based on the current state tracked by the <code>useRef</code> hook:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2Zodnk1eXlqd2pnOHFiYzAzeXdyY2JvbSIsIm5hbWUiOiJ1c2VTdGF0ZSB2cyB1c2VSZWYgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG4gIGhlaWdodDogMTAwdmg7XG59XG5cbmRpdiB7XG4gIGhlaWdodDogMTAwJTtcbiAgd2lkdGg6IDEwMCU7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogc3BhY2UtYXJvdW5kO1xufVxuXG5idXR0b24ge1xuICAtLWI6IDNweDsgLyogYm9yZGVyIHRoaWNrbmVzcyAqL1xuICAtLXM6IDAuNDVlbTsgLyogc2l6ZSBvZiB0aGUgY29ybmVyICovXG4gIC0tY29sb3I6ICMzNzNiNDQ7XG5cbiAgY3Vyc29yOiBwb2ludGVyO1xuICBwYWRkaW5nOiBjYWxjKDAuNWVtICsgdmFyKC0tcykpIGNhbGMoMC45ZW0gKyB2YXIoLS1zKSk7XG4gIGNvbG9yOiB2YXIoLS1jb2xvcik7XG4gIC0tX3A6IHZhcigtLXMpO1xuICBiYWNrZ3JvdW5kOiBjb25pYy1ncmFkaWVudChcbiAgICAgIGZyb20gOTBkZWcgYXQgdmFyKC0tYikgdmFyKC0tYiksXG4gICAgICAjMDAwMCA5MGRlZyxcbiAgICAgIHZhcigtLWNvbG9yKSAwXG4gICAgKVxuICAgIHZhcigtLV9wKSB2YXIoLS1fcCkgLyBjYWxjKDEwMCUgLSB2YXIoLS1iKSAtIDIgKiB2YXIoLS1fcCkpXG4gICAgY2FsYygxMDAlIC0gdmFyKC0tYikgLSAyICogdmFyKC0tX3ApKTtcbiAgdHJhbnNpdGlvbjpcbiAgICAwLjNzIGxpbmVhcixcbiAgICBjb2xvciAwcyxcbiAgICBiYWNrZ3JvdW5kLWNvbG9yIDBzO1xuICBvdXRsaW5lOiB2YXIoLS1iKSBzb2xpZCAjMDAwMDtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuNmVtO1xuICBmb250LXNpemU6IDE2cHg7XG5cbiAgYm9yZGVyOiAwO1xuXG4gIHVzZXItc2VsZWN0OiBub25lO1xuICAtd2Via2l0LXVzZXItc2VsZWN0OiBub25lO1xuICB0b3VjaC1hY3Rpb246IG1hbmlwdWxhdGlvbjtcbn1cblxuYnV0dG9uOmhvdmVyLFxuYnV0dG9uOmZvY3VzLXZpc2libGUge1xuICAtLV9wOiAwcHg7XG4gIG91dGxpbmUtY29sb3I6IHZhcigtLWNvbG9yKTtcbiAgb3V0bGluZS1vZmZzZXQ6IDAuMDVlbTtcbn1cblxuYnV0dG9uOmFjdGl2ZSB7XG4gIGJhY2tncm91bmQ6IHZhcigtLWNvbG9yKTtcbiAgY29sb3I6ICNmZmY7XG59XG4ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlUmVmIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyB1c2VSZXJlbmRlciB9IGZyb20gXCIuL3V0aWxzXCI7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpIHtcbiAgY29uc3QgY291bnQgPSB1c2VSZWYoMCk7XG4gIGNvbnN0IHJlcmVuZGVyID0gdXNlUmVyZW5kZXIoKTtcblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIG9uQ2xpY2s9eygpID0+IHtcbiAgICAgICAgICBjb3VudC5jdXJyZW50ID0gY291bnQuY3VycmVudCArIDE7XG4gICAgICAgIH19XG4gICAgICA+XG4gICAgICAgIHtjb3VudC5jdXJyZW50fVxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uIG9uQ2xpY2s9e3JlcmVuZGVyfT5SZXJlbmRlcjwvYnV0dG9uPlxuICAgIDwvPlxuICApO1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IFwiLi9zdHlsZXMuY3NzXCI7XG5cbmltcG9ydCBBcHAgZnJvbSBcIi4vQXBwXCI7XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+XG4pOyJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjUuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In0sIi91dGlscy5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlU3RhdGUgfSBmcm9tIFwicmVhY3RcIjtcblxuZXhwb3J0IGZ1bmN0aW9uIHVzZVJlcmVuZGVyKCkge1xuICBjb25zdCBbY291bnQsIHNldENvdW50XSA9IHVzZVN0YXRlKDApO1xuICByZXR1cm4gKCkgPT4gc2V0Q291bnQoY291bnQgKyAxKTtcbn1cbiJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjpmYWxzZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJhY3RpdmVGaWxlIjoiL0FwcC5qcyIsInZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="750px" width="100%"></iframe>

<blockquote>
<p>Note: Because updates to useRef values don't trigger re-renders, you want to make sure you don't use a ref's value in your JSX. I know I'm breaking that rule above, but the example is just for demonstrative purposes.</p>
</blockquote>
<p>Ok! So we want to use a ref when we track state that <em>doesn't</em> trigger a re-render. But what kind of state shouldn't trigger a re-render?</p>
<p>Let's take a look at a couple of examples.</p>
<h3 id="heading-how-to-scroll-an-element-into-view">How to scroll an element into view.</h3>
<p>It's common to track DOM elements with the <code>useRef</code> hook. If you pass your ref variable to an element's <code>ref</code> prop, React will automatically set your variable's <code>current</code> property to the DOM element.</p>
<p>Once you have access to the DOM element, you can do various things like reset a form input or scroll an element into view.</p>
<p>In the example below, we have a dummy recipe blog post. Like most recipe blog posts these days, there's content <em>before</em> the recipe that provides some background about the dish.</p>
<p>Wouldn't it be nice if there were a button that let you skip all that extra information and jump straight to the good stuff?</p>
<p>Let's see how we might be able to do that with the <code>useRef</code> hook.</p>
<blockquote>
<p>Note: I used ChatGPT to generate a dummy recipe blog post, so apologies if there are mistakes in the content.</p>
</blockquote>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2Zodnk1eXlqd2pnOHFiYzAzeXdyY2JvbSIsIm5hbWUiOiJ1c2VTdGF0ZSB2cyB1c2VSZWYgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG4gIGhlaWdodDogMTAwdmg7XG59XG5cbmJ1dHRvbiB7XG4gIC0tYjogM3B4OyAvKiBib3JkZXIgdGhpY2tuZXNzICovXG4gIC0tczogMC40NWVtOyAvKiBzaXplIG9mIHRoZSBjb3JuZXIgKi9cbiAgLS1jb2xvcjogIzM3M2I0NDtcblxuICBjdXJzb3I6IHBvaW50ZXI7XG4gIHBhZGRpbmc6IGNhbGMoMC41ZW0gKyB2YXIoLS1zKSkgY2FsYygwLjllbSArIHZhcigtLXMpKTtcbiAgY29sb3I6IHZhcigtLWNvbG9yKTtcbiAgLS1fcDogdmFyKC0tcyk7XG4gIGJhY2tncm91bmQ6IGNvbmljLWdyYWRpZW50KFxuICAgICAgZnJvbSA5MGRlZyBhdCB2YXIoLS1iKSB2YXIoLS1iKSxcbiAgICAgICMwMDAwIDkwZGVnLFxuICAgICAgdmFyKC0tY29sb3IpIDBcbiAgICApXG4gICAgdmFyKC0tX3ApIHZhcigtLV9wKSAvIGNhbGMoMTAwJSAtIHZhcigtLWIpIC0gMiAqIHZhcigtLV9wKSlcbiAgICBjYWxjKDEwMCUgLSB2YXIoLS1iKSAtIDIgKiB2YXIoLS1fcCkpO1xuICB0cmFuc2l0aW9uOlxuICAgIDAuM3MgbGluZWFyLFxuICAgIGNvbG9yIDBzLFxuICAgIGJhY2tncm91bmQtY29sb3IgMHM7XG4gIG91dGxpbmU6IHZhcigtLWIpIHNvbGlkICMwMDAwO1xuICBvdXRsaW5lLW9mZnNldDogMC42ZW07XG4gIGZvbnQtc2l6ZTogMTZweDtcblxuICBib3JkZXI6IDA7XG5cbiAgdXNlci1zZWxlY3Q6IG5vbmU7XG4gIC13ZWJraXQtdXNlci1zZWxlY3Q6IG5vbmU7XG4gIHRvdWNoLWFjdGlvbjogbWFuaXB1bGF0aW9uO1xufVxuXG5idXR0b246aG92ZXIsXG5idXR0b246Zm9jdXMtdmlzaWJsZSB7XG4gIC0tX3A6IDBweDtcbiAgb3V0bGluZS1jb2xvcjogdmFyKC0tY29sb3IpO1xuICBvdXRsaW5lLW9mZnNldDogMC4wNWVtO1xufVxuXG5idXR0b246YWN0aXZlIHtcbiAgYmFja2dyb3VuZDogdmFyKC0tY29sb3IpO1xuICBjb2xvcjogI2ZmZjtcbn1cbiJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJpbXBvcnQgeyB1c2VSZWYgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IHVzZVJlcmVuZGVyIH0gZnJvbSBcIi4vdXRpbHNcIjtcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICBjb25zdCByZWNpcGVSZWYgPSB1c2VSZWYobnVsbCk7XG4gIGNvbnN0IHJlcmVuZGVyID0gdXNlUmVyZW5kZXIoKTtcblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIG9uQ2xpY2s9eygpID0+IHtcbiAgICAgICAgICByZWNpcGVSZWYuY3VycmVudC5zY3JvbGxJbnRvVmlldygpO1xuICAgICAgICB9fVxuICAgICAgPlxuICAgICAgICBKdW1wIHRvIFJlY2lwZVxuICAgICAgPC9idXR0b24+XG5cbiAgICAgIDxkaXY+XG4gICAgICAgIDxoMT5BIFRhc3RlIG9mIEl0YWx5OiBDcmVhbXkgU3BpbmFjaCBhbmQgTXVzaHJvb20gQWxmcmVkbyBQYXN0YTwvaDE+XG4gICAgICAgIDxwPlxuICAgICAgICAgIFdoZW4gaXQgY29tZXMgdG8gY29tZm9ydCBmb29kLCBwYXN0YSBkaXNoZXMgb2Z0ZW4gdG9wIHRoZSBsaXN0LlxuICAgICAgICAgIFRoZXJlJ3Mgc29tZXRoaW5nIGluY3JlZGlibHkgc2F0aXNmeWluZyBhYm91dCB0d2lybGluZyBwZXJmZWN0bHlcbiAgICAgICAgICBjb29rZWQgcGFzdGEgYXJvdW5kIHlvdXIgZm9yayBhbmQgc2F2b3JpbmcgZWFjaCBjcmVhbXkgYml0ZS4gVG9kYXksXG4gICAgICAgICAgSeKAmW0gc2hhcmluZyBhIHJlY2lwZSBmb3IgQ3JlYW15IFNwaW5hY2ggYW5kIE11c2hyb29tIEFsZnJlZG8gUGFzdGEsIGFcbiAgICAgICAgICBkaXNoIHRoYXQgY29tYmluZXMgdGhlIGVhcnRoaW5lc3Mgb2YgbXVzaHJvb21zIHdpdGggdGhlIHZpYnJhbnQgZmxhdm9yXG4gICAgICAgICAgb2Ygc3BpbmFjaCwgYWxsIGNvYXRlZCBpbiBhIGx1c2Npb3VzIEFsZnJlZG8gc2F1Y2UuIFdoZXRoZXIgeW91J3JlXG4gICAgICAgICAgY29va2luZyBmb3IgYSBzcGVjaWFsIG9jY2FzaW9uIG9yIGEgc2ltcGxlIHdlZWtuaWdodCBkaW5uZXIsIHRoaXNcbiAgICAgICAgICBwYXN0YSB3aWxsIGxlYXZlIHlvdSBhbmQgeW91ciBndWVzdHMgd2FudGluZyBtb3JlLlxuICAgICAgICA8L3A+XG5cbiAgICAgICAgPHA+XG4gICAgICAgICAgVGhlIG9yaWdpbnMgb2YgQWxmcmVkbyBzYXVjZSBkYXRlIGJhY2sgdG8gZWFybHkgMjB0aC1jZW50dXJ5IFJvbWUsXG4gICAgICAgICAgd2hlcmUgQWxmcmVkbyBkaSBMZWxpbyBjcmVhdGVkIGl0IHRvIHNhdGlzZnkgaGlzIHdpZmXigJlzIHByZWduYW5jeVxuICAgICAgICAgIGNyYXZpbmdzLiBUaGUgY29tYmluYXRpb24gb2YgYnV0dGVyLCBjcmVhbSwgYW5kIFBhcm1lc2FuIGNoZWVzZVxuICAgICAgICAgIGNyZWF0ZXMgYSByaWNoLCB2ZWx2ZXR5IHNhdWNlIHRoYXQgaGFzIGJlY29tZSBhIGJlbG92ZWQgc3RhcGxlIGluXG4gICAgICAgICAgSXRhbGlhbi1BbWVyaWNhbiBjdWlzaW5lLiBCeSBhZGRpbmcgZnJlc2ggc3BpbmFjaCBhbmQgbXVzaHJvb21zLCB3ZeKAmXJlXG4gICAgICAgICAgbm90IG9ubHkgZW5oYW5jaW5nIHRoZSBudXRyaXRpb25hbCB2YWx1ZSBidXQgYWxzbyBhZGRpbmcgYSBkZXB0aCBvZlxuICAgICAgICAgIGZsYXZvciB0aGF0IGJlYXV0aWZ1bGx5IGNvbXBsZW1lbnRzIHRoZSBjcmVhbXkgc2F1Y2UuXG4gICAgICAgIDwvcD5cblxuICAgICAgICA8cD5cbiAgICAgICAgICBCZWZvcmUgd2UgZGl2ZSBpbnRvIHRoZSByZWNpcGUsIGxldOKAmXMgdGFsayBhYm91dCBhIGZldyBrZXlcbiAgICAgICAgICBpbmdyZWRpZW50czpcbiAgICAgICAgPC9wPlxuICAgICAgICA8dWw+XG4gICAgICAgICAgPGxpPlxuICAgICAgICAgICAgPHN0cm9uZz5QYXN0YTwvc3Ryb25nPjogRmV0dHVjY2luZSBpcyB0cmFkaXRpb25hbGx5IHVzZWQsIGJ1dCBmZWVsXG4gICAgICAgICAgICBmcmVlIHRvIHVzZSB5b3VyIGZhdm9yaXRlIHBhc3RhIHNoYXBlLlxuICAgICAgICAgIDwvbGk+XG4gICAgICAgICAgPGxpPlxuICAgICAgICAgICAgPHN0cm9uZz5NdXNocm9vbXM8L3N0cm9uZz46IEEgbWl4IG9mIGJ1dHRvbiBhbmQgY3JlbWluaSBtdXNocm9vbXNcbiAgICAgICAgICAgIHdvcmtzIHdlbGwsIGJ1dCB5b3UgY2FuIHVzZSBhbnkgdmFyaWV0eSB5b3UgcHJlZmVyLlxuICAgICAgICAgIDwvbGk+XG4gICAgICAgICAgPGxpPlxuICAgICAgICAgICAgPHN0cm9uZz5TcGluYWNoPC9zdHJvbmc+OiBGcmVzaCBiYWJ5IHNwaW5hY2ggYWRkcyBhIGxvdmVseSB0ZXh0dXJlXG4gICAgICAgICAgICBhbmQgY29sb3IuXG4gICAgICAgICAgPC9saT5cbiAgICAgICAgICA8bGk+XG4gICAgICAgICAgICA8c3Ryb25nPlBhcm1lc2FuIENoZWVzZTwvc3Ryb25nPjogRm9yIHRoZSBiZXN0IGZsYXZvciwgdXNlIGZyZXNobHlcbiAgICAgICAgICAgIGdyYXRlZCBQYXJtZXNhbi5cbiAgICAgICAgICA8L2xpPlxuICAgICAgICA8L3VsPlxuXG4gICAgICAgIDxoMiByZWY9e3JlY2lwZVJlZn0+Q3JlYW15IFNwaW5hY2ggYW5kIE11c2hyb29tIEFsZnJlZG8gUGFzdGE8L2gyPlxuXG4gICAgICAgIDxkaXY+XG4gICAgICAgICAgPGgzPkluZ3JlZGllbnRzOjwvaDM+XG4gICAgICAgICAgPHVsPlxuICAgICAgICAgICAgPGxpPjEyIG96IGZldHR1Y2NpbmUgcGFzdGE8L2xpPlxuICAgICAgICAgICAgPGxpPjIgdGJzcCBvbGl2ZSBvaWw8L2xpPlxuICAgICAgICAgICAgPGxpPjIgY2xvdmVzIGdhcmxpYywgbWluY2VkPC9saT5cbiAgICAgICAgICAgIDxsaT4xIHNtYWxsIG9uaW9uLCBmaW5lbHkgY2hvcHBlZDwvbGk+XG4gICAgICAgICAgICA8bGk+OCBveiBidXR0b24gbXVzaHJvb21zLCBzbGljZWQ8L2xpPlxuICAgICAgICAgICAgPGxpPjggb3ogY3JlbWluaSBtdXNocm9vbXMsIHNsaWNlZDwvbGk+XG4gICAgICAgICAgICA8bGk+NCBjdXBzIGJhYnkgc3BpbmFjaDwvbGk+XG4gICAgICAgICAgICA8bGk+MSBjdXAgaGVhdnkgY3JlYW08L2xpPlxuICAgICAgICAgICAgPGxpPjEgY3VwIGZyZXNobHkgZ3JhdGVkIFBhcm1lc2FuIGNoZWVzZTwvbGk+XG4gICAgICAgICAgICA8bGk+U2FsdCBhbmQgcGVwcGVyIHRvIHRhc3RlPC9saT5cbiAgICAgICAgICAgIDxsaT5GcmVzaCBwYXJzbGV5LCBjaG9wcGVkIChmb3IgZ2FybmlzaCk8L2xpPlxuICAgICAgICAgIDwvdWw+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDxkaXY+XG4gICAgICAgICAgPGgzPkluc3RydWN0aW9uczo8L2gzPlxuICAgICAgICAgIDxvbD5cbiAgICAgICAgICAgIDxsaT5cbiAgICAgICAgICAgICAgPHN0cm9uZz5Db29rIHRoZSBQYXN0YTo8L3N0cm9uZz5cbiAgICAgICAgICAgICAgPHVsPlxuICAgICAgICAgICAgICAgIDxsaT5cbiAgICAgICAgICAgICAgICAgIEJyaW5nIGEgbGFyZ2UgcG90IG9mIHNhbHRlZCB3YXRlciB0byBhIGJvaWwuIEFkZCB0aGVcbiAgICAgICAgICAgICAgICAgIGZldHR1Y2NpbmUgYW5kIGNvb2sgYWNjb3JkaW5nIHRvIHRoZSBwYWNrYWdlIGluc3RydWN0aW9uc1xuICAgICAgICAgICAgICAgICAgdW50aWwgYWwgZGVudGUuIFJlc2VydmUgMSBjdXAgb2YgcGFzdGEgd2F0ZXIsIHRoZW4gZHJhaW4gdGhlXG4gICAgICAgICAgICAgICAgICBwYXN0YSBhbmQgc2V0IGFzaWRlLlxuICAgICAgICAgICAgICAgIDwvbGk+XG4gICAgICAgICAgICAgIDwvdWw+XG4gICAgICAgICAgICA8L2xpPlxuICAgICAgICAgICAgPGxpPlxuICAgICAgICAgICAgICA8c3Ryb25nPlByZXBhcmUgdGhlIFNhdWNlOjwvc3Ryb25nPlxuICAgICAgICAgICAgICA8dWw+XG4gICAgICAgICAgICAgICAgPGxpPlxuICAgICAgICAgICAgICAgICAgSW4gYSBsYXJnZSBza2lsbGV0LCBoZWF0IHRoZSBvbGl2ZSBvaWwgb3ZlciBtZWRpdW0gaGVhdC4gQWRkXG4gICAgICAgICAgICAgICAgICB0aGUgZ2FybGljIGFuZCBvbmlvbiwgYW5kIHNhdXTDqSB1bnRpbCBmcmFncmFudCBhbmRcbiAgICAgICAgICAgICAgICAgIHRyYW5zbHVjZW50LCBhYm91dCAyLTMgbWludXRlcy5cbiAgICAgICAgICAgICAgICA8L2xpPlxuICAgICAgICAgICAgICAgIDxsaT5cbiAgICAgICAgICAgICAgICAgIEFkZCB0aGUgc2xpY2VkIG11c2hyb29tcyBhbmQgY29vayB1bnRpbCB0aGV5IGFyZSB0ZW5kZXIgYW5kXG4gICAgICAgICAgICAgICAgICBicm93bmVkLCBhYm91dCA1LTcgbWludXRlcy5cbiAgICAgICAgICAgICAgICA8L2xpPlxuICAgICAgICAgICAgICAgIDxsaT5cbiAgICAgICAgICAgICAgICAgIFN0aXIgaW4gdGhlIGJhYnkgc3BpbmFjaCBhbmQgY29vayB1bnRpbCB3aWx0ZWQsIGFib3V0IDJcbiAgICAgICAgICAgICAgICAgIG1pbnV0ZXMuXG4gICAgICAgICAgICAgICAgPC9saT5cbiAgICAgICAgICAgICAgPC91bD5cbiAgICAgICAgICAgIDwvbGk+XG4gICAgICAgICAgICA8bGk+XG4gICAgICAgICAgICAgIDxzdHJvbmc+TWFrZSB0aGUgQWxmcmVkbyBTYXVjZTo8L3N0cm9uZz5cbiAgICAgICAgICAgICAgPHVsPlxuICAgICAgICAgICAgICAgIDxsaT5cbiAgICAgICAgICAgICAgICAgIFJlZHVjZSB0aGUgaGVhdCB0byBsb3cgYW5kIHBvdXIgaW4gdGhlIGhlYXZ5IGNyZWFtLiBTdGlyIHRvXG4gICAgICAgICAgICAgICAgICBjb21iaW5lIHdpdGggdGhlIHZlZ2V0YWJsZXMuXG4gICAgICAgICAgICAgICAgPC9saT5cbiAgICAgICAgICAgICAgICA8bGk+XG4gICAgICAgICAgICAgICAgICBHcmFkdWFsbHkgYWRkIHRoZSBncmF0ZWQgUGFybWVzYW4gY2hlZXNlLCBzdGlycmluZyBjb25zdGFudGx5XG4gICAgICAgICAgICAgICAgICB1bnRpbCB0aGUgY2hlZXNlIGlzIG1lbHRlZCBhbmQgdGhlIHNhdWNlIGlzIHNtb290aC4gSWYgdGhlXG4gICAgICAgICAgICAgICAgICBzYXVjZSBpcyB0b28gdGhpY2ssIGFkZCBzb21lIG9mIHRoZSByZXNlcnZlZCBwYXN0YSB3YXRlciwgYVxuICAgICAgICAgICAgICAgICAgbGl0dGxlIGF0IGEgdGltZSwgdW50aWwgeW91IHJlYWNoIHRoZSBkZXNpcmVkIGNvbnNpc3RlbmN5LlxuICAgICAgICAgICAgICAgIDwvbGk+XG4gICAgICAgICAgICAgIDwvdWw+XG4gICAgICAgICAgICA8L2xpPlxuICAgICAgICAgICAgPGxpPlxuICAgICAgICAgICAgICA8c3Ryb25nPkNvbWJpbmUgYW5kIFNlcnZlOjwvc3Ryb25nPlxuICAgICAgICAgICAgICA8dWw+XG4gICAgICAgICAgICAgICAgPGxpPlxuICAgICAgICAgICAgICAgICAgQWRkIHRoZSBjb29rZWQgZmV0dHVjY2luZSB0byB0aGUgc2tpbGxldCBhbmQgdG9zcyB0byBjb2F0IHRoZVxuICAgICAgICAgICAgICAgICAgcGFzdGEgZXZlbmx5IHdpdGggdGhlIHNhdWNlLiBTZWFzb24gd2l0aCBzYWx0IGFuZCBwZXBwZXIgdG9cbiAgICAgICAgICAgICAgICAgIHRhc3RlLlxuICAgICAgICAgICAgICAgIDwvbGk+XG4gICAgICAgICAgICAgICAgPGxpPlxuICAgICAgICAgICAgICAgICAgU2VydmUgaW1tZWRpYXRlbHksIGdhcm5pc2hlZCB3aXRoIGZyZXNobHkgY2hvcHBlZCBwYXJzbGV5IGFuZFxuICAgICAgICAgICAgICAgICAgYWRkaXRpb25hbCBQYXJtZXNhbiBjaGVlc2UgaWYgZGVzaXJlZC5cbiAgICAgICAgICAgICAgICA8L2xpPlxuICAgICAgICAgICAgICA8L3VsPlxuICAgICAgICAgICAgPC9saT5cbiAgICAgICAgICA8L29sPlxuICAgICAgICA8L2Rpdj5cblxuICAgICAgICA8cD5cbiAgICAgICAgICBUaGlzIENyZWFteSBTcGluYWNoIGFuZCBNdXNocm9vbSBBbGZyZWRvIFBhc3RhIGlzIGEgZGVsaWdodGZ1bCBkaXNoXG4gICAgICAgICAgdGhhdOKAmXMgc3VyZSB0byBpbXByZXNzLiBUaGUgY29tYmluYXRpb24gb2YgY3JlYW15IHNhdWNlLCB0ZW5kZXJcbiAgICAgICAgICBtdXNocm9vbXMsIGFuZCB2aWJyYW50IHNwaW5hY2ggbWFrZXMgZXZlcnkgYml0ZSBhIGpveS4gUGFpciBpdCB3aXRoIGFcbiAgICAgICAgICBjcmlzcCBncmVlbiBzYWxhZCBhbmQgYSBnbGFzcyBvZiB3aGl0ZSB3aW5lIGZvciBhIGNvbXBsZXRlIG1lYWwuIEJ1b25cbiAgICAgICAgICBhcHBldGl0byFcbiAgICAgICAgPC9wPlxuICAgICAgPC9kaXY+XG4gICAgPC8+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifSwiL3V0aWxzLmpzIjp7ImNvZGUiOiJpbXBvcnQgeyB1c2VTdGF0ZSB9IGZyb20gXCJyZWFjdFwiO1xuXG5leHBvcnQgZnVuY3Rpb24gdXNlUmVyZW5kZXIoKSB7XG4gIGNvbnN0IFtjb3VudCwgc2V0Q291bnRdID0gdXNlU3RhdGUoMCk7XG4gIHJldHVybiAoKSA9PiBzZXRDb3VudChjb3VudCArIDEpO1xufVxuIn19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOnRydWUsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOnRydWUsIndyYXBDb250ZW50Ijp0cnVlLCJjbG9zYWJsZVRhYnMiOnRydWUsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOmZhbHNlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7ImFjdGl2ZUZpbGUiOiIvQXBwLmpzIiwidmlzaWJsZUZpbGVzIjpbIi9BcHAuanMiXSwidGhlbWUiOiJrZW1wb0RhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nUHJldmlldycifX19" height="750px" width="100%"></iframe>

<p>Notice how we're passing the <code>recipeRef</code> to the <code>h2</code> element <code>Creamy Spinach and Mushroom Alfredo Pasta</code>. React automatically sets the <code>current</code> value of our ref to the <code>h2</code> DOM element. With that in place, we can use the ref to scroll the element into view when the button is clicked.</p>
<p>Easy!</p>
<h3 id="heading-how-to-build-a-countdown-timer">How to build a countdown timer.</h3>
<p>Another good use case for the <code>useRef</code> hook is to keep track of an interval ID that's returned from a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/setInterval">setInterval</a> call.</p>
<p>Let's say we had a countdown timer that shows the countdown in seconds. The easiest way to build this would be to use the <code>useState</code> hook to show the current timer value and then update the state every second.</p>
<p>That part is easy.</p>
<p>But what if we wanted a way to <em>start</em> and <em>stop</em> the timer? This is a little bit more tricky. The good news is that we can use the <code>useRef</code> hook for this. We can keep track of an interval ID when the timer starts and use it to clear the interval when the timer stops.</p>
<p>Let's see what that looks like:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2Zodnk1eXlqd2pnOHFiYzAzeXdyY2JvbSIsIm5hbWUiOiJ1c2VTdGF0ZSB2cyB1c2VSZWYgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG4gIGhlaWdodDogMTAwdmg7XG59XG5cbiNyb290LFxuZGl2IHtcbiAgd2lkdGg6IDEwMCU7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGp1c3RpZnktY29udGVudDogc3BhY2UtYXJvdW5kO1xufVxuXG4jcm9vdCB7XG4gIGhlaWdodDogMTAwJTtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7XG4gIGdhcDogNHJlbTtcbn1cblxuYnV0dG9uIHtcbiAgLS1iOiAzcHg7IC8qIGJvcmRlciB0aGlja25lc3MgKi9cbiAgLS1zOiAwLjQ1ZW07IC8qIHNpemUgb2YgdGhlIGNvcm5lciAqL1xuICAtLWNvbG9yOiAjMzczYjQ0O1xuXG4gIGN1cnNvcjogcG9pbnRlcjtcbiAgcGFkZGluZzogY2FsYygwLjVlbSArIHZhcigtLXMpKSBjYWxjKDAuOWVtICsgdmFyKC0tcykpO1xuICBjb2xvcjogdmFyKC0tY29sb3IpO1xuICAtLV9wOiB2YXIoLS1zKTtcbiAgYmFja2dyb3VuZDogY29uaWMtZ3JhZGllbnQoXG4gICAgICBmcm9tIDkwZGVnIGF0IHZhcigtLWIpIHZhcigtLWIpLFxuICAgICAgIzAwMDAgOTBkZWcsXG4gICAgICB2YXIoLS1jb2xvcikgMFxuICAgIClcbiAgICB2YXIoLS1fcCkgdmFyKC0tX3ApIC8gY2FsYygxMDAlIC0gdmFyKC0tYikgLSAyICogdmFyKC0tX3ApKVxuICAgIGNhbGMoMTAwJSAtIHZhcigtLWIpIC0gMiAqIHZhcigtLV9wKSk7XG4gIHRyYW5zaXRpb246XG4gICAgMC4zcyBsaW5lYXIsXG4gICAgY29sb3IgMHMsXG4gICAgYmFja2dyb3VuZC1jb2xvciAwcztcbiAgb3V0bGluZTogdmFyKC0tYikgc29saWQgIzAwMDA7XG4gIG91dGxpbmUtb2Zmc2V0OiAwLjZlbTtcbiAgZm9udC1zaXplOiAxNnB4O1xuXG4gIGJvcmRlcjogMDtcblxuICB1c2VyLXNlbGVjdDogbm9uZTtcbiAgLXdlYmtpdC11c2VyLXNlbGVjdDogbm9uZTtcbiAgdG91Y2gtYWN0aW9uOiBtYW5pcHVsYXRpb247XG59XG5cbmJ1dHRvbjpob3ZlcixcbmJ1dHRvbjpmb2N1cy12aXNpYmxlIHtcbiAgLS1fcDogMHB4O1xuICBvdXRsaW5lLWNvbG9yOiB2YXIoLS1jb2xvcik7XG4gIG91dGxpbmUtb2Zmc2V0OiAwLjA1ZW07XG59XG5cbmJ1dHRvbjphY3RpdmUge1xuICBiYWNrZ3JvdW5kOiB2YXIoLS1jb2xvcik7XG4gIGNvbG9yOiAjZmZmO1xufVxuIn0sIi9BcHAuanMiOnsiY29kZSI6ImltcG9ydCB7IHVzZVJlZiwgdXNlU3RhdGUgfSBmcm9tIFwicmVhY3RcIjtcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICBjb25zdCBbY291bnQsIHNldENvdW50XSA9IHVzZVN0YXRlKDYwKTtcbiAgY29uc3QgaW50ZXJ2YWxJZCA9IHVzZVJlZihudWxsKTtcblxuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8ZGl2PlxuICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgb25DbGljaz17KCkgPT4ge1xuICAgICAgICAgICAgaWYgKGludGVydmFsSWQuY3VycmVudCkgcmV0dXJuO1xuXG4gICAgICAgICAgICBpbnRlcnZhbElkLmN1cnJlbnQgPSBzZXRJbnRlcnZhbCgoKSA9PiB7XG4gICAgICAgICAgICAgIHNldENvdW50KChwcmV2Q291bnQpID0+IHtcbiAgICAgICAgICAgICAgICByZXR1cm4gcHJldkNvdW50ID09PSAwID8gMCA6IHByZXZDb3VudCAtIDE7XG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfSwgMTAwMCk7XG4gICAgICAgICAgfX1cbiAgICAgICAgPlxuICAgICAgICAgIFN0YXJ0XG4gICAgICAgIDwvYnV0dG9uPlxuXG4gICAgICAgIDxidXR0b25cbiAgICAgICAgICBvbkNsaWNrPXsoKSA9PiB7XG4gICAgICAgICAgICBjbGVhckludGVydmFsKGludGVydmFsSWQuY3VycmVudCk7XG4gICAgICAgICAgICBpbnRlcnZhbElkLmN1cnJlbnQgPSBudWxsO1xuICAgICAgICAgIH19XG4gICAgICAgID5cbiAgICAgICAgICBTdG9wXG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgPC9kaXY+XG5cbiAgICAgIDxkaXY+e2NvdW50fSBzZWNvbmRzPC9kaXY+XG4gICAgPC8+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifX0sInRlbXBsYXRlIjoicmVhY3QifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6dHJ1ZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6dHJ1ZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6ZmFsc2UsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2V9fX0sImdlbmVyYWwiOnsiYWN0aXZlRmlsZSI6Ii9BcHAuanMiLCJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0=" height="750px" width="100%"></iframe>

<blockquote>
<p>Note: When we update the count, we're using a <a target="_blank" href="https://blog.thesshguy.com/react-use-state-functional-update">functional update</a> because the function we pass to setInterval will have a stale version of the count variable due to its closure.</p>
</blockquote>
<h2 id="heading-when-to-use-each-hook">When to use each hook.</h2>
<p>Hopefully, the examples above will help you build intuition around when to use each hook. But as a general rule of thumb use the <code>useState</code> hook when you want your component to re-render and the <code>useRef</code> hook when you don't want your component to re-render.</p>
]]></content:encoded></item><item><title><![CDATA[Sandpack: The Ultimate Lightweight Code Playground from CodeSandbox]]></title><description><![CDATA[I've been using code playgrounds in my blog posts for a while now. They're a great way to make the content more engaging and informative.
The easiest way to do this is to embed something like CodeSandbox or StackBlitz into the post. That said, while ...]]></description><link>https://blog.thesshguy.com/sandpack</link><guid isPermaLink="true">https://blog.thesshguy.com/sandpack</guid><category><![CDATA[Frontend Development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Mon, 03 Jun 2024 17:47:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1717436547742/39c53599-d8fa-4b31-9b0a-3dcdfd0427f1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>I've been using code playgrounds in my blog posts for a while now. They're a great way to make the content more engaging and informative.</p>
<p>The easiest way to do this is to embed something like CodeSandbox or StackBlitz into the post. That said, while these tools are amazing, they're not perfect. There's not much room to customize the experience <em>per</em> playground.</p>
<p>Luckily, the CodeSandbox team thought of this and released <a target="_blank" href="https://sandpack.codesandbox.io/">Sandpack</a>!</p>
<h3 id="heading-sandpack-lets-you-build-lightweight-customized-coding-experiences">Sandpack lets you build lightweight customized coding experiences.</h3>
<p>Sandpack is an open-source project that lets you build playgrounds powered by the <em>same</em> bundler that CodeSandbox uses. The difference is that you have far more control over the look and feel of your playground.</p>
<p>Sandpack consists of two main packages:</p>
<ol>
<li><p><a target="_blank" href="https://www.npmjs.com/package/@codesandbox/sandpack-client">sandpack-client</a>: a small package that lets you hook into the Codesandbox bundler</p>
</li>
<li><p><a target="_blank" href="https://www.npmjs.com/package/@codesandbox/sandpack-react">sandpack-react</a>: a set of React components that make it easy to interact with a Sandpack client</p>
</li>
</ol>
<p>For this post, we're going to focus on the <code>sandpack-react</code> package because it abstracts away a lot of the manual steps involved when using the <code>sandpack-client</code> package directly.</p>
<h3 id="heading-how-do-you-build-a-basic-sandpack-playground">How do you build a basic Sandpack playground?</h3>
<p>The easiest way to build a Sandpack playground in React is to use the core <code>Sandpack</code> component:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2dlMTdueHNoN3drZXRzdndyd3E2MXVnciIsIm5hbWUiOiJTYW5kcGFjayBFeGFtcGxlcyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxuaDEge1xuICBmb250LXNpemU6IDEuNXJlbTtcbn0ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgU2FuZHBhY2sgfSBmcm9tIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXJlYWN0XCI7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpIHtcbiAgcmV0dXJuIDxTYW5kcGFjayB0ZW1wbGF0ZT1cInJlYWN0XCIgLz47XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIixcbiAgICBcIkBjb2Rlc2FuZGJveC9zYW5kcGFjay1yZWFjdFwiOiBcIjIuMTQuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="750px" width="100%"></iframe>

<p>Sweet!</p>
<p>Our playground has a basic editor and preview, but how does it get its content? All we're returning is <code>&lt;Sandpack template="react" /&gt;</code>, but somehow, the playground is showing a "<strong>Hello World</strong>" application.</p>
<h3 id="heading-sandpack-comes-with-predefined-templates">Sandpack comes with predefined templates.</h3>
<p>You may have noticed we're passing a <code>template</code> prop to the <code>Sandpack</code> component. This is where the magic happens. Sandpack has a few templates baked into it for common frameworks like React and Vue.</p>
<p>In this case, because I set the template to <code>react</code>, we get this basic "<strong>Hello World</strong>" React app.</p>
<blockquote>
<p>Note: Try changing the template above to "vue" and see what happens.</p>
</blockquote>
<p>Ok!</p>
<p>So a "<strong>Hello World</strong>" app might seem cool, but it doesn't feel very useful. The built-in templates are a great starting point, but if you want to use Sandpack in your blog, you probably want your playground to show different code examples.</p>
<h3 id="heading-you-can-configure-sandpacks-default-files">You can configure Sandpack's default files.</h3>
<p>The good news is that Sandpack makes it easy to override the default template configuration. You can pass a set of files to the component that will be used to initialize the Sandpack instance:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2dlMTdueHNoN3drZXRzdndyd3E2MXVnciIsIm5hbWUiOiJTYW5kcGFjayBFeGFtcGxlcyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxuaDEge1xuICBmb250LXNpemU6IDEuNXJlbTtcbn0ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgU2FuZHBhY2sgfSBmcm9tIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXJlYWN0XCI7XG5cbmNvbnN0IGRlZmF1bHRBcHBGaWxlID0gYFxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICByZXR1cm4gPGgxPkhlbGxvIEN1c3RvbSBBcHAhPC9oMT5cbn1cbmA7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpIHtcbiAgcmV0dXJuIChcbiAgICA8U2FuZHBhY2tcbiAgICAgIHRlbXBsYXRlPVwicmVhY3RcIlxuICAgICAgZmlsZXM9e3tcbiAgICAgICAgXCIvQXBwLmpzXCI6IGRlZmF1bHRBcHBGaWxlLnRyaW0oKSxcbiAgICAgIH19XG4gICAgLz5cbiAgKTtcbn1cbiJ9LCIvaW5kZXguanMiOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiLFxuICAgIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXJlYWN0XCI6IFwiMi4xNC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOnRydWUsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOnRydWUsIndyYXBDb250ZW50Ijp0cnVlLCJjbG9zYWJsZVRhYnMiOnRydWUsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOnRydWUsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2V9fX0sImdlbmVyYWwiOnsidmlzaWJsZUZpbGVzIjpbIi9BcHAuanMiXSwidGhlbWUiOiJrZW1wb0RhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nUHJldmlldycifX19" height="750px" width="100%"></iframe>

<p>The <code>/App.js</code> file in our <code>files</code> prop overwrites the built-in <code>/App.js</code> file.</p>
<p>One thing to keep in mind is that the <em>key</em> (i.e. file name) should be the relative file path you want to override or include. You can find the default files of a template in the Sandpack GitHub repo.</p>
<blockquote>
<p>Note: Here's the <a target="_blank" href="https://github.com/codesandbox/sandpack/blob/main/sandpack-react/src/templates/runtime/react.ts">list of files for the React template</a> we're using.</p>
</blockquote>
<h3 id="heading-use-the-packagejson-file-to-add-npm-packages-to-your-playground">Use the package.json file to add npm packages to your playground.</h3>
<p>A code playground wouldn't be that useful if it didn't support using third-party packages. With Sandpack, you can do this with the <code>package.json</code> file. Override the default file and include whatever npm packages and versions you want.</p>
<p>Here's an example using a few Material UI packages to build an <em>App Bar</em> component:</p>
<blockquote>
<p>Note: This was adapted from the <a target="_blank" href="https://mui.com/material-ui/react-app-bar/#basic-app-bar">Material UI App Bar documentation</a>.</p>
</blockquote>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2dyMTdueHNoN3drZXRzdndyd3E2MXVnciIsIm5hbWUiOiJTYW5kcGFjayBFeGFtcGxlcyAzMyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxuLmJ0bi13cmFwcGVyIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IHJvdztcbiAgd2lkdGg6IDEwMCU7XG4gIGp1c3RpZnktY29udGVudDogc3BhY2UtYXJvdW5kO1xuICBwYWRkaW5nOiA4cHg7XG59XG4ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgU2FuZHBhY2sgfSBmcm9tIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXJlYWN0XCI7XG5cbmNvbnN0IGZpbGVzID0ge1xuICBcIi9wYWNrYWdlLmpzb25cIjogYFxue1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIixcbiAgICBcIkBtdWkvbWF0ZXJpYWxcIjogXCI1LjE1LjE5XCIsXG4gICAgXCJAbXVpL2ljb25zLW1hdGVyaWFsXCI6IFwiNS4xNS4xOVwiLFxuICAgIFwiQGVtb3Rpb24vc3R5bGVkXCI6IFwiMTEuMTEuNVwiLFxuICAgIFwiQGVtb3Rpb24vcmVhY3RcIjogXCIxMS4xMS40XCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59XG4gIGAudHJpbSgpLFxuICBcIi9BcHAuanNcIjogYFxuaW1wb3J0IEFwcEJhciBmcm9tICdAbXVpL21hdGVyaWFsL0FwcEJhcic7XG5pbXBvcnQgQm94IGZyb20gJ0BtdWkvbWF0ZXJpYWwvQm94JztcbmltcG9ydCBUb29sYmFyIGZyb20gJ0BtdWkvbWF0ZXJpYWwvVG9vbGJhcic7XG5pbXBvcnQgVHlwb2dyYXBoeSBmcm9tICdAbXVpL21hdGVyaWFsL1R5cG9ncmFwaHknO1xuaW1wb3J0IEJ1dHRvbiBmcm9tICdAbXVpL21hdGVyaWFsL0J1dHRvbic7XG5pbXBvcnQgSWNvbkJ1dHRvbiBmcm9tICdAbXVpL21hdGVyaWFsL0ljb25CdXR0b24nO1xuaW1wb3J0IE1lbnVJY29uIGZyb20gJ0BtdWkvaWNvbnMtbWF0ZXJpYWwvTWVudSc7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpIHtcbiAgcmV0dXJuIChcbiAgICA8Qm94IHN4PXt7IGZsZXhHcm93OiAxIH19PlxuICAgICAgPEFwcEJhciBwb3NpdGlvbj1cInN0YXRpY1wiPlxuICAgICAgICA8VG9vbGJhcj5cbiAgICAgICAgICA8SWNvbkJ1dHRvblxuICAgICAgICAgICAgc2l6ZT1cImxhcmdlXCJcbiAgICAgICAgICAgIGVkZ2U9XCJzdGFydFwiXG4gICAgICAgICAgICBjb2xvcj1cImluaGVyaXRcIlxuICAgICAgICAgICAgYXJpYS1sYWJlbD1cIm1lbnVcIlxuICAgICAgICAgICAgc3g9e3sgbXI6IDIgfX1cbiAgICAgICAgICA+XG4gICAgICAgICAgICA8TWVudUljb24gLz5cbiAgICAgICAgICA8L0ljb25CdXR0b24+XG4gICAgICAgICAgPFR5cG9ncmFwaHkgdmFyaWFudD1cImg2XCIgY29tcG9uZW50PVwiZGl2XCIgc3g9e3sgZmxleEdyb3c6IDEgfX0+XG4gICAgICAgICAgICBLZW1wb1xuICAgICAgICAgIDwvVHlwb2dyYXBoeT5cbiAgICAgICAgICA8QnV0dG9uIGNvbG9yPVwiaW5oZXJpdFwiPkxvZ2luPC9CdXR0b24+XG4gICAgICAgIDwvVG9vbGJhcj5cbiAgICAgIDwvQXBwQmFyPlxuICAgIDwvQm94PlxuICApO1xufVxuICBgLnRyaW0oKSxcbn07XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpIHtcbiAgcmV0dXJuIChcbiAgICA8U2FuZHBhY2tcbiAgICAgIHRlbXBsYXRlPVwicmVhY3RcIlxuICAgICAgZmlsZXM9e2ZpbGVzfVxuICAgICAgb3B0aW9ucz17eyBhY3RpdmVGaWxlOiBcIi9wYWNrYWdlLmpzb25cIiB9fVxuICAgIC8+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIixcbiAgICBcIkBjb2Rlc2FuZGJveC9zYW5kcGFjay1yZWFjdFwiOiBcIjIuMTQuMFwiLFxuICAgIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXRoZW1lc1wiOiBcIjIuMC4yMVwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="750px" width="100%"></iframe>

<p>So what else can we do?</p>
<p>I promised you customization, right?</p>
<h3 id="heading-you-can-apply-custom-themes-to-your-sandpack-instance">You can apply custom themes to your Sandpack instance.</h3>
<p>If you're feeling adventurous, you can build a theme using the <a target="_blank" href="https://sandpack.codesandbox.io/theme">Sandpack theme builder</a>. That said, Sandpack has several pre-defined themes in the <a target="_blank" href="https://www.npmjs.com/package/@codesandbox/sandpack-themes">sandpack-themes</a> package. To use them, you just have to install the package and choose the theme you want.</p>
<p>Here's an example using the Gruvbox Dark theme:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2dlMTdueHNoN3drZXRzdndyd3E2MXVnciIsIm5hbWUiOiJTYW5kcGFjayBFeGFtcGxlcyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxuaDEge1xuICBmb250LXNpemU6IDEuNXJlbTtcbn0ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgU2FuZHBhY2sgfSBmcm9tIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXJlYWN0XCI7XG5pbXBvcnQgeyBncnV2Ym94RGFyayB9IGZyb20gXCJAY29kZXNhbmRib3gvc2FuZHBhY2stdGhlbWVzXCI7XG5cbmNvbnN0IGRlZmF1bHRBcHBGaWxlID0gYFxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICByZXR1cm4gPGgxPkhlbGxvIEN1c3RvbSBUaGVtZSE8L2gxPlxufVxuYDtcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICByZXR1cm4gKFxuICAgIDxTYW5kcGFja1xuICAgICAgdGVtcGxhdGU9XCJyZWFjdFwiXG4gICAgICB0aGVtZT17Z3J1dmJveERhcmt9XG4gICAgICBmaWxlcz17e1xuICAgICAgICBcIi9BcHAuanNcIjogZGVmYXVsdEFwcEZpbGUudHJpbSgpLFxuICAgICAgfX1cbiAgICAvPlxuICApO1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IFwiLi9zdHlsZXMuY3NzXCI7XG5cbmltcG9ydCBBcHAgZnJvbSBcIi4vQXBwXCI7XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+XG4pOyJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjUuMC4wXCIsXG4gICAgXCJAY29kZXNhbmRib3gvc2FuZHBhY2stcmVhY3RcIjogXCIyLjE0LjBcIixcbiAgICBcIkBjb2Rlc2FuZGJveC9zYW5kcGFjay10aGVtZXNcIjogXCIyLjAuMjFcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifX0sInRlbXBsYXRlIjoicmVhY3QifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6dHJ1ZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6dHJ1ZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6dHJ1ZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0=" height="750px" width="100%"></iframe>

<p>Changing the theme is just the tip of the iceberg. There are way more things you can customize with Sandpack. All the examples we've looked at so far use the <code>Sandpack</code> component.</p>
<p>While this component is the easiest way to get started, Sandpack also gives us access to lower-level components to build even more unique experiences.</p>
<h3 id="heading-create-unique-experiences-using-sandpacks-building-blocks">Create unique experiences using Sandpack's building blocks.</h3>
<p>There are quite a few components that you can use, but here are a few core ones you'll probably reach for most often:</p>
<ol>
<li><p><code>SandpackProvider</code> (Essential): This component is required when using other low-level components. It uses React context to provide the other components with Sandpack's internal state.</p>
</li>
<li><p><code>SandpackLayout</code>: This component wraps other components like the Editor or Preview and gives the playground a basic responsive design.</p>
</li>
<li><p><code>SandpackPreview</code>: This component <em>displays</em> the bundled code in an iframe.</p>
</li>
<li><p><code>SandpackCodeEditor</code>: This component lets you update the code.</p>
</li>
</ol>
<p>Let's see these components in action:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2dlMTdueHNoN3drZXRzdndyd3E2MXVnciIsIm5hbWUiOiJTYW5kcGFjayBFeGFtcGxlcyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxuLmJ0bi13cmFwcGVyIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IHJvdztcbiAgd2lkdGg6IDEwMCU7XG4gIGp1c3RpZnktY29udGVudDogc3BhY2UtYXJvdW5kO1xuICBwYWRkaW5nOiA4cHg7XG59XG4ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHtcbiAgU2FuZHBhY2tQcm92aWRlcixcbiAgU2FuZHBhY2tMYXlvdXQsXG4gIFNhbmRwYWNrQ29kZUVkaXRvcixcbiAgU2FuZHBhY2tQcmV2aWV3LFxufSBmcm9tIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXJlYWN0XCI7XG5pbXBvcnQgeyB1c2VTdGF0ZSB9IGZyb20gXCJyZWFjdFwiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIGNvbnN0IFttb2RlLCBzZXRNb2RlXSA9IHVzZVN0YXRlKFwiZGVmYXVsdFwiKTtcblxuICBjb25zdCBtb2RlcyA9IHtcbiAgICBkZWZhdWx0OiAoXG4gICAgICA8PlxuICAgICAgICA8U2FuZHBhY2tDb2RlRWRpdG9yIC8+XG4gICAgICAgIDxTYW5kcGFja1ByZXZpZXcgLz5cbiAgICAgIDwvPlxuICAgICksXG4gICAgZmxpcHBlZDogKFxuICAgICAgPD5cbiAgICAgICAgPFNhbmRwYWNrUHJldmlldyAvPlxuICAgICAgICA8U2FuZHBhY2tDb2RlRWRpdG9yIC8+XG4gICAgICA8Lz5cbiAgICApLFxuICAgIGVkaXRvcjogPFNhbmRwYWNrQ29kZUVkaXRvciAvPixcbiAgICBwcmV2aWV3OiA8U2FuZHBhY2tQcmV2aWV3IC8+LFxuICB9O1xuXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxkaXYgY2xhc3NOYW1lPVwiYnRuLXdyYXBwZXJcIj5cbiAgICAgICAgPGJ1dHRvbiBvbkNsaWNrPXsoKSA9PiBzZXRNb2RlKFwiZGVmYXVsdFwiKX0+RGVmYXVsdDwvYnV0dG9uPlxuICAgICAgICA8YnV0dG9uIG9uQ2xpY2s9eygpID0+IHNldE1vZGUoXCJmbGlwcGVkXCIpfT5GbGlwcGVkPC9idXR0b24+XG4gICAgICAgIDxidXR0b24gb25DbGljaz17KCkgPT4gc2V0TW9kZShcImVkaXRvclwiKX0+T25seSBFZGl0b3I8L2J1dHRvbj5cbiAgICAgICAgPGJ1dHRvbiBvbkNsaWNrPXsoKSA9PiBzZXRNb2RlKFwicHJldmlld1wiKX0+T25seSBQcmV2aWV3PC9idXR0b24+XG4gICAgICA8L2Rpdj5cblxuICAgICAgPFNhbmRwYWNrUHJvdmlkZXIgdGVtcGxhdGU9XCJyZWFjdFwiPlxuICAgICAgICA8U2FuZHBhY2tMYXlvdXQ+e21vZGVzW21vZGVdfTwvU2FuZHBhY2tMYXlvdXQ+XG4gICAgICA8L1NhbmRwYWNrUHJvdmlkZXI+XG4gICAgPC8+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIixcbiAgICBcIkBjb2Rlc2FuZGJveC9zYW5kcGFjay1yZWFjdFwiOiBcIjIuMTQuMFwiLFxuICAgIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXRoZW1lc1wiOiBcIjIuMC4yMVwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="750px" width="100%"></iframe>

<p>Each button in the example above changes how our playground looks. For example, the <strong>Flipped</strong> button swaps the order of the Editor and Preview components.</p>
<p>This is huge!</p>
<p>Sandpack's building blocks let you create any experience you can imagine.</p>
<h3 id="heading-how-to-build-a-custom-sandpack-component">How to build a custom Sandpack component.</h3>
<p>If Sandpack's built-in components aren't enough, you can build your own components too! Sandpack has several hooks you can use to build these custom components.</p>
<p>Let's say you don't like Sandpack's default code editor. No problem! With a little bit of code, you can swap it out for something else.</p>
<p>Here's an example of how to use the popular <a target="_blank" href="https://github.com/suren-atoyan/monaco-react#readme">Monaco editor</a>:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2dlMTdueHNoN3drZXRzdndyd3E2MXVmZiIsIm5hbWUiOiJTYW5kcGFjayBFeGFtcGxlcyAyIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG4uYnRuLXdyYXBwZXIge1xuICBkaXNwbGF5OiBmbGV4O1xuICBmbGV4LWRpcmVjdGlvbjogcm93O1xuICB3aWR0aDogMTAwJTtcbiAganVzdGlmeS1jb250ZW50OiBzcGFjZS1hcm91bmQ7XG4gIHBhZGRpbmc6IDhweDtcbn1cbiJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJpbXBvcnQge1xuICBTYW5kcGFja1Byb3ZpZGVyLFxuICBTYW5kcGFja0xheW91dCxcbiAgU2FuZHBhY2tDb2RlRWRpdG9yLFxuICBTYW5kcGFja1ByZXZpZXcsXG4gIFNhbmRwYWNrU3RhY2ssXG4gIHVzZUFjdGl2ZUNvZGUsXG4gIHVzZVNhbmRwYWNrLFxufSBmcm9tIFwiQGNvZGVzYW5kYm94L3NhbmRwYWNrLXJlYWN0XCI7XG5pbXBvcnQgRWRpdG9yIGZyb20gXCJAbW9uYWNvLWVkaXRvci9yZWFjdFwiO1xuXG5mdW5jdGlvbiBDdXN0b21FZGl0b3IoKSB7XG4gIGNvbnN0IHsgY29kZSwgdXBkYXRlQ29kZSB9ID0gdXNlQWN0aXZlQ29kZSgpO1xuICBjb25zdCB7IHNhbmRwYWNrIH0gPSB1c2VTYW5kcGFjaygpO1xuXG4gIHJldHVybiAoXG4gICAgPFNhbmRwYWNrU3RhY2s+XG4gICAgICA8RWRpdG9yXG4gICAgICAgIGxhbmd1YWdlPVwiamF2YXNjcmlwdFwiXG4gICAgICAgIHRoZW1lPVwidnMtZGFya1wiXG4gICAgICAgIGtleT17c2FuZHBhY2suYWN0aXZlRmlsZX1cbiAgICAgICAgZGVmYXVsdFZhbHVlPXtjb2RlfVxuICAgICAgICBvbkNoYW5nZT17KHZhbHVlKSA9PiB1cGRhdGVDb2RlKHZhbHVlIHx8IFwiXCIpfVxuICAgICAgLz5cbiAgICA8L1NhbmRwYWNrU3RhY2s+XG4gICk7XG59XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpIHtcbiAgcmV0dXJuIChcbiAgICA8U2FuZHBhY2tQcm92aWRlciB0ZW1wbGF0ZT1cInJlYWN0XCI+XG4gICAgICA8U2FuZHBhY2tMYXlvdXQ+XG4gICAgICAgIDxDdXN0b21FZGl0b3IgLz5cbiAgICAgICAgPFNhbmRwYWNrUHJldmlldyAvPlxuICAgICAgPC9TYW5kcGFja0xheW91dD5cbiAgICA8L1NhbmRwYWNrUHJvdmlkZXI+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIixcbiAgICBcIkBjb2Rlc2FuZGJveC9zYW5kcGFjay1yZWFjdFwiOiBcIjIuMTQuMFwiLFxuICAgIFwiQG1vbmFjby1lZGl0b3IvcmVhY3RcIjogXCI0LjYuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="750px" width="100%"></iframe>

<p>Sandpack's <code>useActiveCode</code> hook returns the current code and a function to update the code. We pass both to the Monaco editor which uses them to update Sandpack's internal state which is reflected in the Preview.</p>
<p>We're also using the <code>useSandpack</code> hook to provide a key to the Monaco editor so that the editor state is reset when the file changes.</p>
<h3 id="heading-how-am-i-using-sandpack-in-my-blog">How am I using Sandpack in my blog?</h3>
<p>This may be obvious, but all the Sandpack examples above require us to write React code. If you have control over your blog's code, installing and using Sandpack is straightforward. However, if your blog is hosted on a platform like Hashnode (like this blog), you won't be able to write code directly in a post.</p>
<p>So how am I able to get around this?</p>
<p>That's where <a target="_blank" href="https://kempo.io/">Kempo</a> comes into play.</p>
<p>I wanted a way to use Sandpack in my own blog, so I built an application that lets you configure Sandpack instances. All the examples above are Sandpack instances that are demoing Sandpack.</p>
<h3 id="heading-in-other-words-its-sandpack-instances-all-the-way-down">In other words, it's Sandpack instances all the way down!</h3>
<p>I know what you're thinking.</p>
<p>Aren't we right back to using something like CodeSandbox to embed code playgrounds into our blogs?</p>
<p>I will admit that this is somewhat true, but there are some major differences. Kempo is built around configuring <em>Sandpack</em>. The goal is to use Sandpack's features without having to write code to configure and style Sandpack directly.</p>
<p>Just like Hashnode takes away the need to build a blogging platform so you can focus on your content, Kempo takes away the need to manually configure Sandpack so you can focus on your demos.</p>
<p>There are still improvements to be made, but for now, I think these are the main benefits:</p>
<ol>
<li><p>Lightweight</p>
</li>
<li><p>Custom themes</p>
</li>
<li><p>Flexible components</p>
</li>
<li><p>Custom layouts for different screens</p>
</li>
</ol>
<blockquote>
<p>Note: You can learn more in <a target="_blank" href="https://blog.thesshguy.com/kempo-introduction">my post where I introduce Kempo</a>.</p>
</blockquote>
<p>If you can use Sandpack directly in your blog then that's great, but if you're writing on a platform like Hashnode, Kempo <em>might</em> be a good fit.</p>
<p>If you do try using Kempo, let me know how it goes!</p>
<p>Either way, I hope this post gets you excited about Sandpack!</p>
]]></content:encoded></item><item><title><![CDATA[How to Use CloudFlare to Redirect www to Non-www]]></title><description><![CDATA[Have you ever needed to make sure your website works with both www and non-www versions?
I did too!
My website is served at https://thesshguy.com, but I also wanted https://www.thesshguy.com to work.
The Problem with Two Versions
The first thing I tr...]]></description><link>https://blog.thesshguy.com/cloudflare-redirect-rules</link><guid isPermaLink="true">https://blog.thesshguy.com/cloudflare-redirect-rules</guid><category><![CDATA[cloudflare]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[SEO]]></category><category><![CDATA[SEO for Developers]]></category><category><![CDATA[Web Hosting]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Fri, 31 May 2024 14:04:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1717098871815/bd94698e-3fe4-4d7c-8873-4e61a580fe39.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Have you ever needed to make sure your website works with both www and non-www versions?</p>
<p>I did too!</p>
<p>My website is served at <a target="_blank" href="https://thesshguy.com">https://thesshguy.com</a>, but I also wanted <a target="_blank" href="https://www.thesshguy.com">https://www.thesshguy.com</a> to work.</p>
<h3 id="heading-the-problem-with-two-versions">The Problem with Two Versions</h3>
<p>The first thing I tried was to add <code>A</code> and <code>AAAA</code> DNS records for both versions. While that does <em>work</em>, there's a problem with this approach. Google and other search engines get confused because it looks like there are two versions of my website.</p>
<p>When <a target="_blank" href="https://blog.thesshguy.com/google-search-indexing#heading-google-analyzes-content-and-collects-signals">Google tries to index my website, there's no clear answer to which is the canonical version</a>.</p>
<p>So what can we do instead?</p>
<h3 id="heading-improve-things-with-redirects">Improve Things with Redirects</h3>
<p>Since we have two versions, we can redirect one to the other to improve things. This tells web crawlers which version is the main one. I used a Cloudflare rule to redirect the www version to the non-www version.</p>
<blockquote>
<p>Note: I had to include DNS records and add a redirect rule for things to work correctly.</p>
</blockquote>
<p>Here's how you can do it too.</p>
<h3 id="heading-steps-to-set-up-the-redirect">Steps to Set Up the Redirect</h3>
<blockquote>
<p>Note: Make sure to use your own website in place of mine.</p>
</blockquote>
<ol>
<li><p>Log into your Cloudflare account</p>
</li>
<li><p>Go to <strong>Rules &gt; Redirect Rules</strong></p>
<p> <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1717096501580/2d4966a1-3488-4185-a44f-41f3de40a15e.png" alt="A menu displaying various rule options including &quot;Rules,&quot; &quot;Configuration Rules,&quot; &quot;Transform Rules,&quot; &quot;Redirect Rules&quot; (highlighted), &quot;Origin Rules,&quot; &quot;Page Rules&quot; (marked as Deprecated), and &quot;Settings.&quot;" class="image--center mx-auto" /></p>
</li>
<li><p>Click <strong>Create rule</strong> under Single Redirects</p>
</li>
<li><p>Name your rule (I named mine "<strong>WWW to Non WWW</strong>")</p>
</li>
<li><p>Set up a custom filter like this:</p>
<ul>
<li><p>Field: <code>Hostname</code></p>
</li>
<li><p>Operator: <code>equals</code></p>
</li>
<li><p>Value: <code>www.thesshguy.com</code></p>
</li>
<li><p>URL redirect Type: <code>dynamic</code></p>
</li>
<li><p>Expression: <code>concat("https://thesshguy.com", http.request.uri.path)</code></p>
</li>
<li><p>Status Code: <code>301</code> (permanent redirect)</p>
</li>
</ul>
</li>
</ol>
<p>Here's what my rule looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1717097047277/4023efe3-05d2-4375-9564-64fe26f29243.png" alt="Screenshot of a web interface for creating a new dynamic redirect rule. The rule is named &quot;WWW to Non WWW&quot;. The interface includes options for matching incoming requests, with a custom filter expression set to match the hostname &quot;www.thesshguy.com&quot;. The redirect type is dynamic, with an expression to concatenate &quot;https://thesshguy.com&quot; with the request URI path. The status code for the redirect is set to 301." class="image--center mx-auto" /></p>
<p>And that's it!</p>
<p>Now your www version will redirect to the non-www version. Hopefully, this helps you set up your own Cloudflare redirects!</p>
]]></content:encoded></item><item><title><![CDATA[A Complete Guide to Managing Arrays with React's useState Hook]]></title><description><![CDATA[The introduction of hooks made state management much easier in React.
With the useState hook, you're given a tuple where the first element is the current state and the second element is a function used to update that state. For primitive types like s...]]></description><link>https://blog.thesshguy.com/react-usestate-arrays</link><guid isPermaLink="true">https://blog.thesshguy.com/react-usestate-arrays</guid><category><![CDATA[React]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[ReactHooks]]></category><category><![CDATA[State Management ]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Thu, 30 May 2024 14:13:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1717045428442/568fb83b-1616-4cf1-a121-a97cebea59a7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>The introduction of hooks made state management much easier in React.</p>
<p>With the <code>useState</code> hook, you're given a tuple where the first element is the current state and the second element is a function used to update that state. For primitive types like strings, using this hook to manage the state is straightforward.</p>
<p>But what about complex data types, like <em>arrays</em>?</p>
<p>The main thing to remember when working with arrays in React is that you don't want to <em>mutate</em> the array. React can't detect the change, so your UI won't update how you expect it to:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2lyYTR5Mmhjdmx6MWllazdjdnQxNnN0byIsIm5hbWUiOiJSZWFjdCB1c2VTdGF0ZSBBcnJheSBFeGFtcGxlIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG4uYnRuIHtcbiAgQGFwcGx5IGZvbnQtYm9sZCBweS0yIHB4LTQgcm91bmRlZDtcbn1cbi5idG4tYmx1ZSB7XG4gIEBhcHBseSBiZy1ibHVlLTUwMCB0ZXh0LXdoaXRlO1xufVxuLmJ0bi1ibHVlOmhvdmVyIHtcbiAgQGFwcGx5IGJnLWJsdWUtNzAwO1xufVxuIn0sIi9BcHAuanMiOnsiY29kZSI6ImltcG9ydCB7IHVzZVN0YXRlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBCdXR0b24gfSBmcm9tIFwiLi9CdXR0b25cIjtcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICBjb25zdCBbbGlzdCwgc2V0TGlzdF0gPSB1c2VTdGF0ZShbMV0pO1xuXG4gIHJldHVybiAoXG4gICAgPGRpdiBjbGFzc05hbWU9XCJmbGV4IGZsZXgtY29sIGdhcC0yXCI+XG4gICAgICA8c3BhbiBjbGFzc05hbWU9XCJ0ZXh0LWNlbnRlciB0ZXh0LWxnXCI+e2xpc3RbMF19PC9zcGFuPlxuXG4gICAgICA8ZGl2IGNsYXNzTmFtZT1cImZsZXgganVzdGlmeS1jZW50ZXIgZ2FwLTRcIj5cbiAgICAgICAgPEJ1dHRvblxuICAgICAgICAgIG9uQ2xpY2s9eygpID0+IHtcbiAgICAgICAgICAgIGxpc3RbMF0gPSBsaXN0WzBdICogMjtcbiAgICAgICAgICAgIHNldExpc3QobGlzdCk7XG4gICAgICAgICAgfX1cbiAgICAgICAgPlxuICAgICAgICAgIE11dGF0ZSBBcnJheSAoQmFkKVxuICAgICAgICA8L0J1dHRvbj5cblxuICAgICAgICA8QnV0dG9uXG4gICAgICAgICAgb25DbGljaz17KCkgPT4ge1xuICAgICAgICAgICAgc2V0TGlzdChbbGlzdFswXSAqIDJdKTtcbiAgICAgICAgICB9fVxuICAgICAgICA+XG4gICAgICAgICAgVXBkYXRlIEFycmF5IChHb29kKVxuICAgICAgICA8L0J1dHRvbj5cbiAgICAgIDwvZGl2PlxuICAgIDwvZGl2PlxuICApO1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IFwiLi9zdHlsZXMuY3NzXCI7XG5cbmltcG9ydCBBcHAgZnJvbSBcIi4vQXBwXCI7XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+XG4pOyJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjUuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In0sIi9CdXR0b24udHN4Ijp7ImNvZGUiOiJleHBvcnQgY29uc3QgQnV0dG9uID0gKHByb3BzKSA9PiAoXG4gIDxidXR0b25cbiAgICB7Li4ucHJvcHN9XG4gICAgY2xhc3NOYW1lPVwiYmctc2t5LTUwMCBob3ZlcjpiZy1za3ktNzAwIHRleHQtc2xhdGUtMTAwIGZvbnQtYm9sZCBweS0yIHB4LTQgcm91bmRlZFwiXG4gIC8+XG4pO1xuIn19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOnRydWUsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOnRydWUsIndyYXBDb250ZW50Ijp0cnVlLCJjbG9zYWJsZVRhYnMiOnRydWUsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOnRydWUsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2V9fX0sImdlbmVyYWwiOnsidmlzaWJsZUZpbGVzIjpbIi9BcHAuanMiXSwidGhlbWUiOiJrZW1wb0RhcmsiLCJleHRlcm5hbFJlc291cmNlcyI6WyJodHRwczovL2Nkbi50YWlsd2luZGNzcy5jb20iXX0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ0VkaXRvcidcbidFZGl0b3InXG4nUHJldmlldycifX19" height="750px" width="100%"></iframe>

<p>In this example, each button is supposed to double the value shown. But when you click the <strong>Mutate Array</strong> button, nothing seems to happen. This is because we're mutating the state and then calling <code>setList</code> with the same array <em>reference</em>. Because the reference is the <em>same</em>, React <em>thinks</em> the array hasn't changed and bails out of the re-render.</p>
<p>This is an optimization that React does internally where if you pass the <em>same</em> state as the current state to the updater function, React ignores the update.</p>
<blockquote>
<p>Note: If you click the Mutate Button a few times and then click the Update Button, you'll notice that the value more than doubles. This is because both buttons reference the same list variable.</p>
<p>Technically the Mutate Button does update the value, React just doesn't know about it until the Update Button is clicked and setList is called with a new array reference.</p>
</blockquote>
<p>So how do you handle this?</p>
<h2 id="heading-when-updating-arrays-in-react-state-create-a-new-array">When updating arrays in React state, create a new array.</h2>
<p>Instead of mutating the existing array, use it as the starting point to create a new array.</p>
<p>Let's take a look at a few examples:</p>
<h3 id="heading-how-to-add-an-element-to-an-array">How to add an element to an array</h3>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ListOfNumbers</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [list, setList] = useState([<span class="hljs-number">1</span>]);
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            {/**
              * It's not a good practice to use the index
              * as the key, but hey this is just an example.
              */}
            {list.map((item, index) =&gt; <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>&gt;</span>{item}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>)}
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setList([...list, Math.random()])}&gt;
                Add Item
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>In this example, when you click the button element, we:</p>
<ol>
<li><p>Create a new list</p>
</li>
<li><p><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax">Spread the existing list into the new list</a></p>
</li>
<li><p>Append a random number to the end of the list</p>
</li>
</ol>
<p>The spread operator is a quick way to create a new array using the values of an existing array.</p>
<h3 id="heading-how-to-remove-an-element-from-a-list">How to remove an element from a list</h3>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ListOfNumbers</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [list, setList] = useState([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]);
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            {list.map((item, index) =&gt; <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>&gt;</span>{item}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>)}
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setList(list.slice(1))}&gt;
                Remove First Item
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>This time when the button is clicked, we <em>slice</em> off the first element. The <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice">slice array method</a> returns a <em>copy</em> of the original array, which makes it safe to use with React state.</p>
<blockquote>
<p>Note: On the other hand, the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice">splice array method</a><em>mutates</em> the existing array and is therefore not a good fit for React state management.</p>
</blockquote>
<h3 id="heading-how-to-update-elements-in-an-array">How to update elements in an array</h3>
<p>Let's say we have a list of numbers and want to double each array element when you click a button. In this case, we can use the <code>map</code> function to handle this:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ListOfNumbers</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [list, setList] = useState([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]);
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            {list.map((item, index) =&gt; <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>&gt;</span>{item}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>)}
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setList(list.map(item =&gt; item * 2))}&gt;
                Double Items
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>What if we wanted to update a <em>specific</em> array element? Luckily you can use the <code>map</code> function for this too!</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ListOfNumbers</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [list, setList] = useState([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]);
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            {list.map((item, index) =&gt; <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>&gt;</span>{item}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>)}
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
                setList(list.map(
                    (item, index) =&gt; index === 2 ? item * 2 : item
                ));
            }}&gt;
                Double Items
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>Notice how we're checking the <code>index</code> in our <code>map</code> function and we only double the item if it's the 3rd element (arrays start with the index <code>0</code>).</p>
<h3 id="heading-how-to-safely-mutate-an-array">How to safely mutate an array</h3>
<p>Let's say you're trying to update your array and the easiest option to solve your problem is to reach for a built-in utility that mutates your array. For example, maybe you want to reverse the order of your array using the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse">reverse array method</a>. Since I said don't <em>mutate</em> your arrays, what should you do?</p>
<p><strong>Make a copy of your array first!</strong></p>
<p>The easiest thing to do is to use the spread operator to create a new copy of the array <em>first</em>. After you make the copy, you're free to mutate it however you want:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ListOfNumbers</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [list, setList] = useState([<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>]);
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            {list.map((item, index) =&gt; <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>&gt;</span>{item}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>)}
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
                const listCopy = [...list];
                listCopy.reverse(); // It's safe to mutate the copy
                setList(listCopy);
            }}&gt;
                Reverse Items
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>While the approach above will work in most cases, there's one major flaw.</p>
<p>The spread operator only creates a <em>shallow</em> clone of the original array. In other words, if you have an array of <em>objects</em>, each object within the cloned array will still reference the same object as the original array.</p>
<p><strong>So how do you update objects within an array?</strong></p>
<p>You do the same thing you did to update the array. You make a <em>copy</em> of the object you want to update using the spread operator:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ListOfPeople</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> [list, setList] = useState([
        { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'Jack'</span> },
        { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'Jill'</span> }
    ]);

    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
            {list.map((item) =&gt; <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{item.id}</span>&gt;</span>{item.name}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>)}
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> {
                // This creates a new object for each item,
                // copies the old object, and updates the name.
                setList(list.map(item =&gt; ({
                    ...item,
                    name: item.name.toUpperCase()
                })));
            }}&gt;
                Capitalize Names
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    );
}
</code></pre>
<p>Awesome!</p>
<p>Now let's put all that together and take a look at a live running example:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2lyYTR5Mmhjdmx6MWllazdjdnQxNnN0byIsIm5hbWUiOiJSZWFjdCB1c2VTdGF0ZSBBcnJheSBFeGFtcGxlIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG4uYnRuIHtcbiAgQGFwcGx5IGZvbnQtYm9sZCBweS0yIHB4LTQgcm91bmRlZDtcbn1cbi5idG4tYmx1ZSB7XG4gIEBhcHBseSBiZy1ibHVlLTUwMCB0ZXh0LXdoaXRlO1xufVxuLmJ0bi1ibHVlOmhvdmVyIHtcbiAgQGFwcGx5IGJnLWJsdWUtNzAwO1xufVxuIn0sIi9BcHAuanMiOnsiY29kZSI6ImltcG9ydCB7IHVzZVN0YXRlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBCdXR0b24sIEljb25CdXR0b24gfSBmcm9tIFwiLi9CdXR0b25cIjtcblxubGV0IGlkID0gMTtcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICBjb25zdCBbY291bnRlcnMsIHNldENvdW50ZXJzXSA9IHVzZVN0YXRlKFt7IGlkOiAwLCB2YWx1ZTogMCB9XSk7XG5cbiAgcmV0dXJuIChcbiAgICA8ZGl2IGNsYXNzTmFtZT1cImZsZXggZmxleC1jb2wgZ2FwLTIgcC00XCI+XG4gICAgICA8ZGl2IGNsYXNzTmFtZT1cImZsZXgganVzdGlmeS1jZW50ZXIgZ2FwLTRcIj5cbiAgICAgICAgPEJ1dHRvblxuICAgICAgICAgIG9uQ2xpY2s9eygpID0+IHNldENvdW50ZXJzKFsuLi5jb3VudGVycywgeyBpZDogaWQrKywgdmFsdWU6IDAgfV0pfVxuICAgICAgICA+XG4gICAgICAgICAgQWRkIENvdW50ZXJcbiAgICAgICAgPC9CdXR0b24+XG5cbiAgICAgICAgPEJ1dHRvbiBvbkNsaWNrPXsoKSA9PiBzZXRDb3VudGVycyhjb3VudGVycy5zbGljZSgxKSl9PlxuICAgICAgICAgIFJlbW92ZSBGaXJzdCBDb3VudGVyXG4gICAgICAgIDwvQnV0dG9uPlxuXG4gICAgICAgIDxCdXR0b25cbiAgICAgICAgICBvbkNsaWNrPXsoKSA9PiB7XG4gICAgICAgICAgICAvLyBDcmVhdGUgYSBjbG9uZSBvZiB0aGUgY291bnRlcnNcbiAgICAgICAgICAgIC8vIGFycmF5IGJlZm9yZSByZXZlcnNpbmcgaXQuXG4gICAgICAgICAgICBjb25zdCBjb3VudGVyc0Nsb25lID0gWy4uLmNvdW50ZXJzXTtcbiAgICAgICAgICAgIGNvdW50ZXJzQ2xvbmUucmV2ZXJzZSgpO1xuICAgICAgICAgICAgc2V0Q291bnRlcnMoY291bnRlcnNDbG9uZSk7XG4gICAgICAgICAgfX1cbiAgICAgICAgPlxuICAgICAgICAgIFJldmVyc2UgQ291bnRlcnNcbiAgICAgICAgPC9CdXR0b24+XG4gICAgICA8L2Rpdj5cbiAgICAgIDx1bCBjbGFzc05hbWU9XCJmbGV4IGZsZXgtY29sIGdhcC0yXCI+XG4gICAgICAgIHtjb3VudGVycy5tYXAoKGNvdW50ZXIsIGluZGV4KSA9PiAoXG4gICAgICAgICAgPGxpXG4gICAgICAgICAgICBjbGFzc05hbWU9XCJmbGV4IGdhcC0yIGl0ZW1zLWNlbnRlciBqdXN0aWZ5LWNlbnRlclwiXG4gICAgICAgICAgICBrZXk9e2NvdW50ZXIuaWR9XG4gICAgICAgICAgPlxuICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJ0ZXh0LWxnXCI+XG4gICAgICAgICAgICAgIENvdW50ZXIgKHtjb3VudGVyLmlkfSkge2NvdW50ZXIudmFsdWV9XG4gICAgICAgICAgICA8L3NwYW4+XG5cbiAgICAgICAgICAgIDxJY29uQnV0dG9uXG4gICAgICAgICAgICAgIG9uQ2xpY2s9eygpID0+IHtcbiAgICAgICAgICAgICAgICAvLyBDcmVhdGUgYSBjbG9uZSBvZiB0aGUgY291bnRlclxuICAgICAgICAgICAgICAgIC8vIG9iamVjdCB3ZSB3YW50IHRvIHVwZGF0ZS5cbiAgICAgICAgICAgICAgICBzZXRDb3VudGVycyhcbiAgICAgICAgICAgICAgICAgIGNvdW50ZXJzLm1hcCgoYywgaSkgPT5cbiAgICAgICAgICAgICAgICAgICAgaSA9PT0gaW5kZXggPyB7IC4uLmMsIHZhbHVlOiBjLnZhbHVlICsgMSB9IDogYyxcbiAgICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgfX1cbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgKyBJbmNyZW1lbnRcbiAgICAgICAgICAgIDwvSWNvbkJ1dHRvbj5cbiAgICAgICAgICA8L2xpPlxuICAgICAgICApKX1cbiAgICAgIDwvdWw+XG4gICAgPC9kaXY+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifSwiL0J1dHRvbi50c3giOnsiY29kZSI6ImV4cG9ydCBjb25zdCBCdXR0b24gPSAocHJvcHMpID0+IChcbiAgPGJ1dHRvblxuICAgIHsuLi5wcm9wc31cbiAgICBjbGFzc05hbWU9XCJiZy1za3ktNTAwIGhvdmVyOmJnLXNreS03MDAgdGV4dC1zbGF0ZS0xMDAgZm9udC1ib2xkIHB5LTIgcHgtNCByb3VuZGVkXCJcbiAgLz5cbik7XG5cbmV4cG9ydCBjb25zdCBJY29uQnV0dG9uID0gKHByb3BzKSA9PiAoXG4gIDxidXR0b25cbiAgICB7Li4ucHJvcHN9XG4gICAgY2xhc3NOYW1lPVwiYmctZ3JheS0zMDAgaG92ZXI6YmctZ3JheS00MDAgdGV4dC1ncmF5LTgwMCBmb250LWJvbGQgcHktMiBweC00IHJvdW5kZWQgaW5saW5lLWZsZXggaXRlbXMtY2VudGVyXCJcbiAgLz5cbik7XG4ifX0sInRlbXBsYXRlIjoicmVhY3QifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6dHJ1ZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6dHJ1ZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6dHJ1ZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImtlbXBvRGFyayIsImV4dGVybmFsUmVzb3VyY2VzIjpbImh0dHBzOi8vY2RuLnRhaWx3aW5kY3NzLmNvbSJdfSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nRWRpdG9yJ1xuJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0=" height="750px" width="100%"></iframe>]]></content:encoded></item><item><title><![CDATA[React useState Callbacks: How to Run Code After Updating State]]></title><description><![CDATA[The useState hook is a basic building block for state management in React.
Using the hook is pretty straightforward. The function returns a tuple with the current state and a function to update the state. Here's a basic example:


In this example, we...]]></description><link>https://blog.thesshguy.com/react-usestate-callbacks</link><guid isPermaLink="true">https://blog.thesshguy.com/react-usestate-callbacks</guid><category><![CDATA[React]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[react hooks]]></category><category><![CDATA[useEffect]]></category><category><![CDATA[useState]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Tue, 28 May 2024 20:34:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716928268987/129029d6-ee3d-427b-b644-0278bc17830c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>The <code>useState</code> hook is a basic building block for state management in React.</p>
<p>Using the hook is pretty straightforward. The function returns a tuple with the current state and a function to update the state. Here's a basic example:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX212YWJzbTQ3Y3Qwcmhvb3RpZHVlM3NqYiIsIm5hbWUiOiJSZWFjdCB1c2VTdGF0ZSBFeGFtcGxlcyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxubGFiZWwge1xuICBkaXNwbGF5OiBmbGV4O1xuICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICBtYXgtd2lkdGg6IDIwMHB4O1xuICBnYXA6IDJweDtcbn1cbiJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJpbXBvcnQgeyB1c2VTdGF0ZSB9IGZyb20gXCJyZWFjdFwiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIGNvbnN0IFtjb21tZW50LCBzZXRDb21tZW50XSA9IHVzZVN0YXRlKFwiXCIpO1xuXG4gIHJldHVybiAoXG4gICAgPGRpdj5cbiAgICAgIDxsYWJlbD5cbiAgICAgICAgQ29tbWVudFxuICAgICAgICA8aW5wdXRcbiAgICAgICAgICB0eXBlPVwidGV4dFwiXG4gICAgICAgICAgdmFsdWU9e2NvbW1lbnR9XG4gICAgICAgICAgb25DaGFuZ2U9eyhlKSA9PiBzZXRDb21tZW50KGUudGFyZ2V0LnZhbHVlKX1cbiAgICAgICAgLz5cbiAgICAgIDwvbGFiZWw+XG4gICAgPC9kaXY+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifX0sInRlbXBsYXRlIjoicmVhY3QifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6dHJ1ZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6dHJ1ZSwicmVhZE9ubHkiOnRydWUsInNob3dSZWFkT25seSI6dHJ1ZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOmZhbHNlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0=" height="750px" width="100%"></iframe>

<p>In this example, we're managing state for a <code>comment</code> variable. We use the state to populate the input and then use the <code>setComment</code> state updater to <em>update</em> the state when the input changes (i.e. you type into the input field).</p>
<p>Ok!</p>
<p>So managing state seems pretty easy, but what if you want to run some code whenever your state changes? Luckily React has you covered!</p>
<h3 id="heading-the-useeffect-hook-runs-when-its-dependencies-change">The useEffect hook runs when its dependencies change</h3>
<p>We can use the <code>useEffect</code> hook for this exact purpose. The hook takes a list of dependencies as its second argument and executes the effect whenever a dependency changes.</p>
<p>In other words, you can pass the state variable to the dependency list and execute your code in the effect.</p>
<p>Here's what that might look like:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX212YWJzbTQ3Y3Qwcmhvb3RpZHVlM3NqYiIsIm5hbWUiOiJSZWFjdCB1c2VTdGF0ZSBFeGFtcGxlcyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxubGFiZWwge1xuICBkaXNwbGF5OiBmbGV4O1xuICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICBtYXgtd2lkdGg6IDIwMHB4O1xuICBnYXA6IDJweDtcbn1cbiJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJpbXBvcnQgeyB1c2VTdGF0ZSwgdXNlRWZmZWN0IH0gZnJvbSBcInJlYWN0XCI7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpIHtcbiAgY29uc3QgW2NvbW1lbnQsIHNldENvbW1lbnRdID0gdXNlU3RhdGUoXCJcIik7XG5cbiAgdXNlRWZmZWN0KCgpID0+IHtcbiAgICBjb25zb2xlLmxvZyhgQ29tbWVudDogJHtjb21tZW50fWApO1xuICB9LCBbY29tbWVudF0pO1xuXG4gIHJldHVybiAoXG4gICAgPGRpdj5cbiAgICAgIDxsYWJlbD5cbiAgICAgICAgQ29tbWVudFxuICAgICAgICA8aW5wdXRcbiAgICAgICAgICB0eXBlPVwidGV4dFwiXG4gICAgICAgICAgdmFsdWU9e2NvbW1lbnR9XG4gICAgICAgICAgb25DaGFuZ2U9eyhlKSA9PiBzZXRDb21tZW50KGUudGFyZ2V0LnZhbHVlKX1cbiAgICAgICAgLz5cbiAgICAgIDwvbGFiZWw+XG4gICAgPC9kaXY+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QgZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoPEFwcCAvPik7XG4ifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6dHJ1ZSwic2hvd1JlYWRPbmx5Ijp0cnVlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6ZmFsc2UsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2V9fSwiQ29uc29sZSI6eyJ0eXBlIjoiY29uc29sZSIsImRhdGEiOnsic2hvd0hlYWRlciI6dHJ1ZSwic2hvd1N5bnRheEVycm9yIjpmYWxzZSwic2hvd1NldHVwUHJvZ3Jlc3MiOmZhbHNlLCJtYXhNZXNzYWdlQ291bnQiOjEsInJlc2V0T25QcmV2aWV3UmVzdGFydCI6ZmFsc2UsInN0YW5kYWxvbmUiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yIEVkaXRvcidcbidFZGl0b3IgRWRpdG9yJ1xuJ1ByZXZpZXcgQ29uc29sZScifX19" height="750px" width="100%"></iframe>

<p>This time, as you type into the input above, there's an effect that runs anytime the <code>comment</code> variable updates. This effect logs the input field's value every time it changes. This is a pretty basic example, but it shows you how to run code when your state changes.</p>
<p>Combining <code>useState</code> and <code>useEffect</code> in React can be super helpful!</p>
<p>These hooks make it easy to manage state and run code when things change. With these tools, you can build apps that work well and do what you expect. It's simple and effective!</p>
]]></content:encoded></item><item><title><![CDATA[The Basics of Google Search Indexing Explained]]></title><description><![CDATA[If you want to have effective SEO, it's important to understand how Google Search indexes your web pages.
Before indexing a page, Google needs to crawl your website.
Google uses bots called crawlers to find pages to index. Once the crawler finds your...]]></description><link>https://blog.thesshguy.com/google-search-indexing</link><guid isPermaLink="true">https://blog.thesshguy.com/google-search-indexing</guid><category><![CDATA[Google]]></category><category><![CDATA[SEO]]></category><category><![CDATA[SEO for Developers]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Thu, 23 May 2024 14:12:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716240232243/e5e3846e-0540-4abc-97ba-7b3b312bee8e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you want to have effective SEO, it's important to understand <em>how</em> Google Search indexes your web pages.</p>
<h3 id="heading-before-indexing-a-page-google-needs-to-crawl-your-website">Before indexing a page, Google needs to crawl your website.</h3>
<p>Google uses <a target="_blank" href="https://blog.thesshguy.com/how-google-crawlers-work">bots called crawlers</a> to find pages to index. Once the crawler finds your page, the next step is to index it.</p>
<p>During the indexing process, Google parses your page's HTML. During this step, the HTML is inspected for semantic issues or invalid HTML tags. These issues <em>might</em> be fixed, but in some cases, HTML tags might be dropped entirely if they're in the wrong place.</p>
<p>With this said, it's important to keep in mind that properly structured HTML is essential for effective indexing.</p>
<h3 id="heading-google-analyzes-content-and-collects-signals">Google analyzes content and collects signals</h3>
<p>The indexing process also involves steps to analyze the textual content, key content tags, attributes, images, and videos on your page. Google also collects signals that help determine your page's relevance and ranking in search results. Some of these signals include:</p>
<ul>
<li><p>Is a web page the canonical page</p>
</li>
<li><p>What language is the page in</p>
</li>
<li><p>How useable is the page</p>
</li>
</ul>
<p><strong>A canonical page is used to represent a group of duplicate pages.</strong></p>
<p>Sometimes you might have duplicate or similar content on several pages. Google groups these pages through a process known as Duplicate Clustering and chooses one to be the canonical version that represents this cluster. Google indexes this canonical version, while the other versions may still be served in different contexts.</p>
<p><strong>The quality of your page's content is key.</strong></p>
<p>Once duplicate web pages are handled, Google will inspect the quality of the content on the page it's trying to index. If the quality of the content is poor, then Google may not index the page. In other words, indexing is not guaranteed. Only high-quality canonical pages are indexed.</p>
<p>Outside of ensuring your website has high-quality content, your pages might not be indexed based on <em>other</em> technical requirements. To be eligible for indexing, the following must be true for your page:</p>
<ul>
<li><p>The Googlebot crawler must not be blocked</p>
</li>
<li><p>It must return an HTTP <code>200</code> status code</p>
</li>
<li><p>The content must be indexable</p>
</li>
</ul>
<h3 id="heading-your-page-might-be-removed-for-spam-policy-violations">Your page might be removed for spam policy violations.</h3>
<p>It's important to ensure your content complies with Google's spam policies. The following can all get your pages removed:</p>
<ul>
<li><p><a target="_blank" href="https://developers.google.com/search/docs/essentials/spam-policies#cloaking">Cloaking</a>: presenting different content to users and search engines</p>
</li>
<li><p><a target="_blank" href="https://developers.google.com/search/docs/essentials/spam-policies#keyword-stuffing">Keyword Stuffing</a>: filling a page with keywords or numbers to manipulate your ranking in Google Search</p>
</li>
<li><p><a target="_blank" href="https://developers.google.com/search/docs/essentials/spam-policies#sneaky-redirects">Sneaky Redirects</a>: redirecting users or search engines unexpectedly to show them different content than what they intended to see</p>
</li>
</ul>
<p>Indexing is an important part of the SEO process. Understanding how to optimize this process, ensuring you produce high-quality content, and following best practices can improve your site's ranking on Google Search.</p>
]]></content:encoded></item><item><title><![CDATA[A Beginner's Guide to React 19's New use API]]></title><description><![CDATA[One of the new features in React 19 is the use API.
The basic idea is that the use API can fetch resources during React's render cycle. Instead of doing something like fetching data in an effect and updating the component state, we can get the data d...]]></description><link>https://blog.thesshguy.com/react-use-api</link><guid isPermaLink="true">https://blog.thesshguy.com/react-use-api</guid><category><![CDATA[React]]></category><category><![CDATA[React 19]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[webdev]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Wed, 22 May 2024 14:07:42 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716324897307/8a8dcdd5-48cc-45f5-940a-bd1f39f4f84c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>One of the new features in React 19 is the <code>use</code> API.</p>
<p>The basic idea is that the <code>use</code> API can fetch resources <em>during</em> React's render cycle. Instead of doing something like fetching data in an effect and updating the component state, we can get the data directly via the <code>use</code> API.</p>
<p>When paired with React's <code>Suspense</code> component, we can easily manage conditional rendering:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3Vvd2YxdXc3aDdqZmk0Y2pxNWllcXByZyIsIm5hbWUiOiJSZWFjdCAxOSB1c2UgQVBJIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xuICBwYWRkaW5nOiAycmVtO1xufVxuXG5oMSB7XG4gIGZvbnQtc2l6ZTogMS41cmVtO1xufVxuXG5oMiB7XG4gIGZvbnQtc2l6ZTogMS4yNXJlbTtcbn1cblxuZm9ybSB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGdhcDogMC41cmVtO1xufVxuXG5mb3JtIGJ1dHRvbiB7XG4gIGFsaWduLXNlbGY6IGZsZXgtZW5kO1xufVxuXG5sYWJlbCB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gIGdhcDogMC4xMjVyZW07XG59XG5cbi5wb3N0LXdyYXBwZXIsXG4uY29tbWVudC13cmFwcGVyIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbn1cblxuLnBvc3Qtd3JhcHBlciBkaXYge1xuICBkaXNwbGF5OiBmbGV4O1xuICBhbGlnbi1pdGVtczogYmFzZWxpbmU7XG4gIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2Vlbjtcbn1cbiJ9LCIvdHNjb25maWcuanNvbiI6eyJjb2RlIjoie1xuICBcImluY2x1ZGVcIjogW1xuICAgIFwiLi8qKi8qXCJcbiAgXSxcbiAgXCJjb21waWxlck9wdGlvbnNcIjoge1xuICAgIFwic3RyaWN0XCI6IHRydWUsXG4gICAgXCJlc01vZHVsZUludGVyb3BcIjogdHJ1ZSxcbiAgICBcImxpYlwiOiBbIFwiZG9tXCIsIFwiZXMyMDE1XCIgXSxcbiAgICBcImpzeFwiOiBcInJlYWN0LWpzeFwiXG4gIH1cbn0ifSwiL0FwcC50c3giOnsiY29kZSI6ImltcG9ydCB7IHVzZSwgU3VzcGVuc2UgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IFBvc3QgfSBmcm9tIFwiLi91dGlsc1wiO1xuXG5leHBvcnQgZnVuY3Rpb24gQ29tbWVudHMoeyBnZXRDb21tZW50c1Byb21pc2UgfSkge1xuICBjb25zdCBjb21tZW50cyA9IHVzZShnZXRDb21tZW50c1Byb21pc2UpO1xuXG4gIHJldHVybiBjb21tZW50cy5tYXAoKGNvbW1lbnQpID0+IDxwIGtleT17Y29tbWVudC5pZH0+e2NvbW1lbnQuY29tbWVudH08L3A+KTtcbn1cblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKHsgZ2V0Q29tbWVudHNQcm9taXNlIH0pOiBKU1guRWxlbWVudCB7XG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxQb3N0IC8+XG4gICAgICA8ZGl2IGNsYXNzTmFtZT1cImNvbW1lbnRzLXdyYXBwZXJcIj5cbiAgICAgICAgPGgyPkNvbW1lbnRzPC9oMj5cblxuICAgICAgICA8U3VzcGVuc2UgZmFsbGJhY2s9e1wiQ29tbWVudHMgTG9hZGluZy4uLlwifT5cbiAgICAgICAgICA8Q29tbWVudHMgZ2V0Q29tbWVudHNQcm9taXNlPXtnZXRDb21tZW50c1Byb21pc2V9IC8+XG4gICAgICAgIDwvU3VzcGVuc2U+XG4gICAgICA8L2Rpdj5cbiAgICA8Lz5cbiAgKTtcbn1cbiJ9LCIvaW5kZXgudHN4Ijp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5cbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuaW1wb3J0IHsgc2xlZXAgfSBmcm9tIFwiLi91dGlsc1wiO1xuXG5jb25zdCBnZXRDb21tZW50cyA9IGFzeW5jICgpID0+IHtcbiAgYXdhaXQgc2xlZXAoMjAwMCk7XG5cbiAgY29uc3QgdXJsID0gbmV3IFVSTChkb2N1bWVudC5sb2NhdGlvbik7XG5cbiAgcmV0dXJuIFtcbiAgICB7IGlkOiAxLCBjb21tZW50OiBcIlRoaXMgaXMgYSBjb21tZW50XCIgfSxcbiAgICB7IGlkOiAxLCBjb21tZW50OiBcIlRoaXMgaXMgYW5vdGhlciBjb21tZW50XCIgfSxcbiAgXTtcbn07XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcig8QXBwIGdldENvbW1lbnRzUHJvbWlzZT17Z2V0Q29tbWVudHMoKX0gLz4pO1xuIn0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIjE5LjAuMC1iZXRhLTI2ZjI0OTYwOTMtMjAyNDA1MTRcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIjE5LjAuMC1iZXRhLTI2ZjI0OTYwOTMtMjAyNDA1MTRcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIixcbiAgICBcInJlYWN0LWVycm9yLWJvdW5kYXJ5XCI6IFwiNC4wLjEzXCJcbiAgfSxcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge1xuICAgIFwiQHR5cGVzL3JlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwiQHR5cGVzL3JlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInR5cGVzY3JpcHRcIjogXCJeNC4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXgudHN4XCJcbn0ifSwiL3V0aWxzLnRzeCI6eyJjb2RlIjoiZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNsZWVwKHRpbWVvdXQ6IG51bWJlcikge1xuICByZXR1cm4gYXdhaXQgbmV3IFByb21pc2UoKHIpID0+IHNldFRpbWVvdXQociwgdGltZW91dCkpO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gUG9zdCgpIHtcbiAgcmV0dXJuIChcbiAgICA8ZGl2IGNsYXNzTmFtZT1cInBvc3Qtd3JhcHBlclwiPlxuICAgICAgPGgxPk15IFBvc3Q8L2gxPlxuXG4gICAgICA8cD5UaGlzIGlzIG15IHBvc3QgY29udGVudDwvcD5cbiAgICA8L2Rpdj5cbiAgKTtcbn1cbiJ9fSwidGVtcGxhdGUiOiJyZWFjdC10cyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5Ijp0cnVlLCJzaG93UmVhZE9ubHkiOnRydWV9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLnRzeCJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0=" height="750px" width="100%"></iframe>

<p>In the example above, the <code>App</code> component gets a <code>getCommentsPromise</code>, which is eventually passed to the <code>use</code> API in the <code>Comments</code> component. The promise takes some time to resolve, which means we should probably show a loading state.</p>
<p>The good news is that we don't have to manage this ourselves. Because the <code>use</code> API works seamlessly with the <code>Suspense</code> component, we can provide a <code>fallback</code> and things just work!</p>
<h3 id="heading-context-values-can-be-conditionally-loaded-with-the-use-api">Context values can be conditionally loaded with the <code>use</code> API.</h3>
<p>Normally, you would need to use the <code>useContext</code> hook to get the value of a Context provider. The problem with this approach is that this hook has to follow the <a target="_blank" href="https://react.dev/warnings/invalid-hook-call-warning">Rules of Hooks</a>.</p>
<p>The relevant rule is that we can't call a hook <em>conditionally</em>.</p>
<p>The <code>use</code> API, on the other hand, doesn't have this restriction. We are free to load a Context value however we want:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3Vvd2YxdXc3aDdqZmk0Y2pxNWllcXByZyIsIm5hbWUiOiJSZWFjdCAxOSB1c2UgQVBJIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xuICBwYWRkaW5nOiAycmVtO1xufVxuIn0sIi90c2NvbmZpZy5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiaW5jbHVkZVwiOiBbXG4gICAgXCIuLyoqLypcIlxuICBdLFxuICBcImNvbXBpbGVyT3B0aW9uc1wiOiB7XG4gICAgXCJzdHJpY3RcIjogdHJ1ZSxcbiAgICBcImVzTW9kdWxlSW50ZXJvcFwiOiB0cnVlLFxuICAgIFwibGliXCI6IFsgXCJkb21cIiwgXCJlczIwMTVcIiBdLFxuICAgIFwianN4XCI6IFwicmVhY3QtanN4XCJcbiAgfVxufSJ9LCIvQXBwLnRzeCI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBVc2VyQ29udGV4dCB9IGZyb20gXCIuL3V0aWxzXCI7XG5cbmV4cG9ydCBmdW5jdGlvbiBVc2VyQmFubmVyKHsgc2hvd1VzZXIgfSkge1xuICBpZiAoIXNob3dVc2VyKSByZXR1cm4gPHA+SGkgVW5rbm93biE8L3A+O1xuXG4gIGNvbnN0IHVzZXIgPSB1c2UoVXNlckNvbnRleHQpO1xuICByZXR1cm4gPGRpdj5IaSB7dXNlcn0hPC9kaXY+O1xufVxuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKTogSlNYLkVsZW1lbnQge1xuICByZXR1cm4gKFxuICAgIDxVc2VyQ29udGV4dC5Qcm92aWRlciB2YWx1ZT1cIkNvbnRleHQgVXNlclwiPlxuICAgICAgPFVzZXJCYW5uZXIgLz5cbiAgICAgIDxVc2VyQmFubmVyIHNob3dVc2VyIC8+XG4gICAgPC9Vc2VyQ29udGV4dC5Qcm92aWRlcj5cbiAgKTtcbn1cbiJ9LCIvaW5kZXgudHN4Ijp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5cbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoPEFwcCAvPik7XG4ifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiMTkuMC4wLWJldGEtMjZmMjQ5NjA5My0yMDI0MDUxNFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiMTkuMC4wLWJldGEtMjZmMjQ5NjA5My0yMDI0MDUxNFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiLFxuICAgIFwicmVhY3QtZXJyb3ItYm91bmRhcnlcIjogXCI0LjAuMTNcIlxuICB9LFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJAdHlwZXMvcmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJAdHlwZXMvcmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwidHlwZXNjcmlwdFwiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC50c3hcIlxufSJ9LCIvdXRpbHMudHN4Ijp7ImNvZGUiOiJpbXBvcnQgeyBjcmVhdGVDb250ZXh0IH0gZnJvbSBcInJlYWN0XCI7XG5cbmV4cG9ydCBjb25zdCBVc2VyQ29udGV4dCA9IGNyZWF0ZUNvbnRleHQoKTtcbiJ9fSwidGVtcGxhdGUiOiJyZWFjdC10cyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5Ijp0cnVlLCJzaG93UmVhZE9ubHkiOnRydWV9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLnRzeCJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidFZGl0b3InXG4nUHJldmlldycifX19" height="750px" width="100%"></iframe>

<p>In this example, notice how the <code>UserBanner</code> component <em>reads</em> the <code>UserContext</code> value <em>if</em> the <code>showUser</code> prop is <code>true</code>. In other words, if this prop is <code>false</code>, then we <em>don't</em> call the <code>use</code> API.</p>
<p>This is the key difference!</p>
<p>If we were to use the <code>useContext</code> hook, then we wouldn't be able to do this. We would have to put the hook at the top of the component and <em>always</em> read the context value regardless of whether we use it in the final JSX.</p>
<h3 id="heading-how-do-you-handle-errors">How do you handle errors?</h3>
<p>So what if the resource you're loading throws an error? Maybe there's an auth error or a timeout. The good news is that handling an error with the <code>use</code> API is straightforward!</p>
<p>You have two main options:</p>
<ol>
<li><p>Use an error boundary</p>
</li>
<li><p>Catch the error and return a different value</p>
</li>
</ol>
<p>Here's an example using an error boundary:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { use, Suspense } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { ErrorBoundary } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-error-boundary'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Comments</span>(<span class="hljs-params">{ getCommentsPromise }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">ErrorBoundary</span> <span class="hljs-attr">fallback</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">p</span>&gt;</span>Oh no! Something went wrong!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>}&gt;
      <span class="hljs-tag">&lt;<span class="hljs-name">Suspense</span> <span class="hljs-attr">fallback</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">p</span>&gt;</span>Fetching Comments!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>}&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">Comment</span> <span class="hljs-attr">commentPromise</span>=<span class="hljs-string">{getCommentsPromise}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Suspense</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ErrorBoundary</span>&gt;</span></span>
  );
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Comment</span>(<span class="hljs-params">{ commentPromise }</span>) </span>{
  <span class="hljs-keyword">const</span> comment = use(commentPromise);
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>{comment}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>;
}
</code></pre>
<h3 id="heading-the-use-api-must-be-called-within-the-render-cycle">The <code>use</code> API must be called within the render cycle.</h3>
<p>One thing to keep in mind is that the <code>use</code> API has to be called in a component's render cycle. This is similar to hooks. The key difference is that you can leverage the <code>use</code> API conditionally. Outside of that, you can also easily manage loading and error states.</p>
<p>And that's it!</p>
<p>React 19's <code>use</code> API is a simple and powerful tool to handle loading data asynchronously, loading context values, and conditionally rendering JSX.</p>
]]></content:encoded></item><item><title><![CDATA[How Google Crawls the Web: A Simple Overview]]></title><description><![CDATA[I've been trying to learn more about SEO in the context of Google search. With that in mind, I wanted to share what I'm learning as a reference for myself and others. Before we get to SEO, we need to understand how Search works in general, and that p...]]></description><link>https://blog.thesshguy.com/how-google-crawlers-work</link><guid isPermaLink="true">https://blog.thesshguy.com/how-google-crawlers-work</guid><category><![CDATA[Google]]></category><category><![CDATA[web crawling]]></category><category><![CDATA[SEO]]></category><category><![CDATA[SEO for Developers]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[webdev]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Tue, 21 May 2024 14:13:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716228405232/fb117f67-466a-4cca-bbbd-68224a096a71.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I've been trying to learn more about SEO in the context of Google search. With that in mind, I wanted to share what I'm learning as a reference for myself and others. Before we get to SEO, we need to understand how <em>Search</em> works in general, and that process starts with something known as Crawling.</p>
<h3 id="heading-crawlers-find-webpages-and-make-them-searchable">Crawlers find webpages and make them searchable.</h3>
<p>Google enlists the help of specialized bots called crawlers to do this. Once the webpages are found, Google can index them and make them discoverable for other people through its search interface.</p>
<p>A crawler is an automated program that works by:</p>
<ol>
<li><p>Browsing the internet</p>
</li>
<li><p>Downloading web pages</p>
</li>
<li><p>Extracting links for further crawling</p>
</li>
</ol>
<h3 id="heading-the-first-step-is-url-discovery">The first step is URL discovery.</h3>
<p>Google is constantly looking for new and updated pages to add to its list of known pages. It uses the following methods to do this:</p>
<ol>
<li><p>Following links from already known pages</p>
</li>
<li><p>Using sitemaps submitted by website owners</p>
</li>
</ol>
<p>The first method is pretty basic. Find a website and navigate to any links that the website has on it. The second method might not be familiar to you, but sitemaps play a significant role in helping Google discover your web pages.</p>
<p>A sitemap is a collection of URLs to pages on your site and can include additional metadata.</p>
<p>Sitemaps aren't mandatory, but they can drastically improve Google's ability to find your content. You can create and submit a sitemap to Google to ensure your pages are crawled and indexed efficiently.</p>
<h3 id="heading-how-does-googles-crawler-work">How does Google's crawler work?</h3>
<p>Google's crawler uses an algorithmic process to determine:</p>
<ol>
<li><p>Which sites to crawl</p>
</li>
<li><p>How often to crawl a site</p>
</li>
<li><p>How many pages to fetch from a site</p>
</li>
</ol>
<p>Google uses an algorithm to determine these things to avoid overloading a website's resources. Every website is different. A website's response times and overall quality of content determine the crawling behavior.</p>
<h3 id="heading-once-a-url-is-discovered-google-renders-the-page-to-view-dynamic-content">Once a URL is discovered, Google renders the page to view dynamic content.</h3>
<p>After the crawler discovers a URL, it fetches the page and its resources to determine what's on it.</p>
<p>Part of this process involves displaying and executing the code returned to generate a visual representation of the website. The content on your page might load dynamically via JavaScript, so Google needs to render the page to see what a user would see.</p>
<p>The Google Search crawling process is key to getting your web pages found and searchable. By understanding how Google's crawler finds, fetches, and shows pages, and by using tools like sitemaps, you can boost your site's SEO and make it more visible in search results.</p>
]]></content:encoded></item><item><title><![CDATA[A Guide to Using React 19's Latest Hook: useOptimistic]]></title><description><![CDATA[React 19's useOptimistic hook lets you update your UI to its expected state after an asynchronous action while the action is still in progress.
Imagine you have an app that lets you add comments to some published content. When you add a new comment, ...]]></description><link>https://blog.thesshguy.com/react-use-optimistic</link><guid isPermaLink="true">https://blog.thesshguy.com/react-use-optimistic</guid><category><![CDATA[React]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[React 19]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[react hooks]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Mon, 20 May 2024 14:15:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716181167242/594c0b89-0e02-4dca-9626-d719827a8bee.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>React 19's <code>useOptimistic</code> hook lets you update your UI to its expected state after an asynchronous action <em>while</em> the action is still in progress.</p>
<p>Imagine you have an app that lets you add comments to some published content. When you add a new comment, a network request is made to the server to create the comment in the app's database.</p>
<p>This process takes time, so you need to account for this in your UI state. The flow might look something like this:</p>
<ol>
<li><p>Make a <code>POST</code> request to create a new comment</p>
</li>
<li><p>Show a loading spinner in your UI</p>
</li>
<li><p>After the <code>POST</code> request finishes, make a <code>GET</code> request to fetch the latest list of comments, which should include your new comment</p>
</li>
<li><p>Remove the loading spinner and show the updated comments</p>
</li>
</ol>
<h3 id="heading-the-useoptimisitic-hook-makes-your-ui-feel-snappier">The useOptimisitic hook makes your UI feel snappier</h3>
<p>Instead of showing a loading spinner and waiting for all your requests to resolve, you can use the <code>useOptimistic</code> hook to immediately add the comment. If the request fails, you can remove the comment and show an error message. The happy path feels much faster and it's easy to recover from the failure path.</p>
<p>The example below shows how this might work by combining the new <code>useActionState</code> hook with the <code>useOptimistic</code> hook.</p>
<blockquote>
<p>If you're unfamiliar with how the <code>useActionState</code> hook works, you can learn more in my post about <a target="_blank" href="https://blog.thesshguy.com/react-19-actions">React 19 actions and form actions</a>.</p>
</blockquote>
<p>You'll notice we show a loading message when you submit a comment. But that's not the only thing we do. We <em>also</em> immediately show the comment in the comment section because we update the <em>optimistic</em> value. This optimistic value has an extra <code>unsaved</code> property that we use to style the comment differently.</p>
<p>Once the request finishes, we update the actual <code>comments</code> state which includes the newly created comment. This time, however, the comment <em>isn't</em> in an unsaved state, so it's styled normally.</p>
<p>To handle errors, we show a toast message and return the old list of comments. When the component re-renders, the <code>useOptimistic</code> hook will detect this, which will remove the optimistically added comment.</p>
<p>To try this out, you can pass the text <code>error</code> to the comment form.</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3Vvd2YxdXc3aDdqZmk0Y2pxNWlhcXBrayIsIm5hbWUiOiJSZWFjdCAxOSBPcHRpbWlzdGljIFVwZGF0ZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG4gIHBhZGRpbmc6IDJyZW07XG59XG5cbmgxIHtcbiAgZm9udC1zaXplOiAxLjVyZW07XG59XG5cbmgyIHtcbiAgZm9udC1zaXplOiAxLjI1cmVtO1xufVxuXG5mb3JtIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZ2FwOiAwLjVyZW07XG59XG5cbmZvcm0gYnV0dG9uIHtcbiAgYWxpZ24tc2VsZjogZmxleC1lbmQ7XG59XG5cbmxhYmVsIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgZ2FwOiAwLjEyNXJlbTtcbn1cblxuLnBvc3Qtd3JhcHBlcixcbi5jb21tZW50LXdyYXBwZXIge1xuICBkaXNwbGF5OiBmbGV4O1xuICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xufVxuXG4ucG9zdC13cmFwcGVyIGRpdiB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGFsaWduLWl0ZW1zOiBiYXNlbGluZTtcbiAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuO1xufVxuIn0sIi90c2NvbmZpZy5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiaW5jbHVkZVwiOiBbXG4gICAgXCIuLyoqLypcIlxuICBdLFxuICBcImNvbXBpbGVyT3B0aW9uc1wiOiB7XG4gICAgXCJzdHJpY3RcIjogdHJ1ZSxcbiAgICBcImVzTW9kdWxlSW50ZXJvcFwiOiB0cnVlLFxuICAgIFwibGliXCI6IFsgXCJkb21cIiwgXCJlczIwMTVcIiBdLFxuICAgIFwianN4XCI6IFwicmVhY3QtanN4XCJcbiAgfVxufSJ9LCIvQXBwLnRzeCI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlQWN0aW9uU3RhdGUsIHVzZU9wdGltaXN0aWMgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IHVzZUZvcm1TdGF0dXMgfSBmcm9tIFwicmVhY3QtZG9tXCI7XG5pbXBvcnQgeyB0b2FzdCB9IGZyb20gXCJzb25uZXJcIjtcbmltcG9ydCB7IGNyZWF0ZUNvbW1lbnQsIFBvc3QsIENvbW1lbnRGb3JtLCBDb21tZW50cyB9IGZyb20gXCIuL3V0aWxzXCI7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpOiBKU1guRWxlbWVudCB7XG4gIGNvbnN0IFtjb21tZW50cywgc3VibWl0QWN0aW9uLCBpc1BlbmRpbmddID0gdXNlQWN0aW9uU3RhdGUoXG4gICAgYXN5bmMgKHByZXZDb21tZW50cywgZm9ybURhdGEpID0+IHtcbiAgICAgIGNvbnN0IGNvbW1lbnQgPSBmb3JtRGF0YS5nZXQoXCJjb21tZW50XCIpO1xuXG4gICAgICBzZXRPcHRpbWlzdGljQ29tbWVudHMoW1xuICAgICAgICAuLi5wcmV2Q29tbWVudHMsXG4gICAgICAgIHsgaWQ6IGBsb2NhbC0ke01hdGgucmFuZG9tKCl9YCwgY29tbWVudCwgdW5zYXZlZDogdHJ1ZSB9LFxuICAgICAgXSk7XG5cbiAgICAgIGNvbnN0IG5ld0NvbW1lbnQgPSBhd2FpdCBjcmVhdGVDb21tZW50KGNvbW1lbnQpO1xuXG4gICAgICBpZiAobmV3Q29tbWVudC5zdGF0dXMgPT09IFwiZXJyb3JcIikge1xuICAgICAgICB0b2FzdC5lcnJvcihcIkZhaWxlZCB0byBjcmVhdGUgY29tbWVudFwiKTtcbiAgICAgICAgcmV0dXJuIHByZXZDb21tZW50cztcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIFsuLi5wcmV2Q29tbWVudHMsIG5ld0NvbW1lbnQuY29tbWVudF07XG4gICAgfSxcbiAgICBbXSxcbiAgKTtcblxuICBjb25zdCBbb3B0aW1pc3RpY0NvbW1lbnRzLCBzZXRPcHRpbWlzdGljQ29tbWVudHNdID0gdXNlT3B0aW1pc3RpYyhjb21tZW50cyk7XG5cbiAgcmV0dXJuIChcbiAgICA8PlxuICAgICAgPFBvc3QgaXNQZW5kaW5nPXtpc1BlbmRpbmd9IC8+XG4gICAgICA8Q29tbWVudEZvcm0gc3VibWl0QWN0aW9uPXtzdWJtaXRBY3Rpb259IGlzUGVuZGluZz17aXNQZW5kaW5nfSAvPlxuICAgICAgPENvbW1lbnRzIGNvbW1lbnRzPXtvcHRpbWlzdGljQ29tbWVudHN9IC8+XG4gICAgPC8+XG4gICk7XG59XG4ifSwiL2luZGV4LnRzeCI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IHsgVG9hc3RlciB9IGZyb20gXCJzb25uZXJcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxUb2FzdGVyIHBvc2l0aW9uPVwidG9wLXJpZ2h0XCIgLz5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT4sXG4pO1xuIn0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIjE5LjAuMC1iZXRhLTI2ZjI0OTYwOTMtMjAyNDA1MTRcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIjE5LjAuMC1iZXRhLTI2ZjI0OTYwOTMtMjAyNDA1MTRcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNC4wLjBcIixcbiAgICBcInNvbm5lclwiOiBcIjEuNC40MVwiXG4gIH0sXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcIkB0eXBlcy9yZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcIkB0eXBlcy9yZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJ0eXBlc2NyaXB0XCI6IFwiXjQuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LnRzeFwiXG59In0sIi91dGlscy50c3giOnsiY29kZSI6ImltcG9ydCB7IHRvYXN0IH0gZnJvbSBcInNvbm5lclwiO1xuXG5hc3luYyBmdW5jdGlvbiBzbGVlcCh0aW1lb3V0OiBudW1iZXIpIHtcbiAgcmV0dXJuIGF3YWl0IG5ldyBQcm9taXNlKChyKSA9PiBzZXRUaW1lb3V0KHIsIHRpbWVvdXQpKTtcbn1cblxubGV0IGNvbW1lbnRJZCA9IDE7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVDb21tZW50KGNvbW1lbnQ6IHN0cmluZykge1xuICBhd2FpdCBzbGVlcCgxMDAwKTtcblxuICBpZiAoY29tbWVudCA9PT0gXCJlcnJvclwiKSB7XG4gICAgcmV0dXJuIHsgc3RhdHVzOiBcImVycm9yXCIgfTtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgc3RhdHVzOiBcInN1Y2Nlc3NcIixcbiAgICBjb21tZW50OiB7IGlkOiBjb21tZW50SWQrKywgY29tbWVudDogY29tbWVudCB8fCBcIkVtcHR5IENvbW1lbnRcIiB9LFxuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gUG9zdCh7IGlzUGVuZGluZyB9KSB7XG4gIHJldHVybiAoXG4gICAgPGRpdiBjbGFzc05hbWU9XCJwb3N0LXdyYXBwZXJcIj5cbiAgICAgIDxkaXY+XG4gICAgICAgIDxoMT5NeSBQb3N0PC9oMT5cbiAgICAgICAge2lzUGVuZGluZyAmJiA8c3Bhbj5Mb2FkaW5nLi4uPC9zcGFuPn1cbiAgICAgIDwvZGl2PlxuICAgICAgPHA+VGhpcyBpcyBteSBwb3N0IGNvbnRlbnQ8L3A+XG4gICAgPC9kaXY+XG4gICk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBDb21tZW50cyh7IGNvbW1lbnRzIH0pIHtcbiAgcmV0dXJuIChcbiAgICA8ZGl2IGNsYXNzTmFtZT1cImNvbW1lbnRzLXdyYXBwZXJcIj5cbiAgICAgIDxkaXY+XG4gICAgICAgIDxoMj5Db21tZW50czwvaDI+XG4gICAgICA8L2Rpdj5cbiAgICAgIHtjb21tZW50cy5tYXAoKGNvbW1lbnQpID0+IChcbiAgICAgICAgPHAga2V5PXtjb21tZW50LmlkfSBzdHlsZT17eyBvcGFjaXR5OiBjb21tZW50LnVuc2F2ZWQgPyAwLjUgOiAxIH19PlxuICAgICAgICAgIHtjb21tZW50LmNvbW1lbnR9IHtjb21tZW50LnVuc2F2ZWQgPyBcIihzYXZpbmcuLi4pXCIgOiBcIlwifVxuICAgICAgICA8L3A+XG4gICAgICApKX1cbiAgICA8L2Rpdj5cbiAgKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIENvbW1lbnRGb3JtKHsgc3VibWl0QWN0aW9uLCBpc1BlbmRpbmcgfSkge1xuICByZXR1cm4gKFxuICAgIDxmb3JtIGFjdGlvbj17c3VibWl0QWN0aW9ufT5cbiAgICAgIDxsYWJlbD5cbiAgICAgICAgPHNwYW4+Q29tbWVudDwvc3Bhbj5cbiAgICAgICAgPGlucHV0IG5hbWU9XCJjb21tZW50XCIgLz5cbiAgICAgIDwvbGFiZWw+XG4gICAgICA8YnV0dG9uIHR5cGU9XCJzdWJtaXRcIiBkaXNhYmxlZD17aXNQZW5kaW5nfT5cbiAgICAgICAgQWRkIENvbW1lbnRcbiAgICAgIDwvYnV0dG9uPlxuICAgIDwvZm9ybT5cbiAgKTtcbn1cbiJ9fSwidGVtcGxhdGUiOiJyZWFjdC10cyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5Ijp0cnVlLCJzaG93UmVhZE9ubHkiOnRydWV9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLnRzeCJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidFZGl0b3InXG4nUHJldmlldycifX19" height="750px" width="100%"></iframe>

<p>And that's it! Hopefully, this post helps you build snappier UIs with React 19's new <code>useOptimistic</code> hook!</p>
]]></content:encoded></item><item><title><![CDATA[Unlocking the Power of Actions in React 19: Simplifying State Management]]></title><description><![CDATA[React 19 makes it way easier to manage state and data mutations! This is made possible with the introduction of "Actions" which are functions that use async Transitions.
Actions Reduce Boilerplate
Actions let us skip the manual process of handling pe...]]></description><link>https://blog.thesshguy.com/react-19-actions</link><guid isPermaLink="true">https://blog.thesshguy.com/react-19-actions</guid><category><![CDATA[React 19]]></category><category><![CDATA[React]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Sat, 18 May 2024 20:25:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1716063697183/f1ba8322-2021-4ba3-aa69-859f53b2a2e6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>React 19 makes it way easier to manage state and data mutations! This is made possible with the introduction of "Actions" which are functions that use async <a target="_blank" href="https://react.dev/reference/react/useTransition">Transitions</a>.</p>
<h2 id="heading-actions-reduce-boilerplate">Actions Reduce Boilerplate</h2>
<p>Actions let us skip the manual process of handling pending states, errors, and optimistic updates. If you pass an async function to a transition, React can take over and manage all of this for you.</p>
<p>The easiest way to do this in a component is with the <code>useTransition</code> hook:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3Vvd2YxdXc3aDdqZmk0Y2pxNWlhcXBqZCIsIm5hbWUiOiJSZWFjdCAxOSBFeGFtcGxlcyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbiAgcGFkZGluZzogMnJlbTtcbn1cblxuaDEge1xuICBmb250LXNpemU6IDEuNXJlbTtcbn1cblxuLnBvc3RzLXdyYXBwZXIge1xuICBkaXNwbGF5OiBmbGV4O1xuICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICBnYXA6IDFyZW07XG59XG5cbi5wb3N0cy13cmFwcGVyIGRpdiB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGFsaWduLWl0ZW1zOiBiYXNlbGluZTtcbiAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuO1xufVxuIn0sIi90c2NvbmZpZy5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiaW5jbHVkZVwiOiBbXG4gICAgXCIuLyoqLypcIlxuICBdLFxuICBcImNvbXBpbGVyT3B0aW9uc1wiOiB7XG4gICAgXCJzdHJpY3RcIjogdHJ1ZSxcbiAgICBcImVzTW9kdWxlSW50ZXJvcFwiOiB0cnVlLFxuICAgIFwibGliXCI6IFsgXCJkb21cIiwgXCJlczIwMTVcIiBdLFxuICAgIFwianN4XCI6IFwicmVhY3QtanN4XCJcbiAgfVxufSJ9LCIvQXBwLnRzeCI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlU3RhdGUsIHVzZVRyYW5zaXRpb24gfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVBvc3QsIFBvc3RzIH0gZnJvbSBcIi4vdXRpbHNcIjtcblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCk6IEpTWC5FbGVtZW50IHtcbiAgY29uc3QgW3Bvc3RzLCBzZXRQb3N0c10gPSB1c2VTdGF0ZShbXSk7XG4gIGNvbnN0IFtpc1BlbmRpbmcsIHN0YXJ0VHJhbnNpdGlvbl0gPSB1c2VUcmFuc2l0aW9uKCk7XG5cbiAgcmV0dXJuIChcbiAgICA8PlxuICAgICAgPGJ1dHRvblxuICAgICAgICBvbkNsaWNrPXsoKSA9PiB7XG4gICAgICAgICAgc3RhcnRUcmFuc2l0aW9uKGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgIGNvbnN0IG5ld1Bvc3QgPSBhd2FpdCBjcmVhdGVQb3N0KCk7XG4gICAgICAgICAgICBzZXRQb3N0cygocHJldlBvc3RzKSA9PiBbLi4ucHJldlBvc3RzLCBuZXdQb3N0XSk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH19XG4gICAgICA+XG4gICAgICAgIEFkZCBQb3N0XG4gICAgICA8L2J1dHRvbj5cbiAgICAgIDxQb3N0cyBwb3N0cz17cG9zdHN9IGlzUGVuZGluZz17aXNQZW5kaW5nfSAvPlxuICAgIDwvPlxuICApO1xufVxuIn0sIi9pbmRleC50c3giOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPixcbik7XG4ifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiMTkuMC4wLWJldGEtMjZmMjQ5NjA5My0yMDI0MDUxNFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiMTkuMC4wLWJldGEtMjZmMjQ5NjA5My0yMDI0MDUxNFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcIkB0eXBlcy9yZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcIkB0eXBlcy9yZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJ0eXBlc2NyaXB0XCI6IFwiXjQuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LnRzeFwiXG59In0sIi91dGlscy50c3giOnsiY29kZSI6ImFzeW5jIGZ1bmN0aW9uIHNsZWVwKHRpbWVvdXQ6IG51bWJlcikge1xuICByZXR1cm4gYXdhaXQgbmV3IFByb21pc2UoKHIpID0+IHNldFRpbWVvdXQociwgdGltZW91dCkpO1xufVxuXG5sZXQgcG9zdCA9IDE7XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBjcmVhdGVQb3N0KCkge1xuICBhd2FpdCBzbGVlcCgxMDAwKTtcbiAgcmV0dXJuIHsgaWQ6IHBvc3QsIHRpdGxlOiBgUG9zdCAke3Bvc3QrK31gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBQb3N0cyh7IHBvc3RzLCBpc1BlbmRpbmcgfSkge1xuICByZXR1cm4gKFxuICAgIDxkaXYgY2xhc3NOYW1lPVwicG9zdHMtd3JhcHBlclwiPlxuICAgICAgPGRpdj5cbiAgICAgICAgPGgxPlBvc3RzPC9oMT5cbiAgICAgICAge2lzUGVuZGluZyAmJiA8c3Bhbj5Mb2FkaW5nLi4uPC9zcGFuPn1cbiAgICAgIDwvZGl2PlxuXG4gICAgICB7cG9zdHMubGVuZ3RoID09PSAwICYmIDxwPk5vIFBvc3RzITwvcD59XG5cbiAgICAgIDx1bD5cbiAgICAgICAge3Bvc3RzLm1hcCgocG9zdCkgPT4gKFxuICAgICAgICAgIDxsaSBrZXk9e3Bvc3QuaWR9Pntwb3N0LnRpdGxlfTwvbGk+XG4gICAgICAgICkpfVxuICAgICAgPC91bD5cbiAgICA8L2Rpdj5cbiAgKTtcbn1cbiJ9fSwidGVtcGxhdGUiOiJyZWFjdC10cyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5Ijp0cnVlLCJzaG93UmVhZE9ubHkiOnRydWV9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLnRzeCJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidFZGl0b3InXG4nUHJldmlldycifX19" height="750px" width="100%"></iframe>

<p>In the example above, notice the <code>Add Post</code> button starts a transition when it's clicked which creates a new post. Because this is an asynchronous process, there's a bit of a delay. While the post is being created in the background, the <code>isPending</code> variable flips to <code>true</code>, which we use to show a loading state.</p>
<h2 id="heading-introducing-form-actions">Introducing Form Actions</h2>
<p>If the changes in the <code>useTransition</code> hook weren't cool enough, React 19 also has updates to make it easier to manage forms.</p>
<p>The new <code>useActionState</code> and <code>useFormStatus</code> hooks make it easier to work with form submissions. In the last example, we had a separate <code>useState</code> hook for the <code>posts</code> state and a <code>useTransition</code> hook to update the <code>posts</code> state. With form actions, these two things can be rolled into one:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX3Vvd2YxdXc3aDdqZmk0Y2pxNWlhcXBqZCIsIm5hbWUiOiJSZWFjdCAxOSBFeGFtcGxlcyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbiAgcGFkZGluZzogMnJlbTtcbn1cblxuaDEge1xuICBmb250LXNpemU6IDEuNXJlbTtcbn1cblxuZm9ybSB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGdhcDogMC41cmVtO1xufVxuXG5mb3JtIGJ1dHRvbiB7XG4gIGFsaWduLXNlbGY6IGZsZXgtZW5kO1xufVxuXG5sYWJlbCB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gIGdhcDogMC4xMjVyZW07XG59XG5cbi5wb3N0cy13cmFwcGVyIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgZ2FwOiAxcmVtO1xufVxuXG4ucG9zdHMtd3JhcHBlciBkaXYge1xuICBkaXNwbGF5OiBmbGV4O1xuICBhbGlnbi1pdGVtczogYmFzZWxpbmU7XG4gIGp1c3RpZnktY29udGVudDogc3BhY2UtYmV0d2Vlbjtcbn1cbiJ9LCIvdHNjb25maWcuanNvbiI6eyJjb2RlIjoie1xuICBcImluY2x1ZGVcIjogW1xuICAgIFwiLi8qKi8qXCJcbiAgXSxcbiAgXCJjb21waWxlck9wdGlvbnNcIjoge1xuICAgIFwic3RyaWN0XCI6IHRydWUsXG4gICAgXCJlc01vZHVsZUludGVyb3BcIjogdHJ1ZSxcbiAgICBcImxpYlwiOiBbIFwiZG9tXCIsIFwiZXMyMDE1XCIgXSxcbiAgICBcImpzeFwiOiBcInJlYWN0LWpzeFwiXG4gIH1cbn0ifSwiL0FwcC50c3giOnsiY29kZSI6ImltcG9ydCB7IHVzZUFjdGlvblN0YXRlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyB1c2VGb3JtU3RhdHVzIH0gZnJvbSBcInJlYWN0LWRvbVwiO1xuaW1wb3J0IHsgY3JlYXRlUG9zdCwgUG9zdHMgfSBmcm9tIFwiLi91dGlsc1wiO1xuXG5mdW5jdGlvbiBTdWJtaXRCdXR0b24ocHJvcHMpIHtcbiAgY29uc3QgeyBwZW5kaW5nIH0gPSB1c2VGb3JtU3RhdHVzKCk7XG4gIHJldHVybiAoXG4gICAgPGJ1dHRvbiB0eXBlPVwic3VibWl0XCIgZGlzYWJsZWQ9e3BlbmRpbmd9PlxuICAgICAge3Byb3BzLmNoaWxkcmVufVxuICAgIDwvYnV0dG9uPlxuICApO1xufVxuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKTogSlNYLkVsZW1lbnQge1xuICBjb25zdCBbcG9zdHMsIHN1Ym1pdEFjdGlvbiwgaXNQZW5kaW5nXSA9IHVzZUFjdGlvblN0YXRlKFxuICAgIGFzeW5jIChwcmV2UG9zdHMsIGZvcm1EYXRhKSA9PiB7XG4gICAgICBjb25zdCBuZXdQb3N0ID0gYXdhaXQgY3JlYXRlUG9zdChmb3JtRGF0YS5nZXQoXCJwb3N0LXRpdGxlXCIpKTtcbiAgICAgIHJldHVybiBbLi4ucHJldlBvc3RzLCBuZXdQb3N0XTtcbiAgICB9LFxuICAgIFtdLFxuICApO1xuXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxmb3JtIGFjdGlvbj17c3VibWl0QWN0aW9ufT5cbiAgICAgICAgPGxhYmVsPlxuICAgICAgICAgIDxzcGFuPlBvc3QgVGl0bGU8L3NwYW4+XG4gICAgICAgICAgPGlucHV0IG5hbWU9XCJwb3N0LXRpdGxlXCIgLz5cbiAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgPFN1Ym1pdEJ1dHRvbj5BZGQgUG9zdDwvU3VibWl0QnV0dG9uPlxuICAgICAgPC9mb3JtPlxuXG4gICAgICA8UG9zdHMgcG9zdHM9e3Bvc3RzfSBpc1BlbmRpbmc9e2lzUGVuZGluZ30gLz5cbiAgICA8Lz5cbiAgKTtcbn1cbiJ9LCIvaW5kZXgudHN4Ijp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT4sXG4pO1xuIn0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIjE5LjAuMC1iZXRhLTI2ZjI0OTYwOTMtMjAyNDA1MTRcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIjE5LjAuMC1iZXRhLTI2ZjI0OTYwOTMtMjAyNDA1MTRcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNC4wLjBcIlxuICB9LFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJAdHlwZXMvcmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJAdHlwZXMvcmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwidHlwZXNjcmlwdFwiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC50c3hcIlxufSJ9LCIvdXRpbHMudHN4Ijp7ImNvZGUiOiJhc3luYyBmdW5jdGlvbiBzbGVlcCh0aW1lb3V0OiBudW1iZXIpIHtcbiAgcmV0dXJuIGF3YWl0IG5ldyBQcm9taXNlKChyKSA9PiBzZXRUaW1lb3V0KHIsIHRpbWVvdXQpKTtcbn1cblxubGV0IHBvc3QgPSAxO1xuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gY3JlYXRlUG9zdCh0aXRsZTogc3RyaW5nKSB7XG4gIGF3YWl0IHNsZWVwKDEwMDApO1xuICByZXR1cm4geyBpZDogcG9zdCwgdGl0bGU6IHRpdGxlIHx8IFwiRW1wdHkgUG9zdFwiIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBQb3N0cyh7IHBvc3RzLCBpc1BlbmRpbmcgfSkge1xuICByZXR1cm4gKFxuICAgIDxkaXYgY2xhc3NOYW1lPVwicG9zdHMtd3JhcHBlclwiPlxuICAgICAgPGRpdj5cbiAgICAgICAgPGgxPlBvc3RzPC9oMT5cbiAgICAgICAge2lzUGVuZGluZyAmJiA8c3Bhbj5Mb2FkaW5nLi4uPC9zcGFuPn1cbiAgICAgIDwvZGl2PlxuXG4gICAgICB7cG9zdHMubGVuZ3RoID09PSAwICYmIDxwPk5vIFBvc3RzITwvcD59XG5cbiAgICAgIDx1bD5cbiAgICAgICAge3Bvc3RzLm1hcCgocG9zdCkgPT4gKFxuICAgICAgICAgIDxsaSBrZXk9e3Bvc3QuaWR9Pntwb3N0LnRpdGxlfTwvbGk+XG4gICAgICAgICkpfVxuICAgICAgPC91bD5cbiAgICA8L2Rpdj5cbiAgKTtcbn1cbiJ9fSwidGVtcGxhdGUiOiJyZWFjdC10cyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5Ijp0cnVlLCJzaG93UmVhZE9ubHkiOnRydWV9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLnRzeCJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidFZGl0b3InXG4nUHJldmlldycifX19" height="750px" width="100%"></iframe>

<p>Notice that we're passing a function to the form's <code>action</code> prop. Normally, this should be a string like <code>POST</code>. This might seem a little weird, but it lets us simplify common use cases.</p>
<p>Here's how it works.</p>
<p>The <code>useActionState</code> hook takes an "Action" and the initial state. The "Action" is an asynchronous function that takes the previous state and the <code>formData</code> and then returns the <em>next</em> state.</p>
<p>The <code>useActionState</code> hook returns the current state, a function to invoke the action, and an <code>isPending</code> state that's <code>true</code> when the Action is executing.</p>
<p>What if we want to know a form is submitting somewhere else in the React component tree?</p>
<p>This is where the <code>useFormStatus</code> hook comes into play. This works similar to Context values in React. Because the <code>SubmitButton</code> is nested inside the <code>form</code> element, we can use the hook to get the <code>pending</code> state of the Action. In this case, we disable the button while a post is created.</p>
<p>And that's it! A quick look at Actions and Form Actions. Hopefully this helps you take advantage of some really cool features in React 19.</p>
]]></content:encoded></item><item><title><![CDATA[React Router Loader Functions Explained: A Simple Overview]]></title><description><![CDATA[The basic idea of a loader function is that it lets you fetch data before rendering your route.
In other words:

A user navigates to a page

A loader runs to fetch data for that page

The page’s component renders with the fetched data


React Router ...]]></description><link>https://blog.thesshguy.com/react-router-loader-functions</link><guid isPermaLink="true">https://blog.thesshguy.com/react-router-loader-functions</guid><category><![CDATA[React]]></category><category><![CDATA[react router]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Client-side rendering]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Fri, 17 May 2024 14:11:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715928861628/15c9c770-e1d2-4a76-b8c7-0ec528c107ba.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>The basic idea of a loader function is that it lets you fetch data <em>before</em> rendering your route.</p>
<p>In other words:</p>
<ol>
<li><p>A user navigates to a page</p>
</li>
<li><p>A loader runs to fetch data for that page</p>
</li>
<li><p>The page’s component renders with the fetched data</p>
</li>
</ol>
<h3 id="heading-react-router-is-built-on-browser-standards">React Router is built on browser standards</h3>
<p>Loader functions are no exception! Since this is the case, it's common to return <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Response">web Responses</a> from a loader function. To make this easier, the library provides a <code>json</code> utility for when you need to return a JSON payload from a request.</p>
<p>Keep in mind that React Router works by <em>intercepting</em> requests made to your server and sending intercepted GET requests to your loader functions. From there, you have better control over the client-server interaction.</p>
<p>An example is worth 1000 words, so let's jump right in!</p>
<p>In the example below, we have a <code>Home</code> route and a <code>Projects</code> route. Each route has an associated loader function that returns a Response for that route.</p>
<p>As you click each link, notice the <em>slight</em> delay before the next page renders. This is from the <code>sleep</code> utility in each of the loader functions. In other words, the loaders return their data, and <em>then</em> the page is rendered.</p>
<p>To help demonstrate this, a loading message appears in the top right corner when <em>any</em> loader function is executing.</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2twM2c4dGM5OXptcnFuaW5wMnFtaXRjcCIsIm5hbWUiOiJSZWFjdCBSb3V0ZXIgRXhhbXBsZXMiLCJjb3JlIjp7ImZpbGVzIjp7Ii9zdHlsZXMuY3NzIjp7ImNvZGUiOiJib2R5IHtcbiAgZm9udC1mYW1pbHk6IHNhbnMtc2VyaWY7XG4gIC13ZWJraXQtZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIC1tb3otb3N4LWZvbnQtc21vb3RoaW5nOiBncmF5c2NhbGU7XG4gIGZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICB0ZXh0LXJlbmRlcmluZzogb3B0aW1pemVMZWdpYmlsaXR5O1xuICBmb250LXNtb290aDogYWx3YXlzO1xuICAtd2Via2l0LXRhcC1oaWdobGlnaHQtY29sb3I6IHRyYW5zcGFyZW50O1xuICAtd2Via2l0LXRvdWNoLWNhbGxvdXQ6IG5vbmU7XG4gIHBhZGRpbmc6IDAgMnJlbTtcbn1cblxuaDEge1xuICBmb250LXNpemU6IDEuNXJlbTtcbn1cblxubmF2IHtcbiAgZGlzcGxheTogZmxleDtcbiAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuO1xuICBwYWRkaW5nOiAycmVtIDA7XG59XG5cbm5hdiBkaXYge1xuICBkaXNwbGF5OiBmbGV4O1xuICBnYXA6IDEuNXJlbTtcbn1cbiJ9LCIvdHNjb25maWcuanNvbiI6eyJjb2RlIjoie1xuICBcImluY2x1ZGVcIjogW1xuICAgIFwiLi8qKi8qXCJcbiAgXSxcbiAgXCJjb21waWxlck9wdGlvbnNcIjoge1xuICAgIFwic3RyaWN0XCI6IHRydWUsXG4gICAgXCJlc01vZHVsZUludGVyb3BcIjogdHJ1ZSxcbiAgICBcImxpYlwiOiBbIFwiZG9tXCIsIFwiZXMyMDE1XCIgXSxcbiAgICBcImpzeFwiOiBcInJlYWN0LWpzeFwiXG4gIH1cbn0ifSwiL0FwcC50c3giOnsiY29kZSI6ImltcG9ydCB7XG4gIGNyZWF0ZUJyb3dzZXJSb3V0ZXIsXG4gIFJvdXRlclByb3ZpZGVyLFxuICB1c2VMb2FkZXJEYXRhLFxuICBqc29uLFxufSBmcm9tIFwicmVhY3Qtcm91dGVyLWRvbVwiO1xuaW1wb3J0IHsgTmF2LCBzbGVlcCB9IGZyb20gXCIuL3V0aWxzXCI7XG5cbmNvbnN0IHJvdXRlciA9IGNyZWF0ZUJyb3dzZXJSb3V0ZXIoW1xuICB7XG4gICAgcGF0aDogXCIvXCIsXG4gICAgZWxlbWVudDogPEhvbWUgLz4sXG4gICAgbG9hZGVyOiBhc3luYyAoKSA9PiB7XG4gICAgICAvLyBTaW11bGF0ZSBhbiBBUEkgcmVxdWVzdFxuICAgICAgYXdhaXQgc2xlZXAoMTAwMCk7XG5cbiAgICAgIHJldHVybiBuZXcgUmVzcG9uc2UoSlNPTi5zdHJpbmdpZnkoeyBuYW1lOiBcIlNhaVwiIH0pLCB7XG4gICAgICAgIHN0YXR1czogMjAwLFxuICAgICAgICBoZWFkZXJzOiB7XG4gICAgICAgICAgXCJDb250ZW50LVR5cGVcIjogXCJhcHBsaWNhdGlvbi9qc29uOyB1dGYtOFwiLFxuICAgICAgICB9LFxuICAgICAgfSk7XG4gICAgfSxcbiAgfSxcbiAge1xuICAgIHBhdGg6IFwiL3Byb2plY3RzXCIsXG4gICAgZWxlbWVudDogPFByb2plY3RzIC8+LFxuICAgIGxvYWRlcjogYXN5bmMgKCkgPT4ge1xuICAgICAgLy8gU2ltdWxhdGUgYW4gQVBJIHJlcXVlc3RcbiAgICAgIGF3YWl0IHNsZWVwKDEwMDApO1xuXG4gICAgICByZXR1cm4ganNvbih7XG4gICAgICAgIHByb2plY3RzOiBbXG4gICAgICAgICAgeyBpZDogMSwgbmFtZTogXCJQcm9qZWN0IDFcIiB9LFxuICAgICAgICAgIHsgaWQ6IDIsIG5hbWU6IFwiUHJvamVjdCAyXCIgfSxcbiAgICAgICAgXSxcbiAgICAgIH0pO1xuICAgIH0sXG4gIH0sXG5dKTtcblxuZnVuY3Rpb24gSG9tZSgpIHtcbiAgY29uc3QgeyBuYW1lIH0gPSB1c2VMb2FkZXJEYXRhKCk7XG5cbiAgcmV0dXJuIChcbiAgICA8PlxuICAgICAgPE5hdiAvPlxuICAgICAgPGgxPkhlbGxvIHtuYW1lfSE8L2gxPlxuICAgIDwvPlxuICApO1xufVxuXG5mdW5jdGlvbiBQcm9qZWN0cygpIHtcbiAgY29uc3QgeyBwcm9qZWN0cyB9ID0gdXNlTG9hZGVyRGF0YSgpO1xuXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxOYXYgLz5cbiAgICAgIDx1bD5cbiAgICAgICAge3Byb2plY3RzLm1hcCgocHJvamVjdCkgPT4gKFxuICAgICAgICAgIDxsaSBrZXk9e3Byb2plY3QuaWR9Pntwcm9qZWN0Lm5hbWV9PC9saT5cbiAgICAgICAgKSl9XG4gICAgICA8L3VsPlxuICAgIDwvPlxuICApO1xufVxuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKTogSlNYLkVsZW1lbnQge1xuICByZXR1cm4gPFJvdXRlclByb3ZpZGVyIHJvdXRlcj17cm91dGVyfSAvPjtcbn1cbiJ9LCIvaW5kZXgudHN4Ijp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNC4wLjBcIixcbiAgICBcInJlYWN0LXJvdXRlci1kb21cIjogXCI2LjIzLjFcIlxuICB9LFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJAdHlwZXMvcmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJAdHlwZXMvcmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwidHlwZXNjcmlwdFwiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC50c3hcIlxufSJ9LCIvdXRpbHMudHN4Ijp7ImNvZGUiOiJpbXBvcnQgeyBOYXZMaW5rLCB1c2VOYXZpZ2F0aW9uIH0gZnJvbSBcInJlYWN0LXJvdXRlci1kb21cIjtcblxuZXhwb3J0IGZ1bmN0aW9uIE5hdigpIHtcbiAgY29uc3QgbmF2aWdhdGlvbiA9IHVzZU5hdmlnYXRpb24oKTtcblxuICByZXR1cm4gKFxuICAgIDxuYXY+XG4gICAgICA8ZGl2PlxuICAgICAgICA8TmF2TGluayB0bz1cIi9cIj5Ib21lPC9OYXZMaW5rPlxuICAgICAgICA8TmF2TGluayB0bz1cIi9wcm9qZWN0c1wiPlByb2plY3RzPC9OYXZMaW5rPlxuICAgICAgPC9kaXY+XG4gICAgICA8ZGl2IHN0eWxlPXt7IG9wYWNpdHk6IFwiMC42NVwiLCBmb250U3R5bGU6IFwiaXRhbGljXCIgfX0+XG4gICAgICAgIHtuYXZpZ2F0aW9uLnN0YXRlID09PSBcImxvYWRpbmdcIiAmJiBcIkxvYWRpbmcuLi5cIn1cbiAgICAgIDwvZGl2PlxuICAgIDwvbmF2PlxuICApO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0VXNlcigpIHtcbiAgYXdhaXQgbmV3IFByb21pc2UoKHIpID0+IHNldFRpbWVvdXQociwgMSkpO1xuICByZXR1cm4gXCJTYWlcIjtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHNsZWVwKHRpbWVvdXQpIHtcbiAgcmV0dXJuIGF3YWl0IG5ldyBQcm9taXNlKChyKSA9PiBzZXRUaW1lb3V0KHIsIHRpbWVvdXQpKTtcbn1cbiJ9fSwidGVtcGxhdGUiOiJyZWFjdC10cyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6dHJ1ZSwic2hvd1JlYWRPbmx5Ijp0cnVlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOnRydWUsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjpmYWxzZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJ2aXNpYmxlRmlsZXMiOlsiL0FwcC50c3giXSwidGhlbWUiOiJrZW1wb0RhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nRWRpdG9yJ1xuJ1ByZXZpZXcnIiwibWVkaXVtIjoiJ0VtcHR5JyJ9fX0=" height="750px" width="100%"></iframe>]]></content:encoded></item><item><title><![CDATA[Understanding Pure Functions in React Functional Components: Importance and How to Implement]]></title><description><![CDATA[You've probably heard the term "pure function" before, but if not, it's a function that follows two principles:

No Side Effects: a pure function must not alter any objects or variables that exist before it's invoked

Deterministic: a pure function m...]]></description><link>https://blog.thesshguy.com/pure-react-components</link><guid isPermaLink="true">https://blog.thesshguy.com/pure-react-components</guid><category><![CDATA[React]]></category><category><![CDATA[Functional Programming]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Thu, 16 May 2024 16:00:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715845434193/6f3d9c80-a20d-47dd-850e-b7644c5298d6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>You've probably heard the term "pure function" before, but if not, it's a function that follows two principles:</p>
<ol>
<li><p><strong>No Side Effects:</strong> a pure function must not alter any objects or variables that exist before it's invoked</p>
</li>
<li><p><strong>Deterministic:</strong> a pure function must always return the same output when given the same set of inputs</p>
</li>
</ol>
<p>In other words, you can tell if a function is "pure" if it's consistent and doesn't affect anything outside its scope.</p>
<p>An <code>add</code> function is a basic example of a pure function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">add</span>(<span class="hljs-params">a, b</span>) </span>{
    <span class="hljs-keyword">return</span> a + b;
}
</code></pre>
<p>In the example above, the <code>add</code> function always returns the same result when you give it a specific set of inputs. <code>2 + 3</code> will always equal <code>5</code> (if for some reason it doesn't, then we probably have bigger problems).</p>
<h2 id="heading-react-assumes-functional-components-are-pure">React assumes functional components are pure</h2>
<p>If your React components aren't pure functions, that'll likely lead to unpredictable and buggy applications. In other words, given the same set of props (i.e. inputs), your component should <em>always</em> return the same JSX (i.e. output).</p>
<p>Let's take a look at an example:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2wwamJiZzI2MDV2YW94OGVzNm5tOHFybCIsIm5hbWUiOiJSZWFjdCBQdXJlIEZ1bmN0aW9ucyBFeGFtcGxlcyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxucCB7XG4gIGZvbnQtc2l6ZTogMS41cmVtO1xufVxuIn0sIi90c2NvbmZpZy5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiaW5jbHVkZVwiOiBbXG4gICAgXCIuLyoqLypcIlxuICBdLFxuICBcImNvbXBpbGVyT3B0aW9uc1wiOiB7XG4gICAgXCJzdHJpY3RcIjogdHJ1ZSxcbiAgICBcImVzTW9kdWxlSW50ZXJvcFwiOiB0cnVlLFxuICAgIFwibGliXCI6IFsgXCJkb21cIiwgXCJlczIwMTVcIiBdLFxuICAgIFwianN4XCI6IFwicmVhY3QtanN4XCJcbiAgfVxufSJ9LCIvQXBwLnRzeCI6eyJjb2RlIjoidHlwZSBCYW5uZXJQcm9wcyA9IHsgbmFtZTogc3RyaW5nIH07XG5cbmZ1bmN0aW9uIEJhbm5lcihwcm9wczogQmFubmVyUHJvcHMpIHtcbiAgcmV0dXJuIDxwPkhlbGxvIHtwcm9wcy5uYW1lfSE8L3A+O1xufVxuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKTogSlNYLkVsZW1lbnQge1xuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8QmFubmVyIG5hbWU9XCJKYWNrXCIgLz5cbiAgICAgIDxCYW5uZXIgbmFtZT1cIkppbGxcIiAvPlxuICAgIDwvPlxuICApO1xufVxuIn0sIi9pbmRleC50c3giOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcIkB0eXBlcy9yZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcIkB0eXBlcy9yZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJ0eXBlc2NyaXB0XCI6IFwiXjQuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LnRzeFwiXG59In19LCJ0ZW1wbGF0ZSI6InJlYWN0LXRzIn0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOmZhbHNlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjpmYWxzZSwicmVhZE9ubHkiOnRydWUsInNob3dSZWFkT25seSI6dHJ1ZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOmZhbHNlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7ImFjdGl2ZUZpbGUiOiIvQXBwLnRzeCIsInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="500px" width="100%"></iframe>

<p>In the example above, the <code>Banner</code> component takes a <code>name</code> prop and displays a welcome message.</p>
<p>Given a specific value for the <code>name</code> prop, the JSX the <code>Banner</code> component returns will <em>always</em> be the same. If you pass the name <code>Jack</code> to the <code>Banner</code> component, it's impossible for the text to be anything other than <code>Hello Jack!</code>.</p>
<h3 id="heading-impure-components-lead-to-inconsistent-jsx">Impure components lead to inconsistent JSX</h3>
<p>An impure component can lead to unexpected behavior:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2wwamJiZzI2MDV2YW94OGVzNm5tOHFybCIsIm5hbWUiOiJSZWFjdCBQdXJlIEZ1bmN0aW9ucyBFeGFtcGxlcyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxucCB7XG4gIGZvbnQtc2l6ZTogMS41cmVtO1xufVxuIn0sIi90c2NvbmZpZy5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiaW5jbHVkZVwiOiBbXG4gICAgXCIuLyoqLypcIlxuICBdLFxuICBcImNvbXBpbGVyT3B0aW9uc1wiOiB7XG4gICAgXCJzdHJpY3RcIjogdHJ1ZSxcbiAgICBcImVzTW9kdWxlSW50ZXJvcFwiOiB0cnVlLFxuICAgIFwibGliXCI6IFsgXCJkb21cIiwgXCJlczIwMTVcIiBdLFxuICAgIFwianN4XCI6IFwicmVhY3QtanN4XCJcbiAgfVxufSJ9LCIvQXBwLnRzeCI6eyJjb2RlIjoidHlwZSBCYW5uZXJQcm9wcyA9IHsgbmFtZTogc3RyaW5nIH07XG5cbmxldCB0aW1lID0gMjM7XG5cbmZ1bmN0aW9uIEJhbm5lcihwcm9wczogQmFubmVyUHJvcHMpIHtcbiAgY29uc3QgaXNQTSA9IHRpbWUgPiAxMTtcbiAgdGltZSA9IHRpbWUgJSAxMjtcblxuICByZXR1cm4gKFxuICAgIDxwPlxuICAgICAgSGVsbG8ge3Byb3BzLm5hbWV9LCBpdCdzIHt0aW1lfSB7aXNQTSA/IFwiUE1cIiA6IFwiQU1cIn0hXG4gICAgPC9wPlxuICApO1xufVxuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKTogSlNYLkVsZW1lbnQge1xuICByZXR1cm4gKFxuICAgIDw+XG4gICAgICA8QmFubmVyIG5hbWU9XCJKYWNrXCIgLz5cbiAgICAgIDxCYW5uZXIgbmFtZT1cIkppbGxcIiAvPlxuICAgIDwvPlxuICApO1xufVxuIn0sIi9pbmRleC50c3giOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoPEFwcCAvPik7XG4ifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcIkB0eXBlcy9yZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcIkB0eXBlcy9yZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJ0eXBlc2NyaXB0XCI6IFwiXjQuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LnRzeFwiXG59In19LCJ0ZW1wbGF0ZSI6InJlYWN0LXRzIn0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOmZhbHNlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjpmYWxzZSwicmVhZE9ubHkiOnRydWUsInNob3dSZWFkT25seSI6dHJ1ZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOmZhbHNlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7ImFjdGl2ZUZpbGUiOiIvQXBwLnRzeCIsInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="500px" width="100%"></iframe>

<p>This time, the <code>Banner</code> component takes a <code>time</code> variable defined outside of it and displays it along with the name. We use <code>time % 12</code> to convert the time to a range between <code>1 - 12</code> and then add <code>AM</code> or <code>PM</code> to indicate whether it's morning or night.</p>
<p>However, because the first <code>Banner</code> component <em>modifies</em> the <code>time</code> component, the second <code>Banner</code> component shows <code>AM</code> instead of <code>PM</code>. When the first <code>Banner</code> component renders, it sets the <code>time</code> variable to <code>11</code> (i.e. <code>23 % 12 === 11</code>), which the second <code>Banner</code> component then uses.</p>
<blockquote>
<p>Note: If you run this with <a target="_blank" href="https://react.dev/reference/react/StrictMode">StrictMode</a> enabled, it will actually show <code>11 AM</code> for both Banners because React renders each component twice in a <code>dev</code> environment.</p>
</blockquote>
<h3 id="heading-access-variables-via-props-instead-of-via-closures">Access variables via props instead of via closures</h3>
<p>In the example above, the <code>Banner</code> component has access to the <code>time</code> variable via a closure. Instead of doing this, you can fix the problem by just passing the time as a prop instead:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2wwamJiZzI2MDV2YW94OGVzNm5tOHFybCIsIm5hbWUiOiJSZWFjdCBQdXJlIEZ1bmN0aW9ucyBFeGFtcGxlcyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxucCB7XG4gIGZvbnQtc2l6ZTogMS41cmVtO1xufVxuIn0sIi90c2NvbmZpZy5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiaW5jbHVkZVwiOiBbXG4gICAgXCIuLyoqLypcIlxuICBdLFxuICBcImNvbXBpbGVyT3B0aW9uc1wiOiB7XG4gICAgXCJzdHJpY3RcIjogdHJ1ZSxcbiAgICBcImVzTW9kdWxlSW50ZXJvcFwiOiB0cnVlLFxuICAgIFwibGliXCI6IFsgXCJkb21cIiwgXCJlczIwMTVcIiBdLFxuICAgIFwianN4XCI6IFwicmVhY3QtanN4XCJcbiAgfVxufSJ9LCIvQXBwLnRzeCI6eyJjb2RlIjoidHlwZSBCYW5uZXJQcm9wcyA9IHsgbmFtZTogc3RyaW5nOyB0aW1lOiBudW1iZXIgfTtcblxuLy8gVGhpcyBpcyBubyBsb25nZXIgdXNlZC4uLlxubGV0IHRpbWUgPSAyMztcblxuZnVuY3Rpb24gQmFubmVyKHByb3BzOiBCYW5uZXJQcm9wcykge1xuICBjb25zdCBpc1BNID0gcHJvcHMudGltZSA+IDExO1xuICBjb25zdCB0aW1lID0gcHJvcHMudGltZSAlIDEyO1xuXG4gIHJldHVybiAoXG4gICAgPHA+XG4gICAgICBIZWxsbyB7cHJvcHMubmFtZX0sIGl0J3Mge3RpbWV9IHtpc1BNID8gXCJQTVwiIDogXCJBTVwifSFcbiAgICA8L3A+XG4gICk7XG59XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpOiBKU1guRWxlbWVudCB7XG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxCYW5uZXIgbmFtZT1cIkphY2tcIiB0aW1lPXsyM30gLz5cbiAgICAgIDxCYW5uZXIgbmFtZT1cIkppbGxcIiB0aW1lPXsyM30gLz5cbiAgICA8Lz5cbiAgKTtcbn1cbiJ9LCIvaW5kZXgudHN4Ijp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKDxBcHAgLz4pO1xuIn0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNC4wLjBcIlxuICB9LFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJAdHlwZXMvcmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJAdHlwZXMvcmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwidHlwZXNjcmlwdFwiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC50c3hcIlxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdC10cyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5Ijp0cnVlLCJzaG93UmVhZE9ubHkiOnRydWV9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjpmYWxzZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJhY3RpdmVGaWxlIjoiL0FwcC50c3giLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidFZGl0b3InXG4nUHJldmlldycifX19" height="500px" width="100%"></iframe>

<h3 id="heading-mutating-local-variables-is-okay">Mutating local variables is okay!</h3>
<p>Remember, pure functions must not alter any objects or variables that exist before they're invoked. This means a pure function can update an object or variable that's created <em>when</em> it's invoked. In other words, you can update local variables all day long without making the function impure!</p>
<p>The following is perfectly acceptable:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> BannerProps = { name: <span class="hljs-built_in">string</span>; time: <span class="hljs-built_in">number</span> };

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Banner</span>(<span class="hljs-params">props: BannerProps</span>) </span>{
    <span class="hljs-keyword">let</span> time = props.time;
    <span class="hljs-keyword">if</span> (time &gt; <span class="hljs-number">23</span>) {
        time = <span class="hljs-number">0</span>;
    }

    <span class="hljs-keyword">const</span> isPM = time &gt; <span class="hljs-number">11</span>;
    time = time % <span class="hljs-number">12</span>;

    <span class="hljs-keyword">return</span> (
        &lt;p&gt;
          Hello {props.name}, it<span class="hljs-string">'s {time} {isPM ? "PM" : "AM"}!
        &lt;/p&gt;
    );
}</span>
</code></pre>
<p>In this example, if the <code>time</code> variable is greater than <code>23</code>, we reset it back to <code>0</code>. You can see that throughout the component, we're updating the <code>time</code> variable in several places. This is completely fine because the <code>time</code> variable was initialized <em>inside</em> the <code>Banner</code> component.</p>
<h2 id="heading-why-are-pure-components-important-in-react">Why are pure components important in React?</h2>
<p>Here are a few reasons why keeping your React components pure is beneficial:</p>
<ol>
<li><p><strong>Predictability</strong>: Pure components are predictable, making debugging easier.</p>
</li>
<li><p><strong>Performance</strong>: Pure components are cacheable, which lets React optimize rendering (i.e. skip renders when inputs haven't changed).</p>
</li>
<li><p><strong>Server-Side Rendering</strong>: Pure components are deterministic, so your server can reuse them for many requests.</p>
</li>
<li><p><strong>Concurrent Mode</strong>: React can optimize rendering by pausing and resuming the render cycle without issues because pure components don’t rely on side effects.</p>
</li>
</ol>
<p>It's important to make sure your components are pure. If you don't, your applications will be harder to maintain and less reliable in the long run!</p>
]]></content:encoded></item><item><title><![CDATA[How to Use CSS Flexbox with Tailwind: An Interactive Guide]]></title><description><![CDATA[Flexbox is a CSS layout module that helps you create responsive designs. When used with Tailwind, you can build these designs faster and more efficiently. In this post, we'll explore how to do that through interactive demos.
The first step to enable ...]]></description><link>https://blog.thesshguy.com/tailwind-flexbox</link><guid isPermaLink="true">https://blog.thesshguy.com/tailwind-flexbox</guid><category><![CDATA[Tailwind CSS]]></category><category><![CDATA[flexbox]]></category><category><![CDATA[CSS]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Tailwind CSS Tutorial]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Wed, 15 May 2024 23:48:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715816624830/1772a0e3-b38d-4b5e-b851-105a1135970a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>Flexbox is a CSS layout module that helps you create responsive designs. When used with Tailwind, you can build these designs faster and more efficiently. In this post, we'll explore how to do that through interactive demos.</p>
<p>The first step to enable Flexbox is to use the <code>flex</code> class:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"flex"</span>&gt;</span>
  <span class="hljs-comment">&lt;!-- Flex items go here --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>By default, Flexbox uses a row or horizontal layout, but if you want to be specific, you can use the <code>flex-row</code> class. To switch to a vertical layout, you can use the <code>flex-col</code> class instead.</p>
<p>Try swapping between the classes below to see how the layout updates:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2c1N2s0cnRsZzRiMWZ4cmM2Njl4eG13cSIsIm5hbWUiOiJUYWlsd2luZCBFeGFtcGxlIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xuICBoZWlnaHQ6IDEwMCU7XG4gIHdpZHRoOiAxMDAlO1xuICBwYWRkaW5nOiAxcmVtO1xuICBnYXA6IDEuNXJlbTtcbn1cblxuZGl2IHtcbiAgd2lkdGg6IDEwMHB4O1xuICBoZWlnaHQ6IDEwMHB4O1xuICBib3JkZXI6IDFweCBzb2xpZCBibGFjaztcbiAgYm9yZGVyLXJhZGl1czogNHB4O1xuICBkaXNwbGF5OiBmbGV4O1xuICBhbGlnbi1pdGVtczogY2VudGVyO1xuICBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjtcbiAgYmFja2dyb3VuZC1jb2xvcjogIzU2NWY4OTtcbiAgY29sb3I6IHdoaXRlO1xufVxuIn0sIi9pbmRleC5odG1sIjp7ImNvZGUiOiI8IWRvY3R5cGUgaHRtbD5cbjxodG1sPlxuICA8aGVhZD5cbiAgICA8dGl0bGU+S2VtcG8gU2FuZGJveDwvdGl0bGU+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCIgLz5cbiAgICA8bGluayByZWw9XCJzdHlsZXNoZWV0XCIgaHJlZj1cIi9zdHlsZXMuY3NzXCIgLz5cbiAgICA8c2NyaXB0IHNyYz1cImh0dHBzOi8vY2RuLnRhaWx3aW5kY3NzLmNvbVwiPjwvc2NyaXB0PlxuICA8L2hlYWQ+XG5cbiAgPGJvZHkgY2xhc3M9XCJmbGV4IGZsZXgtcm93XCI+XG4gICAgPGRpdj5PbmU8L2Rpdj5cbiAgICA8ZGl2PlR3bzwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+XG4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7fSxcbiAgXCJtYWluXCI6IFwiL2luZGV4Lmh0bWxcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifX0sInRlbXBsYXRlIjoic3RhdGljIn0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOmZhbHNlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjpmYWxzZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6ZmFsc2UsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2V9fX0sImdlbmVyYWwiOnsiYWN0aXZlRmlsZSI6Ii9pbmRleC5odG1sIiwidGhlbWUiOiJrZW1wb0RhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nUHJldmlldycifX19" height="500px" width="100%"></iframe>

<p>Great!</p>
<p>Now we can control the direction our elements are placed on the page. But what if we want to move the elements along the main axis?</p>
<blockquote>
<p>The main axis is horizontal for row and vertical for column</p>
</blockquote>
<p>To do this, you can use one of the following classes: <code>justify-start</code>, <code>justify-center</code>, <code>justify-end</code>, <code>justify-between</code>, <code>justify-around</code>, or <code>justify-evenly</code>. Try changing the class below to see what happens:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2c1N2s0cnRsZzRiMWZ4cmM2Njl4eG13cSIsIm5hbWUiOiJUYWlsd2luZCBFeGFtcGxlIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xuICBoZWlnaHQ6IDMwMHB4O1xuICB3aWR0aDogMTAwJTtcbiAgcGFkZGluZzogMXJlbTtcbiAgZ2FwOiAxLjVyZW07XG59XG5cbmRpdiB7XG4gIHdpZHRoOiAxMDBweDtcbiAgaGVpZ2h0OiAxMDBweDtcbiAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7XG4gIGJvcmRlci1yYWRpdXM6IDRweDtcbiAgZGlzcGxheTogZmxleDtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7XG4gIGJhY2tncm91bmQtY29sb3I6ICM1NjVmODk7XG4gIGNvbG9yOiB3aGl0ZTtcbn1cbiJ9LCIvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFkb2N0eXBlIGh0bWw+XG48aHRtbD5cbiAgPGhlYWQ+XG4gICAgPHRpdGxlPktlbXBvIFNhbmRib3g8L3RpdGxlPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiIC8+XG4gICAgPGxpbmsgcmVsPVwic3R5bGVzaGVldFwiIGhyZWY9XCIvc3R5bGVzLmNzc1wiIC8+XG4gICAgPHNjcmlwdCBzcmM9XCJodHRwczovL2Nkbi50YWlsd2luZGNzcy5jb21cIj48L3NjcmlwdD5cbiAgPC9oZWFkPlxuXG4gIDxib2R5IGNsYXNzPVwiZmxleCBmbGV4LXJvdyBqdXN0aWZ5LXN0YXJ0XCI+XG4gICAgPGRpdj5PbmU8L2Rpdj5cbiAgICA8ZGl2PlR3bzwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+XG4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7fSxcbiAgXCJtYWluXCI6IFwiL2luZGV4Lmh0bWxcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifX0sInRlbXBsYXRlIjoic3RhdGljIn0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOmZhbHNlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjpmYWxzZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6ZmFsc2UsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2V9fX0sImdlbmVyYWwiOnsiYWN0aXZlRmlsZSI6Ii9pbmRleC5odG1sIiwidGhlbWUiOiJrZW1wb0RhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nUHJldmlldycifX19" height="500px" width="100%"></iframe>

<blockquote>
<p>Note: Try changing to a column layout above to see how it affects the main axis</p>
</blockquote>
<p>Now that we can move the elements along the main axis, what about the cross axis? The cross axis is the <em>other</em> direction depending on which layout you choose. In other words, it's vertical for row layouts and horizontal for column layouts.</p>
<p>To position elements along this axis, you can use one of the following classes: <code>items-start</code>, <code>items-center</code>, <code>items-end</code>, <code>items-baseline</code>, or <code>items-stretch</code>. Try changing the class below to see what happens:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2c1N2s0cnRsZzRiMWZ4cmM2Njl4eG13cSIsIm5hbWUiOiJUYWlsd2luZCBFeGFtcGxlIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xuICBoZWlnaHQ6IDMwMHB4O1xuICB3aWR0aDogMTAwJTtcbiAgcGFkZGluZzogMXJlbTtcbiAgZ2FwOiAxLjVyZW07XG59XG5cbmRpdiB7XG4gIHdpZHRoOiAxMDBweDtcbiAgaGVpZ2h0OiAxMDBweDtcbiAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7XG4gIGJvcmRlci1yYWRpdXM6IDRweDtcbiAgZGlzcGxheTogZmxleDtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7XG4gIGJhY2tncm91bmQtY29sb3I6ICM1NjVmODk7XG4gIGNvbG9yOiB3aGl0ZTtcbn1cbiJ9LCIvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFkb2N0eXBlIGh0bWw+XG48aHRtbD5cbiAgPGhlYWQ+XG4gICAgPHRpdGxlPktlbXBvIFNhbmRib3g8L3RpdGxlPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiIC8+XG4gICAgPGxpbmsgcmVsPVwic3R5bGVzaGVldFwiIGhyZWY9XCIvc3R5bGVzLmNzc1wiIC8+XG4gICAgPHNjcmlwdCBzcmM9XCJodHRwczovL2Nkbi50YWlsd2luZGNzcy5jb21cIj48L3NjcmlwdD5cbiAgPC9oZWFkPlxuXG4gIDxib2R5IGNsYXNzPVwiZmxleCBmbGV4LXJvdyBpdGVtcy1zdGFydFwiPlxuICAgIDxkaXY+T25lPC9kaXY+XG4gICAgPGRpdj5Ud288L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPlxuIn0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge30sXG4gIFwibWFpblwiOiBcIi9pbmRleC5odG1sXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In19LCJ0ZW1wbGF0ZSI6InN0YXRpYyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOmZhbHNlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7ImFjdGl2ZUZpbGUiOiIvaW5kZXguaHRtbCIsInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="500px" width="100%"></iframe>

<blockquote>
<p>Note: Try changing to a column layout above to see how it affects the cross axis</p>
</blockquote>
<p>Okay, great. So we can move our elements in two directions, but all the elements move together. What if we want to change the position of just one element?</p>
<p>Luckily this is easy to do with Flexbox. In Tailwind, you can position an element along its cross axis using these classes: <code>self-auto</code>, <code>self-start</code>, <code>self-center</code>, <code>self-end</code>, <code>self-baseline</code>, or <code>self-stretch</code>. Try changing the class on the second div below to see what happens:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2c1N2s0cnRsZzRiMWZ4cmM2Njl4eG13cSIsIm5hbWUiOiJUYWlsd2luZCBFeGFtcGxlIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xuICBoZWlnaHQ6IDMwMHB4O1xuICB3aWR0aDogMTAwJTtcbiAgcGFkZGluZzogMXJlbTtcbiAgZ2FwOiAxLjVyZW07XG59XG5cbmRpdiB7XG4gIHdpZHRoOiAxMDBweDtcbiAgaGVpZ2h0OiAxMDBweDtcbiAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7XG4gIGJvcmRlci1yYWRpdXM6IDRweDtcbiAgZGlzcGxheTogZmxleDtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7XG4gIGJhY2tncm91bmQtY29sb3I6ICM1NjVmODk7XG4gIGNvbG9yOiB3aGl0ZTtcbn1cbiJ9LCIvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFkb2N0eXBlIGh0bWw+XG48aHRtbD5cbiAgPGhlYWQ+XG4gICAgPHRpdGxlPktlbXBvIFNhbmRib3g8L3RpdGxlPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiIC8+XG4gICAgPGxpbmsgcmVsPVwic3R5bGVzaGVldFwiIGhyZWY9XCIvc3R5bGVzLmNzc1wiIC8+XG4gICAgPHNjcmlwdCBzcmM9XCJodHRwczovL2Nkbi50YWlsd2luZGNzcy5jb21cIj48L3NjcmlwdD5cbiAgPC9oZWFkPlxuXG4gIDxib2R5IGNsYXNzPVwiZmxleCBmbGV4LXJvd1wiPlxuICAgIDxkaXY+T25lPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cInNlbGYtc3RhcnRcIj5Ud288L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPlxuIn0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge30sXG4gIFwibWFpblwiOiBcIi9pbmRleC5odG1sXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In19LCJ0ZW1wbGF0ZSI6InN0YXRpYyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOmZhbHNlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7ImFjdGl2ZUZpbGUiOiIvaW5kZXguaHRtbCIsInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="500px" width="100%"></iframe>

<blockquote>
<p>Note: Flexbox doesn't support aligning a single item along the main axis. This is because Flexbox is <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Alignment/Box_Alignment_in_Flexbox#there_is_no_justify-self_in_flexbox">always focused on moving the <em>entire</em> group of items</a>.</p>
</blockquote>
<p>Okay! Now let's look at how to control the size of child elements within a Flexbox container.</p>
<p>To control the flex-grow, flex-shrink, and flex-basis properties, use <code>flex-grow-0</code>, <code>flex-grow</code>, <code>flex-shrink-0</code>, <code>flex-shrink</code>, <code>flex-none</code>, <code>flex-1</code>, <code>flex-auto</code>, or <code>flex-initial</code>.</p>
<p>Here's an example of how the <code>flex-shrink-0</code> property prevents the second div from shrinking below its width:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2c1N2s0cnRsZzRiMWZ4cmM2Njl4eG13cSIsIm5hbWUiOiJUYWlsd2luZCBFeGFtcGxlIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xuICBoZWlnaHQ6IDMwMHB4O1xuICB3aWR0aDogMTAwJTtcbiAgcGFkZGluZzogMXJlbTtcbiAgZ2FwOiAxLjVyZW07XG59XG5cbmRpdiB7XG4gIHdpZHRoOiAxMDBweDtcbiAgaGVpZ2h0OiAxMDBweDtcbiAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7XG4gIGJvcmRlci1yYWRpdXM6IDRweDtcbiAgZGlzcGxheTogZmxleDtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7XG4gIGJhY2tncm91bmQtY29sb3I6ICM1NjVmODk7XG4gIGNvbG9yOiB3aGl0ZTtcbn1cbiJ9LCIvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFkb2N0eXBlIGh0bWw+XG48aHRtbD5cbiAgPGhlYWQ+XG4gICAgPHRpdGxlPktlbXBvIFNhbmRib3g8L3RpdGxlPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiIC8+XG4gICAgPGxpbmsgcmVsPVwic3R5bGVzaGVldFwiIGhyZWY9XCIvc3R5bGVzLmNzc1wiIC8+XG4gICAgPHNjcmlwdCBzcmM9XCJodHRwczovL2Nkbi50YWlsd2luZGNzcy5jb21cIj48L3NjcmlwdD5cbiAgPC9oZWFkPlxuXG4gIDxib2R5IGNsYXNzPVwiZmxleCBmbGV4LXJvd1wiPlxuICAgIDxkaXYgY2xhc3M9XCJ3LWZ1bGxcIj5PbmU8L2Rpdj5cbiAgICA8ZGl2IGNsYXNzPVwiZmxleC1zaHJpbmstMFwiPlR3bzwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+XG4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7fSxcbiAgXCJtYWluXCI6IFwiL2luZGV4Lmh0bWxcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifX0sInRlbXBsYXRlIjoic3RhdGljIn0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOmZhbHNlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjpmYWxzZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6ZmFsc2UsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2V9fX0sImdlbmVyYWwiOnsiYWN0aXZlRmlsZSI6Ii9pbmRleC5odG1sIiwidGhlbWUiOiJrZW1wb0RhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nUHJldmlldycifX19" height="500px" width="100%"></iframe>

<p>If you switch to <code>flex-shrink</code>, you'll notice that the second div becomes slightly smaller. If you want to make the div <em>even</em> smaller, you can use an arbitrary value like this: <code>flex-shrink-[2]</code>.</p>
<p>Finally, if you want to alter how your Flexbox wraps, you can use <code>flex-wrap</code>, <code>flex-wrap-reverse</code>, or <code>flex-nowrap</code>. Try switching <code>flex-wrap</code> to <code>flex-wrap-reverse</code> to see how the page updates:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX2c1N2s0cnRsZzRiMWZ4cmM2Njl4eG13cSIsIm5hbWUiOiJUYWlsd2luZCBFeGFtcGxlIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xuICBoZWlnaHQ6IDMwMHB4O1xuICB3aWR0aDogMTAwJTtcbiAgcGFkZGluZzogMXJlbTtcbiAgZ2FwOiAxLjVyZW07XG59XG5cbmRpdiB7XG4gIHdpZHRoOiAxMDBweDtcbiAgaGVpZ2h0OiAxMDBweDtcbiAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7XG4gIGJvcmRlci1yYWRpdXM6IDRweDtcbiAgZGlzcGxheTogZmxleDtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7XG4gIGJhY2tncm91bmQtY29sb3I6ICM1NjVmODk7XG4gIGNvbG9yOiB3aGl0ZTtcbn1cbiJ9LCIvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFkb2N0eXBlIGh0bWw+XG48aHRtbD5cbiAgPGhlYWQ+XG4gICAgPHRpdGxlPktlbXBvIFNhbmRib3g8L3RpdGxlPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiIC8+XG4gICAgPGxpbmsgcmVsPVwic3R5bGVzaGVldFwiIGhyZWY9XCIvc3R5bGVzLmNzc1wiIC8+XG4gICAgPHNjcmlwdCBzcmM9XCJodHRwczovL2Nkbi50YWlsd2luZGNzcy5jb21cIj48L3NjcmlwdD5cbiAgPC9oZWFkPlxuXG4gIDxib2R5IGNsYXNzPVwiZmxleCBmbGV4LXJvdyBmbGV4LXdyYXBcIj5cbiAgICA8ZGl2IGNsYXNzPVwidy1mdWxsXCI+T25lPC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cImZsZXgtMVwiPlR3bzwvZGl2PlxuICAgIDxkaXY+VGhyZWU8L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPlxuIn0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge30sXG4gIFwibWFpblwiOiBcIi9pbmRleC5odG1sXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In19LCJ0ZW1wbGF0ZSI6InN0YXRpYyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOmZhbHNlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7ImFjdGl2ZUZpbGUiOiIvaW5kZXguaHRtbCIsInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="700px" width="100%"></iframe>

<p>And that's it!</p>
<p>This was a quick overview of how to use Flexbox with Tailwind. Hopefully, this makes it easier for you to build awesome responsive designs using Tailwind!</p>
]]></content:encoded></item><item><title><![CDATA[Kempo: Build Your Own Code Playgrounds Using Sandpack]]></title><description><![CDATA[Hey everyone!
I've been working on a side project called Kempo. It's a tool that lets you create custom coding playgrounds, which you can embed in blogs, documentation, or other teaching resources.
It's built on top of Sandpack
Kempo is powered by Co...]]></description><link>https://blog.thesshguy.com/kempo-introduction</link><guid isPermaLink="true">https://blog.thesshguy.com/kempo-introduction</guid><category><![CDATA[sandpack]]></category><category><![CDATA[Build In Public]]></category><category><![CDATA[CodeSandbox]]></category><category><![CDATA[React]]></category><category><![CDATA[Developer]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Tue, 14 May 2024 19:24:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1715714202428/c006f8bd-cebe-49c8-8903-4ceeb6713e3c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>Hey everyone!</p>
<p>I've been working on a side project called Kempo. It's a tool that lets you create custom coding playgrounds, which you can embed in blogs, documentation, or other teaching resources.</p>
<h2 id="heading-its-built-on-top-of-sandpack">It's built on top of Sandpack</h2>
<p>Kempo is powered by CodeSandbox's <a target="_blank" href="https://sandpack.codesandbox.io/">Sandpack</a> live coding editor. Here is what it looks like:</p>
<iframe height="500px" width="100%" src="https://kempo.io/view/eyJpZCI6ImtsX253NzFwbGY4NHQ5ZGdtaWV1N3pyazF6MSIsIm5hbWUiOiJNeSBQcm9qZWN0IiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG5oMSB7XG4gIGZvbnQtc2l6ZTogMS41cmVtO1xufSJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiA8aDE+SGVsbG8gS2VtcG8hPC9oMT5cbn1cbiJ9LCIvaW5kZXguanMiOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjpmYWxzZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0="></iframe>

<p>So why not just use Sandpack or CodeSandbox directly?</p>
<p>For starters, Sandpack is an npm package that exposes a React component that lets you build these custom editors. The problem is that on a blogging platform like Hashnode, you don't have the flexibility to insert your <em>own</em> React components in your blogs. Even if you could, you'd have to spend time designing and customizing the editor experience.</p>
<p>On the other hand, while CodeSandbox <em>does</em> let you embed code playgrounds, you don't have much flexibility in terms of how your playground <em>looks</em>.</p>
<h2 id="heading-kempo-allows-different-layouts-based-on-screen-size">Kempo allows different layouts based on screen size</h2>
<p>For example, on desktop screens, you might want to display an editor and a preview panel that are split vertically:</p>
<iframe height="500px" width="100%" src="https://kempo.io/view/eyJpZCI6ImtsX253NzFwbGY4NHQ5ZGdtaWV1N3pyazF6MSIsIm5hbWUiOiJNeSBQcm9qZWN0IiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG5oMSB7XG4gIGZvbnQtc2l6ZTogMS41cmVtO1xufSJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiA8aDE+SSdtIHNwbGl0IHZlcnRpY2FsbHkhPC9oMT5cbn1cbiJ9LCIvaW5kZXguanMiOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjpmYWxzZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvciBQcmV2aWV3JyJ9fX0="></iframe>

<p>However, on a mobile screen, you might want to display the same content, but split it horizontally instead, since there is less screen space available:</p>
<iframe height="500px" width="100%" src="https://kempo.io/view/eyJpZCI6ImtsX253NzFwbGY4NHQ5ZGdtaWV1N3pyazF6MSIsIm5hbWUiOiJNeSBQcm9qZWN0IiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG5oMSB7XG4gIGZvbnQtc2l6ZTogMS41cmVtO1xufSJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiA8aDE+SSdtIHNwbGl0IGhvcml6b250YWxseSE8L2gxPlxufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IFwiLi9zdHlsZXMuY3NzXCI7XG5cbmltcG9ydCBBcHAgZnJvbSBcIi4vQXBwXCI7XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+XG4pOyJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjUuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOnRydWUsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOnRydWUsIndyYXBDb250ZW50Ijp0cnVlLCJjbG9zYWJsZVRhYnMiOnRydWUsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOmZhbHNlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoia2VtcG9EYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ=="></iframe>

<blockquote>
<p>Note: The examples above are designed to show different layout views and do not change based on screen size.</p>
</blockquote>
<p>With Kempo, this is easy to do using Kempo's <strong><em>Layout Editor</em></strong>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715666715826/84604ff4-0830-4fa7-9d5f-960b802c28c7.png" alt class="image--center mx-auto" /></p>
<p>You can define different layouts for three screen sizes (<code>small</code>, <code>medium</code>, and <code>large</code>).</p>
<p>You can also use this feature to highlight different parts of your playground. For example, you might want to show the preview first, which you can do by setting it first in the <strong><em>Layout Editor</em></strong>:</p>
<iframe height="500px" width="100%" src="https://kempo.io/view/eyJpZCI6ImtsX253NzFwbGY4NHQ5ZGdtaWV1N3pyazF6MSIsIm5hbWUiOiJNeSBQcm9qZWN0IiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG5oMSB7XG4gIGZvbnQtc2l6ZTogMS41cmVtO1xufSJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiA8aDE+UHJldmlldyBvbiB0b3AhPC9oMT5cbn1cbiJ9LCIvaW5kZXguanMiOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjpmYWxzZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImtlbXBvRGFyayJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ1ByZXZpZXcnXG4nRWRpdG9yJyJ9fX0="></iframe>

<h2 id="heading-kempo-supports-different-components">Kempo supports different components</h2>
<p>For now, Kempo supports the following Sandpack components:</p>
<ol>
<li><p><a target="_blank" href="https://sandpack.codesandbox.io/docs/advanced-usage/components#code-editor">Editor</a></p>
</li>
<li><p><a target="_blank" href="https://sandpack.codesandbox.io/docs/advanced-usage/components#preview">Preview</a></p>
</li>
<li><p><a target="_blank" href="https://sandpack.codesandbox.io/docs/advanced-usage/components#console">Console</a></p>
</li>
</ol>
<p>You've already seen the <strong>Editor</strong> and <strong>Preview</strong> components in the examples above. The <strong>Console</strong> is a lightweight version of the browser console that lets you print logs from your editor:</p>
<iframe height="500px" width="100%" src="https://kempo.io/view/eyJpZCI6ImtsX2hjdzVpM3ZycGYyMTN0cXludWttcDh6OCIsIm5hbWUiOiJUZXN0IFRTIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG5oMSB7XG4gIGZvbnQtc2l6ZTogMS41cmVtO1xufSJ9LCIvdHNjb25maWcuanNvbiI6eyJjb2RlIjoie1xuICBcImNvbXBpbGVyT3B0aW9uc1wiOiB7XG4gICAgXCJzdHJpY3RcIjogdHJ1ZSxcbiAgICBcIm1vZHVsZVwiOiBcImNvbW1vbmpzXCIsXG4gICAgXCJqc3hcIjogXCJwcmVzZXJ2ZVwiLFxuICAgIFwiZXNNb2R1bGVJbnRlcm9wXCI6IHRydWUsXG4gICAgXCJzb3VyY2VNYXBcIjogdHJ1ZSxcbiAgICBcImFsbG93SnNcIjogdHJ1ZSxcbiAgICBcImxpYlwiOiBbXG4gICAgICBcImVzNlwiLFxuICAgICAgXCJkb21cIlxuICAgIF0sXG4gICAgXCJyb290RGlyXCI6IFwic3JjXCIsXG4gICAgXCJtb2R1bGVSZXNvbHV0aW9uXCI6IFwibm9kZVwiXG4gIH1cbn0ifSwiL2luZGV4LnRzIjp7ImNvZGUiOiJpbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuY29uc29sZS5sb2coXCJJIGFtIGEgbG9nXCIpO1xuXG5kb2N1bWVudC5nZXRFbGVtZW50QnlJZChcImFwcFwiKS5pbm5lckhUTUwgPSBgXG48aDE+Q2hlY2sgdGhlIGNvbnNvbGU8L2gxPlxuYDtcbiJ9LCIvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbD5cblxuPGhlYWQ+XG4gIDx0aXRsZT5QYXJjZWwgU2FuZGJveDwvdGl0bGU+XG4gIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiIC8+XG48L2hlYWQ+XG5cbjxib2R5PlxuICA8ZGl2IGlkPVwiYXBwXCI+PC9kaXY+XG5cbiAgPHNjcmlwdCBzcmM9XCJpbmRleC50c1wiPlxuICA8L3NjcmlwdD5cbjwvYm9keT5cblxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHt9LFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJ0eXBlc2NyaXB0XCI6IFwiXjQuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LnRzXCJcbn0ifX0sInRlbXBsYXRlIjoidmFuaWxsYS10cyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjpmYWxzZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19LCJDb25zb2xlIjp7InR5cGUiOiJjb25zb2xlIiwiZGF0YSI6eyJzaG93SGVhZGVyIjpmYWxzZSwic2hvd1N5bnRheEVycm9yIjpmYWxzZSwic2hvd1NldHVwUHJvZ3Jlc3MiOmZhbHNlLCJtYXhNZXNzYWdlQ291bnQiOjEsInJlc2V0T25QcmV2aWV3UmVzdGFydCI6ZmFsc2UsInN0YW5kYWxvbmUiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvaW5kZXgudHMiXSwidGhlbWUiOiJrZW1wb0RhcmsifSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3IgRWRpdG9yJ1xuJ1ByZXZpZXcgQ29uc29sZScifX19"></iframe>

<p>Try changing the log statement above to see how the logs update.</p>
<h2 id="heading-kempo-supports-different-themes">Kempo supports different themes</h2>
<p>Kempo offers four built-in themes that let you customize your playgrounds to better suit your needs. You can choose your theme on the project settings page:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1715705906161/2557398f-4db3-427a-9c61-2b0db6a50e6d.png" alt class="image--center mx-auto" /></p>
<p>Here's an example of the Dracula theme:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX253NzFwbGY4NHQ5ZGdtaWV1N3pyazF6MSIsIm5hbWUiOiJNeSBQcm9qZWN0IiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG5oMSB7XG4gIGZvbnQtc2l6ZTogMS41cmVtO1xufSJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiA8aDE+RHJhY3VsYSBUaGVtZSE8L2gxPjtcbn1cbiJ9LCIvaW5kZXguanMiOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl41LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6dHJ1ZSwiY2xvc2FibGVUYWJzIjp0cnVlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjpmYWxzZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZX19LCJDb25zb2xlIjp7InR5cGUiOiJjb25zb2xlIiwiZGF0YSI6eyJzaG93SGVhZGVyIjpmYWxzZSwic2hvd1N5bnRheEVycm9yIjpmYWxzZSwic2hvd1NldHVwUHJvZ3Jlc3MiOmZhbHNlLCJtYXhNZXNzYWdlQ291bnQiOjEsInJlc2V0T25QcmV2aWV3UmVzdGFydCI6ZmFsc2UsInN0YW5kYWxvbmUiOmZhbHNlfX19LCJnZW5lcmFsIjp7InZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoiZHJhY3VsYSJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0=" height="500px" width="100%"></iframe>

<p>And here's one with the Gruvbox (Dark) Theme:</p>
<iframe src="https://kempo.io/view/eyJpZCI6ImtsX253NzFwbGY4NHQ5ZGdtaWV1N3pyazF6MSIsIm5hbWUiOiJNeSBQcm9qZWN0IiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG5oMSB7XG4gIGZvbnQtc2l6ZTogMS41cmVtO1xufSJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiA8aDE+R3J1dmJveCAoRGFyaykgVGhlbWUhPC9oMT47XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNS4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifX0sInRlbXBsYXRlIjoicmVhY3QifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6dHJ1ZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOnRydWUsImNsb3NhYmxlVGFicyI6dHJ1ZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6ZmFsc2UsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2V9fSwiQ29uc29sZSI6eyJ0eXBlIjoiY29uc29sZSIsImRhdGEiOnsic2hvd0hlYWRlciI6ZmFsc2UsInNob3dTeW50YXhFcnJvciI6ZmFsc2UsInNob3dTZXR1cFByb2dyZXNzIjpmYWxzZSwibWF4TWVzc2FnZUNvdW50IjoxLCJyZXNldE9uUHJldmlld1Jlc3RhcnQiOmZhbHNlLCJzdGFuZGFsb25lIjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImdydXZib3hEYXJrIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="500px" width="100%"></iframe>

<h2 id="heading-next-steps">Next Steps</h2>
<p>While Kempo is still a work in progress, I plan to use it for my blog. I also plan to keep building in public and share more updates about the technical details behind its features and the challenges I encounter.</p>
<p>If you're interested in checking it out, you can get started at <a target="_blank" href="https://kempo.io">Kempo.io</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Understanding Honeypots: A Guide to Protecting Your Website from Spam Bots]]></title><description><![CDATA[The modern web is full of spam bots.
Bots are automated "users" that are programmed for specific tasks. While some bots are good, such as bots designed to monitor website health, many bots are created for malicious reasons. For example, spam bots are...]]></description><link>https://blog.thesshguy.com/understanding-honeypots</link><guid isPermaLink="true">https://blog.thesshguy.com/understanding-honeypots</guid><category><![CDATA[React]]></category><category><![CDATA[forms]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Fri, 29 Mar 2024 14:00:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1711591929274/f0de997f-347e-4039-84a1-096e3283f39c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>The modern web is full of spam bots.</p>
<p>Bots are automated "users" that are programmed for specific tasks. While <em>some</em> bots are good, such as bots designed to monitor website health, many bots are created for malicious reasons. For example, spam bots are created to post potentially harmful content wherever possible. This can lead to a strain on your server resources and lead to unnecessary data populating your databases.</p>
<p>Spam bots can achieve their goal by abusing HTML forms, which exist on almost every non-trivial website. This is how the bot might work:</p>
<ol>
<li><p>It inspects the page and looks for HTML <code>form</code> elements</p>
</li>
<li><p>It populates dummy data into the <code>input</code> fields</p>
</li>
<li><p>It submits the form to the server</p>
</li>
</ol>
<p>In other words, it mimics a normal user's behavior but provides information your application may not care about.</p>
<p>So what can we do to stop these bots?</p>
<h2 id="heading-honeypot-fields-can-trick-spam-bots">Honeypot fields can trick spam bots</h2>
<p>A honeypot field is a special input field hidden from real users, but visible to spam bots. These fields are hidden visually on the page and from screen readers, preventing most real users from filling them out. On the other hand, a spam bot works by inspecting the DOM and filling out any <code>input</code> fields it finds.</p>
<p>In other words, a spam bot will fill out fields even when it shouldn't be possible. This means all you need to do is reject any submissions on your server where the honeypot field was filled out.</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiSG9uZXlwb3QgRXhhbXBsZSIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxuaDEge1xuICBmb250LXNpemU6IDEuNXJlbTtcbn1cblxuZm9ybSB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gIHBhZGRpbmc6IDVweDtcbiAgbWF4LXdpZHRoOiAyMDBweDtcbn1cblxubGFiZWwge1xuICBkaXNwbGF5OiBmbGV4O1xuICBnYXA6IDI7XG4gIHBhZGRpbmc6IDVweDtcbn1cblxuaW5wdXQge1xuICBtYXJnaW4tbGVmdDogMTBweDtcbn1cblxuLmhpZGRlbiB7XG4gIGRpc3BsYXk6IG5vbmU7XG59In0sIi9BcHAuanMiOnsiY29kZSI6ImltcG9ydCB7IHVzZVN0YXRlIH0gZnJvbSBcInJlYWN0XCI7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpIHtcbiAgY29uc3QgW2Zvcm1WYWx1ZXMsIHNldEZvcm1WYWx1ZXNdID0gdXNlU3RhdGUoe30pO1xuXG4gIHJldHVybiAoXG4gICAgPD5cbiAgICAgIDxmb3JtXG4gICAgICAgIG9uU3VibWl0PXsoZSkgPT4ge1xuICAgICAgICAgIGNvbnNvbGUubG9nKGUudGFyZ2V0LmVsZW1lbnRzLnVzZXJuYW1lKTtcbiAgICAgICAgICBlLnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgc2V0Rm9ybVZhbHVlcyh7XG4gICAgICAgICAgICB1c2VybmFtZTogZS50YXJnZXQuZWxlbWVudHMudXNlcm5hbWUudmFsdWUsXG4gICAgICAgICAgICBoaWRkZW46IGUudGFyZ2V0LmVsZW1lbnRzLmhvbmV5cG90LnZhbHVlLFxuICAgICAgICAgIH0pO1xuICAgICAgICB9fVxuICAgICAgPlxuICAgICAgICA8bGFiZWw+XG4gICAgICAgICAgVXNlcm5hbWVcbiAgICAgICAgICA8aW5wdXQgbmFtZT1cInVzZXJuYW1lXCIgLz5cbiAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgPGxhYmVsIGNsYXNzTmFtZT1cImhpZGRlblwiPlxuICAgICAgICAgIEkgYW0gaGlkZGVuLCBkb24ndCBjbGljayBtZS5cbiAgICAgICAgICA8aW5wdXQgbmFtZT1cImhvbmV5cG90XCIgLz5cbiAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgPGJ1dHRvbj5TdWJtaXQ8L2J1dHRvbj5cbiAgICAgIDwvZm9ybT5cbiAgICAgIDxkaXY+XG4gICAgICAgIDxoMj5TdWJtaXR0ZWQgRm9ybSBWYWx1ZXM8L2gyPlxuICAgICAgICB7SlNPTi5zdHJpbmdpZnkoZm9ybVZhbHVlcywgbnVsbCwgMil9XG4gICAgICA8L2Rpdj5cbiAgICA8Lz5cbiAgKTtcbn1cbiJ9LCIvaW5kZXguanMiOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjp0cnVlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6ZmFsc2UsImNsb3NhYmxlVGFicyI6dHJ1ZSwicmVhZE9ubHkiOnRydWUsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOnRydWV9fX0sImdlbmVyYWwiOnsiYWN0aXZlRmlsZSI6IiIsInZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoiZHJhY3VsYSIsImVuYWJsZUZ1bGxTY3JlZW4iOmZhbHNlLCJlbmFibGVGb3JtYXR0aW5nIjpmYWxzZX0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0=" height="700" width="100%"></iframe>

<p>In the example above, you can see that we have a hidden field that indicates you shouldn't fill it out. Normally, you wouldn't be able to see the field, but if you end up in a state where the field is visible, you'll know that you should leave it alone.</p>
<p>You might be thinking that this is too obvious and a spam bot would know not to fill out the field, but it turns out that most spambots aren't too sophisticated and this is usually enough to <em>trick</em> them.</p>
<p>And that's how honeypot fields work. The next time you want to stop spam on your website, try reaching for a honeypot!</p>
]]></content:encoded></item><item><title><![CDATA[5 Must-Know Facts on React Event Handling]]></title><description><![CDATA[A key part of building any non-trivial front-end application involves managing user interactions.
React applications are no exception. When a user performs an action, it triggers an event that can be handled. What sets React apart is the way it manag...]]></description><link>https://blog.thesshguy.com/5-must-know-facts-on-react-event-handling</link><guid isPermaLink="true">https://blog.thesshguy.com/5-must-know-facts-on-react-event-handling</guid><category><![CDATA[React]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Wed, 17 May 2023 12:46:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1684298699446/3803f5b5-bcb9-43a3-ab90-413e12f98998.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>A key part of building any non-trivial front-end application involves managing user interactions.</p>
<p>React applications are no exception. When a user performs an action, it triggers an event that can be handled. What sets React apart is the way it manages these events. Understanding these differences will make it easier to build interactive React applications.</p>
<p>Keep these 5 tips in mind when handling events in React:</p>
<blockquote>
<p>TL;DR</p>
<ol>
<li><p>Don't Execute Functions; Pass References.</p>
</li>
<li><p>Wrap Functions That Take Arguments.</p>
</li>
<li><p>Use CamelCase for Event Handler Props.</p>
</li>
<li><p>Events Bubble Upwards but are Captured Downwards.</p>
</li>
<li><p>Beware of Default Browser Event Handlers.</p>
</li>
</ol>
</blockquote>
<h2 id="heading-1-dont-execute-functions-pass-references">1. Don't Execute Functions; Pass References.</h2>
<p>When you pass a function to an event handler, make sure you pass a <em>reference</em> to the function. A common problem I've seen is that a developer will execute the function instead of passing a reference:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> breakApp = <span class="hljs-function">() =&gt;</span> { <span class="hljs-comment">/* Stuff */</span> };

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> &lt;button onClick={breakApp()}&gt;Push to Break&lt;/button&gt;;
}
</code></pre>
<p>The <code>breakApp</code> function is executed <em>immediately</em> during <code>MyComponent</code>'s render cycle instead of when the button is clicked. Unfortunately for your users, this means they don't get the satisfaction of getting to break the app themselves. They're stuck with a broken app from the start.</p>
<p>So how do you fix the app, so that your <em>users</em> can break the app?</p>
<p>Just don't execute the function.</p>
<p>Instead, pass a function reference to the event handler. This will give React a chance to <em>remember</em> the function and execute it sometime in the future:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> breakApp = <span class="hljs-function">() =&gt;</span> { <span class="hljs-comment">/* Stuff */</span> };

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> &lt;button onClick={breakApp}&gt;Push to Break&lt;/button&gt;;
}
</code></pre>
<p>Passing a function reference to the event handler is easy enough to do, but there's a small problem. What if you want to pass arguments to your function? Keeping with the theme above, let's say that <code>breakApp</code> shouldn't work 100% of the time.</p>
<p>Instead, we'll pass a random number to <code>breakApp</code>, and based on that number, the function will succeed or fail. Once again, we need to make sure to pass a function reference to the event handler, so this won't work:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> breakApp = <span class="hljs-function">() =&gt;</span> { <span class="hljs-comment">/* Stuff */</span> };

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> rng = randomNumberGenerator();
    <span class="hljs-keyword">return</span> &lt;button onClick={breakApp(rng)}&gt;Push to Break&lt;/button&gt;;
}
</code></pre>
<p>Luckily, this is easy to fix.</p>
<p>All you have to do is...</p>
<h2 id="heading-2-wrap-functions-that-take-arguments">2. Wrap Functions That Take Arguments.</h2>
<p>You can't pass arguments to event handler functions.</p>
<p>At least not directly.</p>
<p>So now what?</p>
<p>Remember, you just need to pass a function reference to your event handlers for things to work correctly. So let's do that. Wrap your function that takes arguments with <em>another</em> function that just executes the first one.</p>
<p>Yes. Yes.</p>
<p>It's functions all the way down:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> breakApp = <span class="hljs-function">() =&gt;</span> { <span class="hljs-comment">/* Stuff */</span> };

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> rng = randomNumberGenerator();
    <span class="hljs-keyword">const</span> wrappedFunction = <span class="hljs-function">() =&gt;</span> breakApp(rng);
    <span class="hljs-keyword">return</span> &lt;button onClick={wrappedFunction}&gt;Push to Break&lt;/button&gt;;
}
</code></pre>
<p>Instead of executing <code>breakApp</code> immediately, it's wrapped by another function called <code>wrappedFunction</code>. Since <code>wrappedFunction</code> doesn't take any arguments, we can safely pass it to the <code>onClick</code> event handler.</p>
<h2 id="heading-3-use-camelcase-for-event-handler-props">3. Use CamelCase for Event Handler Props.</h2>
<p>Most <a target="_blank" href="https://blog.thesshguy.com/jsx-made-simple#heading-3-most-jsx-attributes-and-values-use-camelcase">JSX attributes use camel case</a>, and event handlers are no exception. This is different than standard HTML which uses <a target="_blank" href="https://html.spec.whatwg.org/multipage/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects">lowercase for event handler names</a>. In other words, you want to make sure to write <code>onClick</code> with a capital <code>C</code> instead of <code>onclick</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Wrong! `onclick`</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">BadComponent</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> &lt;button onclick={breakApp}&gt;Push to Break&lt;/button&gt;;
}

<span class="hljs-comment">// Right! `onClick`</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GoodComponent</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> &lt;button onClick={breakApp}&gt;Push to Break&lt;/button&gt;;
}
</code></pre>
<p>This is an easy mistake to make when you first start writing React code. Luckily React will warn you when you make a mistake like this, so it should be easy to recover from.</p>
<h2 id="heading-4-events-bubble-upwards-but-are-captured-downwards">4. Events Bubble Upwards but are Captured Downwards.</h2>
<p>Great!</p>
<p>So now you know how to attach an event handler to an element. But what if you wanted to track events for a <em>group</em> of elements? While you <em>could</em> attach an event handler to each element, there's another way.</p>
<h3 id="heading-events-bubble-up-the-component-hierarchy">Events bubble up the component hierarchy.</h3>
<p>This means when you click on an element, not only will React execute the element's <code>onClick</code> handler, but React will also execute every parent element's <code>onClick</code> handler.</p>
<p>Let's take a look at the example below.</p>
<p>Try clicking on each of the numbered blocks and also on the background.</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiYmxpc3NmdWxfZXVjbGlkIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiaHRtbCxcbmJvZHksXG4jcm9vdCB7XG4gIGhlaWdodDogMTAwJTtcbn1cblxuKiB7XG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XG4gIG1hcmdpbjogMDtcbn1cblxuYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG4jcm9vdCB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICB3aWR0aDogMTAwJTtcbiAgaGVpZ2h0OiAxMDAlO1xufVxuXG4uZXZlbnQtZGVtbyB7XG4gIGRpc3BsYXk6IGdyaWQ7XG4gIGdyaWQtdGVtcGxhdGUtY29sdW1uczogcmVwZWF0KDIsIDFmcik7XG4gIGdyaWQtdGVtcGxhdGUtcm93czogcmVwZWF0KDIsIDFmcik7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICB3aWR0aDogMTAwJTtcbiAgaGVpZ2h0OiAxMDAlO1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjMjgyYTM2O1xufVxuXG4uZXZlbnQtZGVtbyBidXR0b24ge1xuICBkaXNwbGF5OiBmbGV4O1xuICBhbGlnbi1pdGVtczogY2VudGVyO1xuICBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjtcbiAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7XG4gIGhlaWdodDogNTBweDtcbiAgd2lkdGg6IDEwMHB4O1xuICBiYWNrZ3JvdW5kLWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgYm9yZGVyLXJhZGl1czogMXJlbTtcbiAgY3Vyc29yOiBwb2ludGVyO1xuICB0cmFuc2l0aW9uLXByb3BlcnR5OiB0cmFuc2Zvcm0sIGJveC1zaGFkb3c7XG4gIHRyYW5zaXRpb24tZHVyYXRpb246IDMwMG1zO1xuICBtYXJnaW46IGF1dG87XG4gIGJhY2tncm91bmQtY29sb3I6IHdoaXRlO1xufVxuXG4uZXZlbnQtZGVtbyBidXR0b246aG92ZXIge1xuICB0cmFuc2Zvcm06IHNjYWxlKDEuMTUpO1xuICBib3gtc2hhZG93OiAwcHggMjRweCA0OHB4IHJnYigwLCAwLCAwLCAwLjE2KTtcbn1cbiJ9LCIvdHNjb25maWcuanNvbiI6eyJjb2RlIjoie1xuICBcImluY2x1ZGVcIjogW1xuICAgIFwiLi8qKi8qXCJcbiAgXSxcbiAgXCJjb21waWxlck9wdGlvbnNcIjoge1xuICAgIFwic3RyaWN0XCI6IHRydWUsXG4gICAgXCJlc01vZHVsZUludGVyb3BcIjogdHJ1ZSxcbiAgICBcImxpYlwiOiBbIFwiZG9tXCIsIFwiZXMyMDE1XCIgXSxcbiAgICBcImpzeFwiOiBcInJlYWN0LWpzeFwiXG4gIH1cbn0ifSwiL0FwcC50c3giOnsiY29kZSI6ImltcG9ydCB7IEV2ZW50RGVtbyB9IGZyb20gXCIuL0V2ZW50RGVtb1wiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKTogSlNYLkVsZW1lbnQge1xuICByZXR1cm4gPEV2ZW50RGVtbyAvPjtcbn1cbiJ9LCIvaW5kZXgudHN4Ijp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNC4wLjBcIlxuICB9LFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJAdHlwZXMvcmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJAdHlwZXMvcmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwidHlwZXNjcmlwdFwiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC50c3hcIlxufSJ9LCIvRXZlbnREZW1vLnRzeCI6eyJjb2RlIjoiZXhwb3J0IGZ1bmN0aW9uIEV2ZW50RGVtbygpIHtcbiAgcmV0dXJuIChcbiAgICA8ZGl2XG4gICAgICBjbGFzc05hbWU9XCJldmVudC1kZW1vXCJcbiAgICAgIG9uQ2xpY2s9eyhldmVudCkgPT4ge1xuICAgICAgICBpZiAoZXZlbnQuY3VycmVudFRhcmdldCA9PT0gZXZlbnQudGFyZ2V0KSB7XG4gICAgICAgICAgY29uc29sZS5sb2coXCJQYXJlbnQgY2xpY2tlZCFcIik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc29sZS5sb2coYEJ1dHRvbiAke2V2ZW50LnRhcmdldC5pZH0gY2xpY2tlZCFgKTtcbiAgICAgICAgfVxuICAgICAgfX1cbiAgICA+XG4gICAgICA8YnV0dG9uIGlkPVwib25lXCI+T25lPC9idXR0b24+XG4gICAgICA8YnV0dG9uIGlkPVwidHdvXCI+VHdvPC9idXR0b24+XG4gICAgICA8YnV0dG9uIGlkPVwidGhyZWVcIj5UaHJlZTwvYnV0dG9uPlxuICAgICAgPGJ1dHRvbiBpZD1cImZvdXJcIj5Gb3VyPC9idXR0b24+XG4gICAgPC9kaXY+XG4gICk7XG59XG4ifX0sInRlbXBsYXRlIjoicmVhY3QtdHMifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6ZmFsc2UsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOnRydWUsIndyYXBDb250ZW50IjpmYWxzZSwiY2xvc2FibGVUYWJzIjpmYWxzZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6ZmFsc2UsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2UsInN0YXJ0Um91dGUiOiIifX0sIkNvbnNvbGUiOnsidHlwZSI6ImNvbnNvbGUiLCJkYXRhIjp7InNob3dIZWFkZXIiOmZhbHNlLCJzaG93U3ludGF4RXJyb3IiOmZhbHNlLCJzaG93U2V0dXBQcm9ncmVzcyI6ZmFsc2UsIm1heE1lc3NhZ2VDb3VudCI6NTAwLCJyZXNldE9uUHJldmlld1Jlc3RhcnQiOmZhbHNlLCJzdGFuZGFsb25lIjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJhY3RpdmVGaWxlIjoiL0V2ZW50RGVtby50c3giLCJ2aXNpYmxlRmlsZXMiOltdLCJ0aGVtZSI6ImRyYWN1bGEiLCJlbmFibGVGdWxsU2NyZWVuIjpmYWxzZSwiZW5hYmxlRm9ybWF0dGluZyI6dHJ1ZX0sImxheW91dHMiOnsic21hbGwiOiInUHJldmlldydcbidDb25zb2xlJ1xuJ0VkaXRvciciLCJtZWRpdW0iOiInUHJldmlldyBQcmV2aWV3J1xuJ0VkaXRvciBDb25zb2xlJyJ9fX0=" height="700" width="100%"></iframe>

<p>Notice how there's only one event handler in the code above that's attached to the <code>div</code> element that wraps the <code>button</code> elements. Even though the buttons don't have <code>onClick</code> handlers, an event <em>bubbles up</em> to the parent element when you click one.</p>
<p>This lets us handle all the events in one place.</p>
<blockquote>
<p>This behavior is consistent for <em>all</em> React events, except for the <code>onScroll</code> event.</p>
</blockquote>
<p>So what if you don't want an event to bubble up to a parent element? Don't worry, React has you covered. All you need to do to opt out of this behavior is to stop the event from propagating:</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiYmxpc3NmdWxfZXVjbGlkIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiaHRtbCxcbmJvZHksXG4jcm9vdCB7XG4gIGhlaWdodDogMTAwJTtcbn1cblxuKiB7XG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XG4gIG1hcmdpbjogMDtcbn1cblxuYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG4jcm9vdCB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICB3aWR0aDogMTAwJTtcbiAgaGVpZ2h0OiAxMDAlO1xufVxuXG4uZXZlbnQtZGVtbyB7XG4gIGRpc3BsYXk6IGdyaWQ7XG4gIGdyaWQtdGVtcGxhdGUtY29sdW1uczogcmVwZWF0KDIsIDFmcik7XG4gIGdyaWQtdGVtcGxhdGUtcm93czogcmVwZWF0KDIsIDFmcik7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICB3aWR0aDogMTAwJTtcbiAgaGVpZ2h0OiAxMDAlO1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjMjgyYTM2O1xufVxuXG4uZXZlbnQtZGVtbyBidXR0b24ge1xuICBkaXNwbGF5OiBmbGV4O1xuICBhbGlnbi1pdGVtczogY2VudGVyO1xuICBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjtcbiAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7XG4gIGhlaWdodDogNTBweDtcbiAgd2lkdGg6IDEwMHB4O1xuICBiYWNrZ3JvdW5kLWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgYm9yZGVyLXJhZGl1czogMXJlbTtcbiAgY3Vyc29yOiBwb2ludGVyO1xuICB0cmFuc2l0aW9uLXByb3BlcnR5OiB0cmFuc2Zvcm0sIGJveC1zaGFkb3c7XG4gIHRyYW5zaXRpb24tZHVyYXRpb246IDMwMG1zO1xuICBtYXJnaW46IGF1dG87XG4gIGJhY2tncm91bmQtY29sb3I6IHdoaXRlO1xufVxuXG4uZXZlbnQtZGVtbyBidXR0b246aG92ZXIge1xuICB0cmFuc2Zvcm06IHNjYWxlKDEuMTUpO1xuICBib3gtc2hhZG93OiAwcHggMjRweCA0OHB4IHJnYigwLCAwLCAwLCAwLjE2KTtcbn1cbiJ9LCIvdHNjb25maWcuanNvbiI6eyJjb2RlIjoie1xuICBcImluY2x1ZGVcIjogW1xuICAgIFwiLi8qKi8qXCJcbiAgXSxcbiAgXCJjb21waWxlck9wdGlvbnNcIjoge1xuICAgIFwic3RyaWN0XCI6IHRydWUsXG4gICAgXCJlc01vZHVsZUludGVyb3BcIjogdHJ1ZSxcbiAgICBcImxpYlwiOiBbIFwiZG9tXCIsIFwiZXMyMDE1XCIgXSxcbiAgICBcImpzeFwiOiBcInJlYWN0LWpzeFwiXG4gIH1cbn0ifSwiL0FwcC50c3giOnsiY29kZSI6ImltcG9ydCB7IEV2ZW50RGVtbyB9IGZyb20gXCIuL0V2ZW50RGVtb1wiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKTogSlNYLkVsZW1lbnQge1xuICByZXR1cm4gPEV2ZW50RGVtbyAvPjtcbn1cbiJ9LCIvaW5kZXgudHN4Ijp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNC4wLjBcIlxuICB9LFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJAdHlwZXMvcmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJAdHlwZXMvcmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwidHlwZXNjcmlwdFwiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC50c3hcIlxufSJ9LCIvRXZlbnREZW1vLnRzeCI6eyJjb2RlIjoiZXhwb3J0IGZ1bmN0aW9uIEV2ZW50RGVtbygpIHtcbiAgcmV0dXJuIChcbiAgICA8ZGl2XG4gICAgICBjbGFzc05hbWU9XCJldmVudC1kZW1vXCJcbiAgICAgIG9uQ2xpY2s9eyhldmVudCkgPT4ge1xuICAgICAgICBpZiAoZXZlbnQuY3VycmVudFRhcmdldCA9PT0gZXZlbnQudGFyZ2V0KSB7XG4gICAgICAgICAgY29uc29sZS5sb2coXCJQYXJlbnQgY2xpY2tlZCFcIik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgY29uc29sZS5sb2coYEJ1dHRvbiAke2V2ZW50LnRhcmdldC5pZH0gY2xpY2tlZCFgKTtcbiAgICAgICAgfVxuICAgICAgfX1cbiAgICA+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIGlkPVwib25lXCJcbiAgICAgICAgb25DbGljaz17KGV2ZW50KSA9PiB7XG4gICAgICAgICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgY29uc29sZS5sb2coXCJZb3UgY2xpY2tlZCBidXR0b24gMSFcIik7XG4gICAgICAgIH19XG4gICAgICA+XG4gICAgICAgIE9uZVxuICAgICAgPC9idXR0b24+XG4gICAgICA8YnV0dG9uIGlkPVwidHdvXCI+VHdvPC9idXR0b24+XG4gICAgICA8YnV0dG9uIGlkPVwidGhyZWVcIj5UaHJlZTwvYnV0dG9uPlxuICAgICAgPGJ1dHRvbiBpZD1cImZvdXJcIj5Gb3VyPC9idXR0b24+XG4gICAgPC9kaXY+XG4gICk7XG59XG4ifX0sInRlbXBsYXRlIjoicmVhY3QtdHMifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6ZmFsc2UsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOnRydWUsIndyYXBDb250ZW50IjpmYWxzZSwiY2xvc2FibGVUYWJzIjpmYWxzZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6ZmFsc2UsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2UsInN0YXJ0Um91dGUiOiIifX0sIkNvbnNvbGUiOnsidHlwZSI6ImNvbnNvbGUiLCJkYXRhIjp7InNob3dIZWFkZXIiOmZhbHNlLCJzaG93U3ludGF4RXJyb3IiOmZhbHNlLCJzaG93U2V0dXBQcm9ncmVzcyI6ZmFsc2UsIm1heE1lc3NhZ2VDb3VudCI6NTAwLCJyZXNldE9uUHJldmlld1Jlc3RhcnQiOmZhbHNlLCJzdGFuZGFsb25lIjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJhY3RpdmVGaWxlIjoiL0V2ZW50RGVtby50c3giLCJ2aXNpYmxlRmlsZXMiOltdLCJ0aGVtZSI6ImRyYWN1bGEiLCJlbmFibGVGdWxsU2NyZWVuIjpmYWxzZSwiZW5hYmxlRm9ybWF0dGluZyI6dHJ1ZX0sImxheW91dHMiOnsic21hbGwiOiInUHJldmlldydcbidDb25zb2xlJ1xuJ0VkaXRvciciLCJtZWRpdW0iOiInUHJldmlldyBQcmV2aWV3J1xuJ0VkaXRvciBDb25zb2xlJyJ9fX0=" height="700" width="100%"></iframe>

<p>This time, button one has its own event handler. When you click this button, the first thing we do is call <code>event.stopPropagation()</code>. This tells React to make sure it doesn't execute the <code>onClick</code> handler for any parent elements.</p>
<p>Now when you click button one, we <em>only</em> log the text in the button's handler.</p>
<h3 id="heading-but-what-if-you-still-want-the-parent-to-respond-to-the-button-click">But what if you <em>still</em> want the parent to respond to the button click?</h3>
<p>In rare cases, you still might want to handle the button click from a parent handler.</p>
<p>Let's say you were using a 3rd party button component that calls <code>event.stopPropagation()</code> internally when clicked. In this case, you can't remove that code because you don't own it. In other words, there's no way for you to handle the click from a parent element...</p>
<p>...or is there?</p>
<p><strong>You can <em>capture</em> the event.</strong></p>
<h3 id="heading-react-captures-events-down-the-element-hierarchy"><strong>React captures events down the element hierarchy.</strong></h3>
<p>You can attach special event handlers to your React components that let you handle an event <em>even if</em> a child element calls <code>event.stopPropagation()</code>.</p>
<p>All you need to do is add the word <code>Capture</code> to the end of your event handlers. For click events, the handler is called <code>onClickCapture</code>. To understand <em>why</em> this works, we need to take a closer look at the order in which these event handlers are executed:</p>
<p>When you trigger an event (e.g. by clicking a button), it moves in the following way:</p>
<ol>
<li><p>It moves down the element hierarchy, calling <code>onClickCapture</code> for every element</p>
</li>
<li><p>It calls the <code>onClick</code> event handler for the element you clicked</p>
</li>
<li><p>It moves up the element hierarchy, calling <code>onClick</code> for every element</p>
</li>
</ol>
<p>This works because your code runs during <em>step 1</em>, but <code>event.stopPropagation()</code> prevents <em>step 3</em>.</p>
<p>Try clicking on the button in the example below. It should log the order of the event handlers.</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiYmxpc3NmdWxfZXVjbGlkIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiaHRtbCxcbmJvZHksXG4jcm9vdCB7XG4gIGhlaWdodDogMTAwJTtcbn1cblxuKiB7XG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XG4gIG1hcmdpbjogMDtcbn1cblxuYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG4jcm9vdCB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICB3aWR0aDogMTAwJTtcbiAgaGVpZ2h0OiAxMDAlO1xufVxuXG4uZXZlbnQtZGVtbyB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICB3aWR0aDogMTAwJTtcbiAgaGVpZ2h0OiAxMDAlO1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjMjgyYTM2O1xufVxuXG4uZXZlbnQtZGVtbyBidXR0b24ge1xuICBkaXNwbGF5OiBmbGV4O1xuICBhbGlnbi1pdGVtczogY2VudGVyO1xuICBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjtcbiAgYm9yZGVyOiAxcHggc29saWQgYmxhY2s7XG4gIGhlaWdodDogNTBweDtcbiAgd2lkdGg6IDEwMHB4O1xuICBiYWNrZ3JvdW5kLWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgYm9yZGVyLXJhZGl1czogMXJlbTtcbiAgY3Vyc29yOiBwb2ludGVyO1xuICB0cmFuc2l0aW9uLXByb3BlcnR5OiB0cmFuc2Zvcm0sIGJveC1zaGFkb3c7XG4gIHRyYW5zaXRpb24tZHVyYXRpb246IDMwMG1zO1xuICBtYXJnaW46IGF1dG87XG4gIGJhY2tncm91bmQtY29sb3I6IHdoaXRlO1xufVxuXG4uZXZlbnQtZGVtbyBidXR0b246aG92ZXIge1xuICB0cmFuc2Zvcm06IHNjYWxlKDEuMTUpO1xuICBib3gtc2hhZG93OiAwcHggMjRweCA0OHB4IHJnYigwLCAwLCAwLCAwLjE2KTtcbn1cbiJ9LCIvdHNjb25maWcuanNvbiI6eyJjb2RlIjoie1xuICBcImluY2x1ZGVcIjogW1xuICAgIFwiLi8qKi8qXCJcbiAgXSxcbiAgXCJjb21waWxlck9wdGlvbnNcIjoge1xuICAgIFwic3RyaWN0XCI6IHRydWUsXG4gICAgXCJlc01vZHVsZUludGVyb3BcIjogdHJ1ZSxcbiAgICBcImxpYlwiOiBbIFwiZG9tXCIsIFwiZXMyMDE1XCIgXSxcbiAgICBcImpzeFwiOiBcInJlYWN0LWpzeFwiXG4gIH1cbn0ifSwiL0FwcC50c3giOnsiY29kZSI6ImltcG9ydCB7IEV2ZW50RGVtbyB9IGZyb20gXCIuL0V2ZW50RGVtb1wiO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKTogSlNYLkVsZW1lbnQge1xuICByZXR1cm4gPEV2ZW50RGVtbyAvPjtcbn1cbiJ9LCIvaW5kZXgudHN4Ijp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNC4wLjBcIlxuICB9LFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJAdHlwZXMvcmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJAdHlwZXMvcmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwidHlwZXNjcmlwdFwiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC50c3hcIlxufSJ9LCIvRXZlbnREZW1vLnRzeCI6eyJjb2RlIjoiZXhwb3J0IGZ1bmN0aW9uIEV2ZW50RGVtbygpIHtcbiAgcmV0dXJuIChcbiAgICA8ZGl2XG4gICAgICBjbGFzc05hbWU9XCJldmVudC1kZW1vXCJcbiAgICAgIG9uQ2xpY2s9e2xvZyhcIm9uQ2xpY2sgUGFyZW50XCIpfVxuICAgICAgb25DbGlja0NhcHR1cmU9e2xvZyhcIm9uQ2xpY2tDYXB0dXJlIFBhcmVudFwiKX1cbiAgICA+XG4gICAgICA8YnV0dG9uXG4gICAgICAgIG9uQ2xpY2s9e2xvZyhcIm9uQ2xpY2sgQnV0dG9uXCIpfVxuICAgICAgICBvbkNsaWNrQ2FwdHVyZT17bG9nKFwib25DbGlja0NhcHR1cmUgQnV0dG9uXCIpfVxuICAgICAgPlxuICAgICAgICBCdXR0b25cbiAgICAgIDwvYnV0dG9uPlxuICAgIDwvZGl2PlxuICApO1xufVxuXG5mdW5jdGlvbiBsb2cobWVzc2FnZSkge1xuICByZXR1cm4gKCkgPT4gY29uc29sZS5sb2cobWVzc2FnZSk7XG59XG4ifX0sInRlbXBsYXRlIjoicmVhY3QtdHMifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6ZmFsc2UsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOnRydWUsIndyYXBDb250ZW50IjpmYWxzZSwiY2xvc2FibGVUYWJzIjpmYWxzZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6ZmFsc2UsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2UsInN0YXJ0Um91dGUiOiIifX0sIkNvbnNvbGUiOnsidHlwZSI6ImNvbnNvbGUiLCJkYXRhIjp7InNob3dIZWFkZXIiOmZhbHNlLCJzaG93U3ludGF4RXJyb3IiOmZhbHNlLCJzaG93U2V0dXBQcm9ncmVzcyI6ZmFsc2UsIm1heE1lc3NhZ2VDb3VudCI6NTAwLCJyZXNldE9uUHJldmlld1Jlc3RhcnQiOmZhbHNlLCJzdGFuZGFsb25lIjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJhY3RpdmVGaWxlIjoiL0V2ZW50RGVtby50c3giLCJ2aXNpYmxlRmlsZXMiOltdLCJ0aGVtZSI6ImRyYWN1bGEiLCJlbmFibGVGdWxsU2NyZWVuIjpmYWxzZSwiZW5hYmxlRm9ybWF0dGluZyI6dHJ1ZX0sImxheW91dHMiOnsic21hbGwiOiInUHJldmlldydcbidDb25zb2xlJ1xuJ0VkaXRvciciLCJtZWRpdW0iOiInUHJldmlldyBQcmV2aWV3J1xuJ0VkaXRvciBDb25zb2xlJyJ9fX0=" height="700" width="100%"></iframe>

<h2 id="heading-5-beware-of-default-browser-event-handlers">5. Beware of Default Browser Event Handlers.</h2>
<p>Some events have a default browser implementation.</p>
<p>If you're not careful, this can lead to some unexpected behavior. For example, most browsers refresh the entire page for the <code>form</code> element's <code>onSubmit</code> event.</p>
<p>For <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/SPA">Single Page Applications</a>, it's common to prevent this behavior and handle the submission yourself.</p>
<p>Luckily, it's easy to do this with React:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyForm</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">onSubmit</span>(<span class="hljs-params">event</span>) </span>{
        event.preventDefault();
        <span class="hljs-comment">// Do other form stuff after...</span>
    }

    <span class="hljs-keyword">return</span> (
        &lt;form onSubmit={onSubmit}&gt;
            {<span class="hljs-comment">/* Form stuff goes here */</span>}
        &lt;/form&gt;
    );
}
</code></pre>
<p>The call to <code>event.preventDefault()</code> prevents the browser's default implementation.</p>
<p>In the example below, the second form prevents the browser's default implementation while the first form doesn't. Try clicking both submit buttons to see the difference.</p>
<p>When the first button is clicked, the entire page refreshes.</p>
<p>To make this more obvious, enter a value into the <code>input</code> field and <em>then</em> submit the form. When the page refreshes from the first button click, we lose the value stored in the input element.</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiYmxpc3NmdWxfZXVjbGlkIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiaHRtbCxcbmJvZHksXG4jcm9vdCB7XG4gIGhlaWdodDogMTAwJTtcbn1cblxuKiB7XG4gIGJveC1zaXppbmc6IGJvcmRlci1ib3g7XG4gIG1hcmdpbjogMDtcbn1cblxuYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG4jcm9vdCB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICB3aWR0aDogMTAwJTtcbiAgaGVpZ2h0OiAxMDAlO1xufVxuXG4uZXZlbnQtZGVtbyB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gIGdhcDogMnJlbTtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7XG4gIHdpZHRoOiAxMDAlO1xuICBoZWlnaHQ6IDEwMCU7XG4gIGJhY2tncm91bmQtY29sb3I6ICMyODJhMzY7XG59XG5cbmxhYmVsIHtcbiAgY29sb3I6IHdoaXRlO1xuICBkaXNwbGF5OiBmbGV4O1xuICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICBnYXA6IDAuMjVyZW07XG59XG5cbmZvcm0ge1xuICBkaXNwbGF5OiBmbGV4O1xuICBnYXA6IDFyZW07XG4gIGFsaWduLWl0ZW1zOiBmbGV4LWVuZDtcbn1cblxuaW5wdXQge1xuICBoZWlnaHQ6IDMwcHg7XG4gIGJvcmRlci1yYWRpdXM6IDAuNXJlbTtcbiAgZm9udC1zaXplOiAxLjI1cmVtO1xuICBwYWRkaW5nOiAuNXJlbTtcbn1cblxuLmV2ZW50LWRlbW8gYnV0dG9uIHtcbiAgZGlzcGxheTogZmxleDtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAganVzdGlmeS1jb250ZW50OiBjZW50ZXI7XG4gIGJvcmRlcjogMXB4IHNvbGlkIGJsYWNrO1xuICBoZWlnaHQ6IDMwcHg7XG4gIHdpZHRoOiAxMDBweDtcbiAgYmFja2dyb3VuZC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIGJvcmRlci1yYWRpdXM6IDFyZW07XG4gIGN1cnNvcjogcG9pbnRlcjtcbiAgdHJhbnNpdGlvbi1wcm9wZXJ0eTogdHJhbnNmb3JtLCBib3gtc2hhZG93O1xuICB0cmFuc2l0aW9uLWR1cmF0aW9uOiAzMDBtcztcbiAgYmFja2dyb3VuZC1jb2xvcjogd2hpdGU7XG59XG5cbi5ldmVudC1kZW1vIGJ1dHRvbjpob3ZlciB7XG4gIHRyYW5zZm9ybTogc2NhbGUoMS4xNSk7XG4gIGJveC1zaGFkb3c6IDBweCAyNHB4IDQ4cHggcmdiKDAsIDAsIDAsIDAuMTYpO1xufVxuIn0sIi90c2NvbmZpZy5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiaW5jbHVkZVwiOiBbXG4gICAgXCIuLyoqLypcIlxuICBdLFxuICBcImNvbXBpbGVyT3B0aW9uc1wiOiB7XG4gICAgXCJzdHJpY3RcIjogdHJ1ZSxcbiAgICBcImVzTW9kdWxlSW50ZXJvcFwiOiB0cnVlLFxuICAgIFwibGliXCI6IFsgXCJkb21cIiwgXCJlczIwMTVcIiBdLFxuICAgIFwianN4XCI6IFwicmVhY3QtanN4XCJcbiAgfVxufSJ9LCIvQXBwLnRzeCI6eyJjb2RlIjoiaW1wb3J0IHsgRXZlbnREZW1vIH0gZnJvbSBcIi4vRXZlbnREZW1vXCI7XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpOiBKU1guRWxlbWVudCB7XG4gIHJldHVybiA8RXZlbnREZW1vIC8+O1xufVxuIn0sIi9pbmRleC50c3giOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcIkB0eXBlcy9yZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcIkB0eXBlcy9yZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJ0eXBlc2NyaXB0XCI6IFwiXjQuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LnRzeFwiXG59In0sIi9FdmVudERlbW8udHN4Ijp7ImNvZGUiOiJleHBvcnQgZnVuY3Rpb24gRXZlbnREZW1vKCkge1xuICByZXR1cm4gKFxuICAgIDxkaXYgY2xhc3NOYW1lPVwiZXZlbnQtZGVtb1wiPlxuICAgICAgPGZvcm1cbiAgICAgICAgb25TdWJtaXQ9eyhldmVudCkgPT4ge1xuICAgICAgICAgIGNvbnNvbGUubG9nKFwiRm9ybSBPbmUgU3VibWl0dGVkIVwiKTtcbiAgICAgICAgfX1cbiAgICAgID5cbiAgICAgICAgPGxhYmVsPlxuICAgICAgICAgIEVtYWlsOlxuICAgICAgICAgIDxpbnB1dCAvPlxuICAgICAgICA8L2xhYmVsPlxuICAgICAgICA8YnV0dG9uPlN1Ym1pdDwvYnV0dG9uPlxuICAgICAgPC9mb3JtPlxuXG4gICAgICA8Zm9ybVxuICAgICAgICBvblN1Ym1pdD17KGV2ZW50KSA9PiB7XG4gICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICBjb25zb2xlLmxvZyhcIkZvcm0gVHdvIFN1Ym1pdHRlZCFcIik7XG4gICAgICAgIH19XG4gICAgICA+XG4gICAgICAgIDxsYWJlbD5cbiAgICAgICAgICBFbWFpbDpcbiAgICAgICAgICA8aW5wdXQgLz5cbiAgICAgICAgPC9sYWJlbD5cbiAgICAgICAgPGJ1dHRvbj5TdWJtaXQ8L2J1dHRvbj5cbiAgICAgIDwvZm9ybT5cbiAgICA8L2Rpdj5cbiAgKTtcbn1cbiJ9fSwidGVtcGxhdGUiOiJyZWFjdC10cyJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOmZhbHNlLCJjbG9zYWJsZVRhYnMiOmZhbHNlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjpmYWxzZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZSwic3RhcnRSb3V0ZSI6IiJ9fSwiQ29uc29sZSI6eyJ0eXBlIjoiY29uc29sZSIsImRhdGEiOnsic2hvd0hlYWRlciI6ZmFsc2UsInNob3dTeW50YXhFcnJvciI6ZmFsc2UsInNob3dTZXR1cFByb2dyZXNzIjpmYWxzZSwibWF4TWVzc2FnZUNvdW50Ijo1MDAsInJlc2V0T25QcmV2aWV3UmVzdGFydCI6ZmFsc2UsInN0YW5kYWxvbmUiOmZhbHNlfX19LCJnZW5lcmFsIjp7ImFjdGl2ZUZpbGUiOiIvRXZlbnREZW1vLnRzeCIsInZpc2libGVGaWxlcyI6W10sInRoZW1lIjoiZHJhY3VsYSIsImVuYWJsZUZ1bGxTY3JlZW4iOmZhbHNlLCJlbmFibGVGb3JtYXR0aW5nIjp0cnVlfSwibGF5b3V0cyI6eyJzbWFsbCI6IidQcmV2aWV3J1xuJ0NvbnNvbGUnXG4nRWRpdG9yJyIsIm1lZGl1bSI6IidQcmV2aWV3IFByZXZpZXcnXG4nRWRpdG9yIENvbnNvbGUnIn19fQ==" height="700" width="100%"></iframe>

<h3 id="heading-preventdefault-vs-stoppropagation">preventDefault vs stopPropagation</h3>
<p>One thing to keep in mind is that while it may seem like there's some overlap between <code>preventDefault</code> and <code>stopPropagation</code>, both methods have different purposes:</p>
<ul>
<li><p><code>preventDefault</code>: This has to do with preventing the <em>browser's</em> event handlers</p>
</li>
<li><p><code>stopPropagation</code>: This has to do with preventing the <em>developer's</em> event handlers on a parent element</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Conditional React: 3 Ways To Control Your Component Flow]]></title><description><![CDATA[If you've written any code, you've probably come across control flow.
But what is it?
Here's what MDN says about control flow:

The control flow is the order in which the computer executes statements in a script.

So control flow describes what parts...]]></description><link>https://blog.thesshguy.com/conditional-react</link><guid isPermaLink="true">https://blog.thesshguy.com/conditional-react</guid><category><![CDATA[React]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Wed, 05 Apr 2023 12:45:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1680669882990/d80777a8-bd34-4fee-a0fa-67c53d58ca02.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>If you've written any code, you've probably come across control flow.</p>
<p>But what is it?</p>
<p>Here's what <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/Control_flow">MDN says about control flow</a>:</p>
<blockquote>
<p>The <em>control flow</em> is the order in which the computer executes statements in a script.</p>
</blockquote>
<p>So control flow describes what parts of your code run and in what order. Cool...but that's not that interesting. It would be more valuable if we could <em>manipulate</em> the control flow and change your code's execution order.</p>
<p>Luckily most programming languages have built-in ways to do this.</p>
<p>While React isn't a programming language in its own right, it relies heavily on the fundamentals of JavaScript. Understanding how JavaScript deals with control flow will help you write flexible React components that change how they render.</p>
<p>There are 3 core ways to control how your components render.</p>
<h2 id="heading-1-the-ifelse-statement">1. The if/else statement</h2>
<p>The most common conditional is the basic <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/if...else">if/else statement</a>:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">myFunc</span>(<span class="hljs-params">name</span>) </span>{
    <span class="hljs-keyword">if</span> (name === <span class="hljs-string">'thesshguy'</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-string">'Cool'</span>;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-string">'Not Cool'</span>;
}
</code></pre>
<p>An if/else statement lets you return <em>early</em> when some condition is met. This same principle can be used in your React components. You can return different JSX elements based on the value of a prop:</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiUmVhY3QgQ29uZGl0aW9uYWxzIGlmL2Vsc2UgU3RhdGVtZW50IiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG5tYWluIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgZ2FwOiAzMnB4O1xuICAtLXNoYWRvdy1jb2xvcjogMjAzZGVnIDEwMCUgNyU7XG59XG5cbm1haW4gZGl2IHtcbiAgYm9yZGVyOiAycHggc29saWQgIzBjNGE2ZTtcbiAgcGFkZGluZzogMTZweDtcbiAgYm9yZGVyLXJhZGl1czogOHB4O1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjMGM0YTZlO1xuICBjb2xvcjogI2YwZjlmZjtcbiAgd2lkdGg6IDUwJTtcbiAgdGV4dC1hbGlnbjogY2VudGVyO1xuICBib3gtc2hhZG93OiAwLjRweCAwLjNweCAwLjdweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuMjIpLFxuICAgIDEuOXB4IDEuN3B4IDMuNHB4IC0wLjNweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuMzgpLFxuICAgIDQuMXB4IDMuOHB4IDcuNXB4IC0wLjdweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuNTUpLFxuICAgIDkuMnB4IDguM3B4IDE2LjdweCAtMXB4IGhzbCh2YXIoLS1zaGFkb3ctY29sb3IpIC8gMC43MSk7XG59In0sIi9BcHAuanMiOnsiY29kZSI6ImZ1bmN0aW9uIENvb2xDaGVja2VyKHByb3BzKSB7XG4gIGlmIChwcm9wcy5uYW1lID09PSAndGhlc3NoZ3V5Jykge1xuICAgIHJldHVybiA8ZGl2Pntwcm9wcy5uYW1lfSBpcyBjb29sITwvZGl2PjtcbiAgfVxuXG4gIHJldHVybiA8ZGl2Pntwcm9wcy5uYW1lfSBpcyBub3QgY29vbCDwn6W4ITwvZGl2Pjtcbn1cblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICByZXR1cm4gKFxuICAgIDxtYWluPlxuICAgICAgPENvb2xDaGVja2VyIG5hbWU9XCJ0aGVzc2hndXlcIiAvPlxuICAgICAgPENvb2xDaGVja2VyIG5hbWU9XCJvdGhlcmd1eVwiIC8+XG4gICAgPC9tYWluPlxuICApO1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IFwiLi9zdHlsZXMuY3NzXCI7XG5cbmltcG9ydCBBcHAgZnJvbSBcIi4vQXBwXCI7XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+XG4pOyJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjQuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOmZhbHNlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjpmYWxzZSwid3JhcENvbnRlbnQiOmZhbHNlLCJjbG9zYWJsZVRhYnMiOmZhbHNlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOnRydWUsInJlbGF0aXZlUGF0aCI6IiJ9fX0sImdlbmVyYWwiOnsiYWN0aXZlRmlsZSI6Ii9BcHAuanMiLCJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImRyYWN1bGEiLCJlbmFibGVGdWxsU2NyZWVuIjpmYWxzZSwiZW5hYmxlRm9ybWF0dGluZyI6dHJ1ZX0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0=" height="600" width="100%"></iframe>

<p>Notice how we return different JSX based on the <code>name</code> prop.</p>
<p>Try changing the names to see what happens.</p>
<h2 id="heading-2-the-ternary-operator">2. The ternary operator</h2>
<p>If you've never seen a ternary operator before, it might look a little confusing at first.</p>
<p>The ternary operator takes three inputs and returns a single value. The three inputs are a <strong>condition</strong>, a <strong>truthy value</strong>, and a <strong>falsy value</strong>. <strong>Truthy</strong> and <strong>falsy</strong> just mean values that are considered <code>true</code> or <code>false</code> when converted to a <code>Boolean</code>.</p>
<p>All ternary operators follow the same format in JavaScript:</p>
<ol>
<li><p>A condition followed by a <code>?</code></p>
</li>
<li><p>An expression to execute if the condition is <strong>truthy</strong> followed by a <code>:</code></p>
</li>
<li><p>An expression to execute if the condition is <strong>falsy</strong></p>
</li>
</ol>
<p><strong>Note:</strong> If that's still not clear, you can use the handy acronym <strong><em>W.T.F.</em></strong> to help you remember: <code>what (cond) ? true : false</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">myFunc</span>(<span class="hljs-params">name</span>) </span>{
    <span class="hljs-comment">/*
     * W.T.F.
     * 1. What: name === 'thesshguy'
     * 2. True: 'Cool'
     * 3. False: 'Not Cool'
     */</span>
    <span class="hljs-keyword">return</span> name === <span class="hljs-string">'thesshguy'</span> ? <span class="hljs-string">'Cool'</span> : <span class="hljs-string">'Not Cool'</span>;
}
</code></pre>
<p>If that sounds familiar, it should.</p>
<p>The ternary operator is often used as an alternative to the if/else statement.</p>
<p>Let's rewrite the example above using a ternary operator:</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiUmVhY3QgQ29uZGl0aW9uYWxzIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG5tYWluIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgZ2FwOiAzMnB4O1xuICAtLXNoYWRvdy1jb2xvcjogMjAzZGVnIDEwMCUgNyU7XG59XG5cbm1haW4gZGl2IHtcbiAgYm9yZGVyOiAycHggc29saWQgIzBjNGE2ZTtcbiAgcGFkZGluZzogMTZweDtcbiAgYm9yZGVyLXJhZGl1czogOHB4O1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjMGM0YTZlO1xuICBjb2xvcjogI2YwZjlmZjtcbiAgd2lkdGg6IDUwJTtcbiAgdGV4dC1hbGlnbjogY2VudGVyO1xuICBib3gtc2hhZG93OiAwLjRweCAwLjNweCAwLjdweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuMjIpLFxuICAgIDEuOXB4IDEuN3B4IDMuNHB4IC0wLjNweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuMzgpLFxuICAgIDQuMXB4IDMuOHB4IDcuNXB4IC0wLjdweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuNTUpLFxuICAgIDkuMnB4IDguM3B4IDE2LjdweCAtMXB4IGhzbCh2YXIoLS1zaGFkb3ctY29sb3IpIC8gMC43MSk7XG59In0sIi9BcHAuanMiOnsiY29kZSI6ImZ1bmN0aW9uIENvb2xDaGVja2VyKHByb3BzKSB7XG4gIHJldHVybiBwcm9wcy5uYW1lID09PSBcInRoZXNzaGd1eVwiXG4gICAgPyA8ZGl2Pntwcm9wcy5uYW1lfSBpcyBjb29sITwvZGl2PlxuICAgIDogPGRpdj57cHJvcHMubmFtZX0gaXMgbm90IGNvb2wg8J+luCE8L2Rpdj47XG59XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpIHtcbiAgcmV0dXJuIChcbiAgICA8bWFpbj5cbiAgICAgIDxDb29sQ2hlY2tlciBuYW1lPVwidGhlc3NoZ3V5XCIgLz5cbiAgICAgIDxDb29sQ2hlY2tlciBuYW1lPVwib3RoZXJndXlcIiAvPlxuICAgIDwvbWFpbj5cbiAgKTtcbn1cbiJ9LCIvaW5kZXguanMiOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6ZmFsc2UsIndyYXBDb250ZW50IjpmYWxzZSwiY2xvc2FibGVUYWJzIjpmYWxzZSwicmVhZE9ubHkiOmZhbHNlLCJzaG93UmVhZE9ubHkiOmZhbHNlfX0sIlByZXZpZXciOnsidHlwZSI6InByZXZpZXciLCJkYXRhIjp7InNob3dOYXZpZ2F0b3IiOmZhbHNlLCJzaG93T3BlbkluQ29kZVNhbmRib3giOmZhbHNlLCJzaG93UmVmcmVzaEJ1dHRvbiI6dHJ1ZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5Ijp0cnVlfX19LCJnZW5lcmFsIjp7ImFjdGl2ZUZpbGUiOiIvQXBwLmpzIiwidmlzaWJsZUZpbGVzIjpbIi9BcHAuanMiXSwidGhlbWUiOiJkcmFjdWxhIiwiZW5hYmxlRnVsbFNjcmVlbiI6ZmFsc2UsImVuYWJsZUZvcm1hdHRpbmciOnRydWV9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvcidcbidFZGl0b3InXG4nUHJldmlldycifX19" height="600" width="100%"></iframe>

<p>Easy!</p>
<p>But why would you ever choose to use the ternary operator? Is the only difference the syntax, or are there <em>other</em> benefits?</p>
<p>Great questions!</p>
<h3 id="heading-ternary-operators-can-be-used-in-jsx">Ternary operators can be used in JSX</h3>
<p>One drawback of the if/else statement is that it can't be used inside of JSX:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        &lt;div&gt;
            {
                <span class="hljs-keyword">if</span> (someCondition) {
                    <span class="hljs-keyword">return</span> <span class="hljs-string">'True'</span>;
                }
            }
        &lt;/div&gt;
    );
}
</code></pre>
<p>The reason you can't do this has to do with how JSX is converted to HTML under the hood. If you want an in-depth explanation, you can read my post about <a target="_blank" href="https://blog.thesshguy.com/react-jsx-to-html">how React converts JSX to HTML</a>. If you want to skip that post (I totally understand if you do 🙂), here's a quick recap.</p>
<p>When your React code is built, the above gets converted to the following:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> React.createElement(
        <span class="hljs-string">'div'</span>,
        {},
        <span class="hljs-keyword">if</span> (someCondition) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">'True'</span>;
        }
    );
}
</code></pre>
<p>A JSX element's children are passed as arguments to <code>React.createElement</code>.</p>
<p>The issue is that an if/else statement can't be used as a value in JavaScript. It can't be passed as a function argument. Since it doesn't have a value, JavaScript can't do anything with it.</p>
<p>It's not a limitation with React, but rather a limitation with JavaScript.</p>
<p>On the other hand, a ternary operator <em>can</em> be used in JSX for the same reason. It resolves to a value that <em>can</em> be passed to <code>React.createElement</code>. This means the following will work:</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiUmVhY3QgQ29uZGl0aW9uYWxzIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG5tYWluIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgZ2FwOiAzMnB4O1xuICAtLXNoYWRvdy1jb2xvcjogMjAzZGVnIDEwMCUgNyU7XG59XG5cbm1haW4gZGl2IHtcbiAgYm9yZGVyOiAycHggc29saWQgIzBjNGE2ZTtcbiAgcGFkZGluZzogMTZweDtcbiAgYm9yZGVyLXJhZGl1czogOHB4O1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjMGM0YTZlO1xuICBjb2xvcjogI2YwZjlmZjtcbiAgd2lkdGg6IDUwJTtcbiAgdGV4dC1hbGlnbjogY2VudGVyO1xuICBib3gtc2hhZG93OiAwLjRweCAwLjNweCAwLjdweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuMjIpLFxuICAgIDEuOXB4IDEuN3B4IDMuNHB4IC0wLjNweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuMzgpLFxuICAgIDQuMXB4IDMuOHB4IDcuNXB4IC0wLjdweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuNTUpLFxuICAgIDkuMnB4IDguM3B4IDE2LjdweCAtMXB4IGhzbCh2YXIoLS1zaGFkb3ctY29sb3IpIC8gMC43MSk7XG59In0sIi9BcHAuanMiOnsiY29kZSI6ImZ1bmN0aW9uIENvb2xDaGVja2VyKHByb3BzKSB7XG4gIHJldHVybiAoXG4gICAgPGRpdj5cbiAgICAgIHtwcm9wcy5uYW1lfVxuICAgICAge3Byb3BzLm5hbWUgPT09IFwidGhlc3NoZ3V5XCIgPyBcIiBpcyBjb29sIVwiIDogXCIgaXMgbm90IGNvb2wg8J+luCFcIn1cbiAgICA8L2Rpdj5cbiAgKTtcbn1cblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICByZXR1cm4gKFxuICAgIDxtYWluPlxuICAgICAgPENvb2xDaGVja2VyIG5hbWU9XCJ0aGVzc2hndXlcIiAvPlxuICAgICAgPENvb2xDaGVja2VyIG5hbWU9XCJvdGhlcmd1eVwiIC8+XG4gICAgPC9tYWluPlxuICApO1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IFwiLi9zdHlsZXMuY3NzXCI7XG5cbmltcG9ydCBBcHAgZnJvbSBcIi4vQXBwXCI7XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+XG4pOyJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjQuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOmZhbHNlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjpmYWxzZSwid3JhcENvbnRlbnQiOmZhbHNlLCJjbG9zYWJsZVRhYnMiOmZhbHNlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjp0cnVlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOnRydWV9fX0sImdlbmVyYWwiOnsiYWN0aXZlRmlsZSI6Ii9BcHAuanMiLCJ2aXNpYmxlRmlsZXMiOlsiL0FwcC5qcyJdLCJ0aGVtZSI6ImRyYWN1bGEiLCJlbmFibGVGdWxsU2NyZWVuIjpmYWxzZSwiZW5hYmxlRm9ybWF0dGluZyI6dHJ1ZX0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ0VkaXRvcidcbidQcmV2aWV3JyJ9fX0=" height="600" width="100%"></iframe>

<p>Another benefit is that this pattern lets us <a target="_blank" href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY</a> up our code. We don't need to repeat the <code>&lt;div&gt;&lt;/div&gt;</code> for both conditions.</p>
<h2 id="heading-3-the-logical-and-ampamp-operator">3. The Logical AND (&amp;&amp;) operator</h2>
<p>The final way to render things conditionally in React is with the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_AND">Logical AND (&amp;&amp;) operator</a>.</p>
<p>The Logical AND takes two inputs separated by the <code>&amp;&amp;</code> operator. If the first value is <strong>truthy</strong> then the second value is returned. If the first value is <strong>falsy</strong> then the first value is returned:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">LogicalANDExample</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span> &amp;&amp; <span class="hljs-string">'Hello'</span>; <span class="hljs-comment">// returns 'Hello'</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span> &amp;&amp; <span class="hljs-string">'Hello'</span>; <span class="hljs-comment">// returns false</span>
}
</code></pre>
<p>This same idea can be used to render JSX elements in a React component. If you make the second value a JSX element, you can conditionally render it based on the first value:</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiUmVhY3QgQ29uZGl0aW9uYWxzIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG5tYWluIHtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbiAgZ2FwOiAzMnB4O1xuICAtLXNoYWRvdy1jb2xvcjogMjAzZGVnIDEwMCUgNyU7XG59XG5cbm1haW4gZGl2IHtcbiAgYm9yZGVyOiAycHggc29saWQgIzBjNGE2ZTtcbiAgcGFkZGluZzogMTZweDtcbiAgYm9yZGVyLXJhZGl1czogOHB4O1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjMGM0YTZlO1xuICBjb2xvcjogI2YwZjlmZjtcbiAgd2lkdGg6IDUwJTtcbiAgdGV4dC1hbGlnbjogY2VudGVyO1xuICBib3gtc2hhZG93OiAwLjRweCAwLjNweCAwLjdweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuMjIpLFxuICAgIDEuOXB4IDEuN3B4IDMuNHB4IC0wLjNweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuMzgpLFxuICAgIDQuMXB4IDMuOHB4IDcuNXB4IC0wLjdweCBoc2wodmFyKC0tc2hhZG93LWNvbG9yKSAvIDAuNTUpLFxuICAgIDkuMnB4IDguM3B4IDE2LjdweCAtMXB4IGhzbCh2YXIoLS1zaGFkb3ctY29sb3IpIC8gMC43MSk7XG59In0sIi9BcHAuanMiOnsiY29kZSI6ImZ1bmN0aW9uIENvb2xDaGVja2VyKHByb3BzKSB7XG4gIHJldHVybiBwcm9wcy5uYW1lID09PSBcInRoZXNzaGd1eVwiICYmIDxkaXY+e3Byb3BzLm5hbWV9IGlzIGNvb2whPC9kaXY+O1xufVxuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiAoXG4gICAgPG1haW4+XG4gICAgICA8Q29vbENoZWNrZXIgbmFtZT1cInRoZXNzaGd1eVwiIC8+XG4gICAgICA8Q29vbENoZWNrZXIgbmFtZT1cIm90aGVyZ3V5XCIgLz5cbiAgICA8L21haW4+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNC4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifX0sInRlbXBsYXRlIjoicmVhY3QifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6ZmFsc2UsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOmZhbHNlLCJ3cmFwQ29udGVudCI6ZmFsc2UsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOnRydWUsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6dHJ1ZX19fSwiZ2VuZXJhbCI6eyJhY3RpdmVGaWxlIjoiL0FwcC5qcyIsInZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoiZHJhY3VsYSIsImVuYWJsZUZ1bGxTY3JlZW4iOmZhbHNlLCJlbmFibGVGb3JtYXR0aW5nIjp0cnVlfSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="500" width="100%"></iframe>

<p>If <code>props.name</code> equals the string <code>thesshguy</code>, we return the JSX, otherwise, we return <code>false</code>.</p>
<p>It should be clear why the second <code>div</code> no longer shows up. The condition is <code>false</code> so we don't return the <code>div</code> in that case. But what about the value that we <em>are</em> returning?</p>
<p>What the heck happened to the <code>false</code> value and why didn't React render it?</p>
<h3 id="heading-react-doesnt-render-some-falsy-values">React doesn't render (<em>some</em>) falsy values</h3>
<p>Let's say you have a situation where you don't want to render <em>anything</em>.</p>
<p>React allows for this by letting you return certain <strong>falsy</strong> values. The most common value that's used for this is <code>null</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">EmptyComponent</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">// I do not render anything</span>
    <span class="hljs-keyword">return</span> &lt;EmptyComponent /&gt;;
}
</code></pre>
<p>This is also the case for <code>undefined</code> and <code>false</code>, which is why in the example above, nothing is rendered for the second <code>&lt;CoolChecker name="otherguy" /&gt;</code> element.</p>
<h3 id="heading-what-about-0">What about 0?</h3>
<p>One thing that trips up new React developers is the value <code>0</code>. While it <em>is**</em>falsy<em>*, when you use it as the first value of the Logical AND operator, React </em>still* renders it:</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiUmVhY3QgQ29uZGl0aW9uYWxzIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG4jcm9vdCB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG59XG5cbm1haW4ge1xuICB0ZXh0LWFsaWduOiBjZW50ZXI7XG59XG5cbmRpdiB7XG4gIG1hcmdpbi1ib3R0b206IDE2cHg7XG59In0sIi9BcHAuanMiOnsiY29kZSI6ImZ1bmN0aW9uIFJlbmRlckNvdW50KHByb3BzKSB7XG4gIHJldHVybiBwcm9wcy5jb3VudCAmJiA8ZGl2PkNvdW50IGlzIHtwcm9wcy5jb3VudH08L2Rpdj47XG59XG5cbmV4cG9ydCBkZWZhdWx0IGZ1bmN0aW9uIEFwcCgpIHtcbiAgcmV0dXJuIChcbiAgICA8bWFpbj5cbiAgICAgIDxSZW5kZXJDb3VudCBjb3VudD17MX0gLz5cbiAgICAgIDxSZW5kZXJDb3VudCBjb3VudD17MH0gLz5cbiAgICA8L21haW4+XG4gICk7XG59XG4ifSwiL2luZGV4LmpzIjp7ImNvZGUiOiJpbXBvcnQgUmVhY3QsIHsgU3RyaWN0TW9kZSB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgY3JlYXRlUm9vdCB9IGZyb20gXCJyZWFjdC1kb20vY2xpZW50XCI7XG5pbXBvcnQgXCIuL3N0eWxlcy5jc3NcIjtcblxuaW1wb3J0IEFwcCBmcm9tIFwiLi9BcHBcIjtcblxuY29uc3Qgcm9vdCA9IGNyZWF0ZVJvb3QoZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoXCJyb290XCIpKTtcbnJvb3QucmVuZGVyKFxuICA8U3RyaWN0TW9kZT5cbiAgICA8QXBwIC8+XG4gIDwvU3RyaWN0TW9kZT5cbik7In0sIi9wdWJsaWMvaW5kZXguaHRtbCI6eyJjb2RlIjoiPCFET0NUWVBFIGh0bWw+XG48aHRtbCBsYW5nPVwiZW5cIj5cbiAgPGhlYWQ+XG4gICAgPG1ldGEgY2hhcnNldD1cIlVURi04XCI+XG4gICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIj5cbiAgICA8dGl0bGU+RG9jdW1lbnQ8L3RpdGxlPlxuICA8L2hlYWQ+XG4gIDxib2R5PlxuICAgIDxkaXYgaWQ9XCJyb290XCI+PC9kaXY+XG4gIDwvYm9keT5cbjwvaHRtbD4ifSwiL3BhY2thZ2UuanNvbiI6eyJjb2RlIjoie1xuICBcImRlcGVuZGVuY2llc1wiOiB7XG4gICAgXCJyZWFjdFwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LWRvbVwiOiBcIl4xOC4wLjBcIixcbiAgICBcInJlYWN0LXNjcmlwdHNcIjogXCJeNC4wLjBcIlxuICB9LFxuICBcIm1haW5cIjogXCIvaW5kZXguanNcIixcbiAgXCJkZXZEZXBlbmRlbmNpZXNcIjoge31cbn0ifX0sInRlbXBsYXRlIjoicmVhY3QifSwiZGlzcGxheSI6eyJjb21wb25lbnRzIjp7IkVkaXRvciI6eyJ0eXBlIjoiZWRpdG9yIiwiZGF0YSI6eyJzaG93VGFicyI6ZmFsc2UsInNob3dMaW5lTnVtYmVycyI6dHJ1ZSwic2hvd0lubGluZUVycm9ycyI6ZmFsc2UsInNob3dSdW5CdXR0b24iOmZhbHNlLCJ3cmFwQ29udGVudCI6ZmFsc2UsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOnRydWUsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6dHJ1ZX19fSwiZ2VuZXJhbCI6eyJhY3RpdmVGaWxlIjoiL0FwcC5qcyIsInZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoiZHJhY3VsYSIsImVuYWJsZUZ1bGxTY3JlZW4iOmZhbHNlLCJlbmFibGVGb3JtYXR0aW5nIjp0cnVlfSwibGF5b3V0cyI6eyJzbWFsbCI6IidFZGl0b3InXG4nRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="450" width="100%"></iframe>

<p>This can be a bit confusing at first, but it makes sense why React would do this.</p>
<p>There are legitimate reasons why you'd want to render <code>0</code>. For example, maybe you want to show a countdown on the page. If React didn't render <code>0</code>, you'd have to convert it to a string first.</p>
<p>So how do you get around this issue?</p>
<p>The easiest way is to make sure you return <code>false</code> instead:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">RenderCount</span>(<span class="hljs-params">props</span>) </span>{
  <span class="hljs-keyword">return</span> props.count &gt; <span class="hljs-number">0</span> &amp;&amp; &lt;div&gt;Count is {props.count}&lt;/div&gt;;
}
</code></pre>
<p>One way to do that is with a comparison operator like <em>greater than</em>. Not only does <code>props.count &gt; 0</code> always return a boolean, it also makes the intention of the code more clear.</p>
<p>Win-Win!</p>
<h2 id="heading-conditionals-for-jsx-assignment">Conditionals for JSX assignment</h2>
<p>JSX isn't limited to <code>return</code> statements.</p>
<p>You can assign JSX to a variable. This lets you reuse the JSX in multiple places and keeps the code clean. Variable assignment can take advantage of all the conditional logic that's been mentioned above:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">let</span> first;
    <span class="hljs-keyword">if</span> (someCond) {
        first = &lt;div&gt;The <span class="hljs-keyword">if</span>/<span class="hljs-keyword">else</span> statement is <span class="hljs-literal">true</span>!&lt;/div&gt;;
    }

    <span class="hljs-keyword">const</span> second = (
        &lt;div&gt;
            The ternary is {someCond ? <span class="hljs-string">'true!'</span> : <span class="hljs-string">'false!'</span>}
        &lt;/div&gt;
    );

    <span class="hljs-keyword">const</span> third = someCond &amp;&amp; &lt;div&gt;The Logical AND is <span class="hljs-literal">true</span>!&lt;/div&gt;;

    <span class="hljs-keyword">return</span> (
        &lt;div&gt;
            {first}
            {second}
            {third}
        &lt;/div&gt;
    );
}
</code></pre>
<p>What's great about all the examples above is that they rely on standard JavaScript functionality.</p>
<p>By learning how to manage control flow for your React components, you become a better JavaScript developer!</p>
]]></content:encoded></item><item><title><![CDATA[The Simple Guide to Rendering Lists in React]]></title><description><![CDATA[If you've done any UI development, you've probably had to build a list or two.
What do I mean by a list?
Let's take Twitter as an example.
When you log onto Twitter, you're presented with a feed that contains an endless list of Tweets. In other words...]]></description><link>https://blog.thesshguy.com/render-react-lists</link><guid isPermaLink="true">https://blog.thesshguy.com/render-react-lists</guid><category><![CDATA[React]]></category><category><![CDATA[UI]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><dc:creator><![CDATA[Sai Hari]]></dc:creator><pubDate>Fri, 31 Mar 2023 12:45:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1680205091030/d9c202fa-6166-4cf0-8b27-158254aace85.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="hn-embed-widget" id="kempo"></div><p> </p>
<p>If you've done any UI development, you've probably had to build a list or two.</p>
<p>What do I mean by a list?</p>
<p>Let's take Twitter as an example.</p>
<p>When you log onto Twitter, you're presented with a <em>feed</em> that contains an endless list of Tweets. In other words, a <em>list</em> is a series of similarly structured content that's placed one after the other.</p>
<p>In this post, we'll take a look at how to build lists effectively in React.</p>
<p>The naive approach would be to hardcode each element in the list:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Feed</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        &lt;div&gt;
            &lt;span&gt;I am tweet <span class="hljs-number">1</span>&lt;/span&gt;
            &lt;span&gt;I am tweet <span class="hljs-number">2</span>&lt;/span&gt;
            &lt;span&gt;I am tweet <span class="hljs-number">3</span>&lt;/span&gt;
            &lt;span&gt;I am tweet <span class="hljs-number">4</span>&lt;/span&gt;
        &lt;/div&gt;
    );
}
</code></pre>
<p>This approach has a few problems.</p>
<h2 id="heading-duplicate-code-is-error-prone-and-hard-to-read">Duplicate code is error-prone and hard to read</h2>
<p>The example above is simple.</p>
<p>You can add a new <strong><em>Tweet</em></strong> by creating a new <code>&lt;span /&gt;</code> element. But this isn't a real-world scenario. What do things look like when the JSX is a <em>little</em> more complex?</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Feed</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        &lt;div&gt;
            &lt;div&gt;
                &lt;span&gt;<span class="hljs-meta">@username</span>&lt;/span&gt;
                &lt;span&gt;I am tweet <span class="hljs-number">1</span>&lt;/span&gt;
                &lt;span&gt;created timestamp&lt;/span&gt;
                &lt;div&gt;
                    &lt;span&gt;Like&lt;/span&gt;
                    &lt;span&gt;Retweet&lt;/span&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div&gt;
                &lt;span&gt;<span class="hljs-meta">@username</span>&lt;/span&gt;
                &lt;span&gt;I am tweet <span class="hljs-number">2</span>&lt;/span&gt;
                &lt;span&gt;created timestamp&lt;/span&gt;
                &lt;div&gt;
                    &lt;span&gt;Like&lt;/span&gt;
                    &lt;span&gt;Retweet&lt;/span&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div&gt;
                &lt;span&gt;<span class="hljs-meta">@username</span>&lt;/span&gt;
                &lt;span&gt;I am tweet <span class="hljs-number">3</span>&lt;/span&gt;
                &lt;span&gt;created timestamp&lt;/span&gt;
                &lt;div&gt;
                    &lt;span&gt;Like&lt;/span&gt;
                    &lt;span&gt;Retweet&lt;/span&gt;
                &lt;/div&gt;
            &lt;/div&gt;
            &lt;div&gt;
                &lt;span&gt;<span class="hljs-meta">@username</span>&lt;/span&gt;
                &lt;span&gt;I am tweet <span class="hljs-number">4</span>&lt;/span&gt;
                &lt;span&gt;created timestamp&lt;/span&gt;
                &lt;div&gt;
                    &lt;span&gt;Like&lt;/span&gt;
                    &lt;span&gt;Retweet&lt;/span&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    );
}
</code></pre>
<p>This code is a mess...and we're only 4 Tweets in!</p>
<p>Now if you try to copy the code to make a new Tweet, you're more likely to miss something. This code is also harder to maintain.</p>
<h2 id="heading-you-cant-hardcode-jsx-for-dynamic-data">You can't hardcode JSX for dynamic data</h2>
<p>It's clear the code above is too messy, but there's a more serious problem.</p>
<p>You may have already realized it, but for a website like Twitter, it's not possible to hardcode the JSX. Tweets are created dynamically. You won't have the information ahead of time to write the JSX.</p>
<p>Instead, the JSX is created on the fly from Tweets fetched from a database.</p>
<h3 id="heading-using-dynamic-lists-will-solve-both-problems">Using dynamic lists will solve both problems.</h3>
<p>Great!</p>
<p>But how do you do that?</p>
<p>React doesn't have any built-in utilities to create lists. Instead, it's recommended you leverage standard JavaScript array methods. The most common method used is the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map">map method</a>.</p>
<p>But before we get to that, it's important to understand <em>how</em> React renders arrays:</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiUmVhY3QgQmFzaWMgQXJyYXlzIiwiY29yZSI6eyJmaWxlcyI6eyIvQXBwLmpzIjp7ImNvZGUiOiJleHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiAoXG4gICAgPGRpdiBjbGFzc05hbWU9XCJ3cmFwcGVyXCI+XG4gICAgICB7W1xuICAgICAgICA8c3Bhbj5JdGVtIE9uZTwvc3Bhbj4sXG4gICAgICAgIDxzcGFuPkl0ZW0gVHdvPC9zcGFuPlxuICAgICAgXX1cbiAgICA8L2Rpdj5cbiAgKTtcbn1cbiJ9LCIvaW5kZXguanMiOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxuLndyYXBwZXIge1xuICBmb250LXNpemU6IDEuNXJlbTtcbiAgZGlzcGxheTogZmxleDtcbiAgZ2FwOiAxNnB4O1xufSJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjQuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJFZGl0b3IiOnsidHlwZSI6ImVkaXRvciIsImRhdGEiOnsic2hvd1RhYnMiOmZhbHNlLCJzaG93TGluZU51bWJlcnMiOnRydWUsInNob3dJbmxpbmVFcnJvcnMiOmZhbHNlLCJzaG93UnVuQnV0dG9uIjp0cnVlLCJ3cmFwQ29udGVudCI6ZmFsc2UsImNsb3NhYmxlVGFicyI6ZmFsc2UsInJlYWRPbmx5IjpmYWxzZSwic2hvd1JlYWRPbmx5IjpmYWxzZX19LCJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOmZhbHNlLCJzaG93U2FuZHBhY2tFcnJvck92ZXJsYXkiOmZhbHNlLCJyZWxhdGl2ZVBhdGgiOiIifX19LCJnZW5lcmFsIjp7ImFjdGl2ZUZpbGUiOiIvQXBwLmpzIiwidmlzaWJsZUZpbGVzIjpbIi9BcHAuanMiXSwidGhlbWUiOiJkcmFjdWxhIn0sImxheW91dHMiOnsic21hbGwiOiInRWRpdG9yJ1xuJ1ByZXZpZXcnIn19fQ==" height="500" width="100%"></iframe>

<p>Notice how the <code>children</code> passed to the <code>div</code> is an array that contains two JSX elements. React takes that array and dumps the children out in the browser. This lets us do some powerful things using array methods.</p>
<h2 id="heading-map-an-array-of-data-to-jsx">Map an array of data to JSX</h2>
<p>With the <code>map</code> method, you can convert array elements into JSX. React can then take that array and render things properly:</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiUmVhY3QgQmFzaWMgQXJyYXlzIiwiY29yZSI6eyJmaWxlcyI6eyIvc3R5bGVzLmNzcyI6eyJjb2RlIjoiYm9keSB7XG4gIGZvbnQtZmFtaWx5OiBzYW5zLXNlcmlmO1xuICAtd2Via2l0LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LWZvbnQtc21vb3RoaW5nOiBhdXRvO1xuICAtbW96LW9zeC1mb250LXNtb290aGluZzogZ3JheXNjYWxlO1xuICBmb250LXNtb290aGluZzogYXV0bztcbiAgdGV4dC1yZW5kZXJpbmc6IG9wdGltaXplTGVnaWJpbGl0eTtcbiAgZm9udC1zbW9vdGg6IGFsd2F5cztcbiAgLXdlYmtpdC10YXAtaGlnaGxpZ2h0LWNvbG9yOiB0cmFuc3BhcmVudDtcbiAgLXdlYmtpdC10b3VjaC1jYWxsb3V0OiBub25lO1xufVxuXG4ud3JhcHBlciB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gIGdhcDogMTZweDtcbiAgYWxpZ24taXRlbXM6IGNlbnRlcjtcbn1cblxuLnR3ZWV0IHtcbiAgZGlzcGxheTogZmxleDtcbiAgZmxleC1kaXJlY3Rpb246IGNvbHVtbjtcbiAgZ2FwOiA0cHg7XG4gIGJvcmRlcjogMXB4IHNvbGlkIGJsYWNrO1xuICB3aWR0aDogMTI4cHg7XG4gIHBhZGRpbmc6IDhweDtcbiAgYm9yZGVyLXJhZGl1czogOHB4O1xufVxuXG4udXNlcixcbi5jcmVhdGVkIHtcbiAgZm9udC1zaXplOiAxNHB4O1xuICBjb2xvcjogZ3JheTtcbn1cblxuLmNvbnRlbnQge1xuICBmb250LXNpemU6IDE4cHg7XG4gIHBhZGRpbmc6IDhweCAwO1xufVxuXG4uYnV0dG9uLXdyYXBwZXIge1xuICBkaXNwbGF5OiBmbGV4O1xuICBwYWRkaW5nLXRvcDogMTZweDtcbiAganVzdGlmeS1jb250ZW50OiBzcGFjZS1iZXR3ZWVuO1xufSJ9LCIvQXBwLmpzIjp7ImNvZGUiOiJjb25zdCB0d2VldHMgPSBbXG4gIHsgaGFuZGxlOiAnQFRoZVNTSEd1eScsIGNvbnRlbnQ6IFwiSSdtIHR3ZWV0IDFcIiwgY3JlYXRlZDogXCJKYW4gMXN0IDIwMDBcIiB9LFxuICB7IGhhbmRsZTogJ0BUaGVTU0hHdXknLCBjb250ZW50OiBcIkknbSB0d2VldCAyXCIsIGNyZWF0ZWQ6IFwiSmFuIDFzdCAyMDAwXCIgfSxcbiAgeyBoYW5kbGU6ICdAVGhlU1NIR3V5JywgY29udGVudDogXCJJJ20gdHdlZXQgM1wiLCBjcmVhdGVkOiBcIkphbiAxc3QgMjAwMFwiIH0sXG4gIHsgaGFuZGxlOiAnQFRoZVNTSEd1eScsIGNvbnRlbnQ6IFwiSSdtIHR3ZWV0IDRcIiwgY3JlYXRlZDogXCJKYW4gMXN0IDIwMDBcIiB9XG5dO1xuXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBBcHAoKSB7XG4gIHJldHVybiAoXG4gICAgPGRpdiBjbGFzc05hbWU9XCJ3cmFwcGVyXCI+XG4gICAgICB7dHdlZXRzLm1hcCgoeyBoYW5kbGUsIGNvbnRlbnQsIGNyZWF0ZWQgfSkgPT4gKFxuICAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJ0d2VldFwiPlxuICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT1cInVzZXJcIj57aGFuZGxlfTwvc3Bhbj5cbiAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9XCJjb250ZW50XCI+e2NvbnRlbnR9PC9zcGFuPlxuICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT1cImNyZWF0ZWRcIj57Y3JlYXRlZH08L3NwYW4+XG4gICAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJidXR0b24td3JhcHBlclwiPlxuICAgICAgICAgICAgPHNwYW4+TGlrZTwvc3Bhbj5cbiAgICAgICAgICAgIDxzcGFuPlJldHdlZXQ8L3NwYW4+XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICA8L2Rpdj5cbiAgICAgICAgKVxuICAgICAgKX1cbiAgICA8L2Rpdj5cbiAgKTtcbn1cbiJ9LCIvaW5kZXguanMiOnsiY29kZSI6ImltcG9ydCBSZWFjdCwgeyBTdHJpY3RNb2RlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgeyBjcmVhdGVSb290IH0gZnJvbSBcInJlYWN0LWRvbS9jbGllbnRcIjtcbmltcG9ydCBcIi4vc3R5bGVzLmNzc1wiO1xuXG5pbXBvcnQgQXBwIGZyb20gXCIuL0FwcFwiO1xuXG5jb25zdCByb290ID0gY3JlYXRlUm9vdChkb2N1bWVudC5nZXRFbGVtZW50QnlJZChcInJvb3RcIikpO1xucm9vdC5yZW5kZXIoXG4gIDxTdHJpY3RNb2RlPlxuICAgIDxBcHAgLz5cbiAgPC9TdHJpY3RNb2RlPlxuKTsifSwiL3B1YmxpYy9pbmRleC5odG1sIjp7ImNvZGUiOiI8IURPQ1RZUEUgaHRtbD5cbjxodG1sIGxhbmc9XCJlblwiPlxuICA8aGVhZD5cbiAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIj5cbiAgICA8bWV0YSBuYW1lPVwidmlld3BvcnRcIiBjb250ZW50PVwid2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMFwiPlxuICAgIDx0aXRsZT5Eb2N1bWVudDwvdGl0bGU+XG4gIDwvaGVhZD5cbiAgPGJvZHk+XG4gICAgPGRpdiBpZD1cInJvb3RcIj48L2Rpdj5cbiAgPC9ib2R5PlxuPC9odG1sPiJ9LCIvcGFja2FnZS5qc29uIjp7ImNvZGUiOiJ7XG4gIFwiZGVwZW5kZW5jaWVzXCI6IHtcbiAgICBcInJlYWN0XCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3QtZG9tXCI6IFwiXjE4LjAuMFwiLFxuICAgIFwicmVhY3Qtc2NyaXB0c1wiOiBcIl40LjAuMFwiXG4gIH0sXG4gIFwibWFpblwiOiBcIi9pbmRleC5qc1wiLFxuICBcImRldkRlcGVuZGVuY2llc1wiOiB7fVxufSJ9fSwidGVtcGxhdGUiOiJyZWFjdCJ9LCJkaXNwbGF5Ijp7ImNvbXBvbmVudHMiOnsiRWRpdG9yIjp7InR5cGUiOiJlZGl0b3IiLCJkYXRhIjp7InNob3dUYWJzIjpmYWxzZSwic2hvd0xpbmVOdW1iZXJzIjp0cnVlLCJzaG93SW5saW5lRXJyb3JzIjpmYWxzZSwic2hvd1J1bkJ1dHRvbiI6dHJ1ZSwid3JhcENvbnRlbnQiOmZhbHNlLCJjbG9zYWJsZVRhYnMiOmZhbHNlLCJyZWFkT25seSI6ZmFsc2UsInNob3dSZWFkT25seSI6ZmFsc2V9fSwiUHJldmlldyI6eyJ0eXBlIjoicHJldmlldyIsImRhdGEiOnsic2hvd05hdmlnYXRvciI6ZmFsc2UsInNob3dPcGVuSW5Db2RlU2FuZGJveCI6ZmFsc2UsInNob3dSZWZyZXNoQnV0dG9uIjpmYWxzZSwic2hvd1NhbmRwYWNrRXJyb3JPdmVybGF5IjpmYWxzZSwicmVsYXRpdmVQYXRoIjoiIn19LCJDb25zb2xlIjp7InR5cGUiOiJjb25zb2xlIiwiZGF0YSI6eyJzaG93SGVhZGVyIjp0cnVlLCJzaG93U3ludGF4RXJyb3IiOmZhbHNlLCJzaG93U2V0dXBQcm9ncmVzcyI6ZmFsc2UsIm1heE1lc3NhZ2VDb3VudCI6NTAwLCJyZXNldE9uUHJldmlld1Jlc3RhcnQiOmZhbHNlLCJzdGFuZGFsb25lIjpmYWxzZX19fSwiZ2VuZXJhbCI6eyJhY3RpdmVGaWxlIjoiL0FwcC5qcyIsInZpc2libGVGaWxlcyI6WyIvQXBwLmpzIl0sInRoZW1lIjoiZHJhY3VsYSJ9LCJsYXlvdXRzIjp7InNtYWxsIjoiJ0VkaXRvciBFZGl0b3InXG4nUHJldmlldyBDb25zb2xlJyJ9fX0=" height="700" width="100%"></iframe>

<p>In the example above, we map over a collection of Tweet data and return a new collection of JSX.</p>
<p>This code isn't duplicated, so it's easier to read and maintain. Another benefit is that we can load the Tweets dynamically from a database. As long as the Tweets are in the correct format, the code doesn't care how you get them.</p>
<h2 id="heading-each-child-in-a-react-list-must-have-a-unique-key">Each child in a React list must have a unique key</h2>
<p>If you take a look at the console in the example above, you'll see that React is throwing an error:</p>
<blockquote>
<p>Warning: Each child in a list should have a unique "key" prop.</p>
</blockquote>
<p>A key is a unique string or number.</p>
<p>React uses keys to correctly identify elements in an array. The elements are tracked so that React can make DOM updates (i.e. <em>re-renders</em>) efficiently. A <em>bad</em> key can lead to bugs when a list can be reordered (e.g. via sorting) or when elements can be added/removed from a list.</p>
<p><strong>Let's look at a bug in action.</strong></p>
<p>In the example below, we have two lists of boxes. Each box has an HTML <code>input</code> element <em>and</em> a number to identify it. Right above the boxes is a <strong>Shuffle</strong> button that re-orders the boxes.</p>
<p>The boxes in list one are using a unique key. The boxes in list two are using the array index for the keys. Since an array index isn't unique to a box, we've introduced a subtle bug when we try to shuffle the boxes.</p>
<p>Try it out for yourself and see if you can spot the issue.</p>
<iframe src="https://www.kempo.io/view/eyJuYW1lIjoiVGhlIEltcG9ydGFuY2Ugb2YgUmVhY3QgS2V5cyIsImNvcmUiOnsiZmlsZXMiOnsiL3N0eWxlcy5jc3MiOnsiY29kZSI6ImJvZHkge1xuICBmb250LWZhbWlseTogc2Fucy1zZXJpZjtcbiAgLXdlYmtpdC1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1mb250LXNtb290aGluZzogYXV0bztcbiAgLW1vei1vc3gtZm9udC1zbW9vdGhpbmc6IGdyYXlzY2FsZTtcbiAgZm9udC1zbW9vdGhpbmc6IGF1dG87XG4gIHRleHQtcmVuZGVyaW5nOiBvcHRpbWl6ZUxlZ2liaWxpdHk7XG4gIGZvbnQtc21vb3RoOiBhbHdheXM7XG4gIC13ZWJraXQtdGFwLWhpZ2hsaWdodC1jb2xvcjogdHJhbnNwYXJlbnQ7XG4gIC13ZWJraXQtdG91Y2gtY2FsbG91dDogbm9uZTtcbn1cblxubWFpbiB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGZsZXgtZGlyZWN0aW9uOiBjb2x1bW47XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGdhcDogMTZweDtcbn1cblxuYnV0dG9uIHtcbiAgd2lkdGg6IDk2cHg7XG4gIGhlaWdodDogMzZweDtcbiAgZm9udC13ZWlnaHQ6IGJvbGQ7XG59XG5cbmlucHV0IHtcbiAgd2lkdGg6IDgwJTtcbiAgYm9yZGVyLXJhZGl1czogNHB4O1xuICBib3JkZXI6IDFweCBzb2xpZCBibGFjaztcbiAgYmFja2dyb3VuZDogdHJhbnNwYXJlbnQ7XG4gIGZvbnQtc2l6ZTogMTZweDtcbiAgdGV4dC1hbGlnbjogY2VudGVyO1xufVxuXG4ud3JhcHBlciB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGdhcDogNDhweDtcbiAganVzdGlmeS1jb250ZW5vdDogY2VudGVyO1xufVxuXG4uYm94LXdyYXBwZXIge1xuICBkaXNwbGF5OiBmbGV4O1xuICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICBnYXA6IDhweDtcbn1cblxuLmJveCB7XG4gIGJvcmRlcjogMXB4IHNvbGlkIGJsYWNrO1xuICB3aWR0aDogOTZweDtcbiAgaGVpZ2h0OiA5NnB4O1xuICBib3JkZXItcmFkaXVzOiA4cHg7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICBkaXNwbGF5OiBmbGV4O1xuICBmbGV4LWRpcmVjdGlvbjogY29sdW1uO1xuICBnYXA6IDhweDtcbiAgYm94LXNoYWRvdzogMC4zcHggMC40cHggMC42cHggaHNsKDBkZWcgMCUgNjMlIC8gMC4zNiksXG4gICAgMC45cHggMS4ycHggMS43cHggLTAuOHB4IGhzbCgwZGVnIDAlIDYzJSAvIDAuMzYpLFxuICAgIDIuM3B4IDNweCA0LjNweCAtMS43cHggaHNsKDBkZWcgMCUgNjMlIC8gMC4zNiksXG4gICAgNS41cHggNy40cHggMTAuNHB4IC0yLjVweCBoc2woMGRlZyAwJSA2MyUgLyAwLjM2KTtcbn0ifSwiL0FwcC5qcyI6eyJjb2RlIjoiaW1wb3J0IHsgdXNlU3RhdGUgfSBmcm9tICdyZWFjdCc7XG5cbmNvbnN0IHNodWZmbGVBcnJheSA9IChhcnIpID0+IHtcbiAgcmV0dXJuIGFyci5tYXAodmFsdWUgPT4gKHsgdmFsdWUsIHNvcnQ6IE1hdGgucmFuZG9tKCkgfSkpXG4gICAgLnNvcnQoKGEsIGIpID0+IGEuc29ydCAtIGIuc29ydClcbiAgICAubWFwKCh7IHZhbHVlIH0pID0+IHZhbHVlKTtcbn1cblxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gQXBwKCkge1xuICBjb25zdCBbaXRlbXMsIHNldEl0ZW1zXSA9IHVzZVN0YXRlKFtcbiAgICB7IGtleTogMSwgaW5zaWRlOiAnQScgfSxcbiAgICB7IGtleTogMiwgaW5zaWRlOiAnQicgfSxcbiAgICB7IGtleTogMywgaW5zaWRlOiAnQycgfSxcbiAgICB7IGtleTogNCwgaW5zaWRlOiAnRCcgfVxuICBdKTtcblxuICBjb25zdCBsaXN0T25lID0gaXRlbXMubWFwKChpdGVtKSA9PiAoXG4gICAgPGRpdiBjbGFzc05hbWU9XCJib3hcIiBrZXk9e2l0ZW0ua2V5fT5cbiAgICAgIDxzcGFuPkJveCB7aXRlbS5rZXl9PC9zcGFuPlxuICAgICAgPGlucHV0IGRlZmF1bHRWYWx1ZT17aXRlbS5pbnNpZGV9IC8+XG4gICAgPC9kaXY+XG4gICkpO1xuICBcbiAgY29uc3QgbGlzdFR3byA9IGl0ZW1zLm1hcCgoaXRlbSwgaW5kZXgpID0+IChcbiAgICA8ZGl2IGNsYXNzTmFtZT1cImJveFwiIGtleT17aW5kZXh9PlxuICAgICAgPHNwYW4+Qm94IHtpdGVtLmtleX08L3NwYW4+XG4gICAgICA8aW5wdXQgZGVmYXVsdFZhbHVlPXtpdGVtLmluc2lkZX0gLz5cbiAgICA8L2Rpdj5cbiAgKSk7XG4gIFxuICByZXR1cm4gKFxuICAgIDxtYWluPlxuICAgICAgPGJ1dHRvbiBvbkNsaWNrPXsoKSA9PiBzZXRJdGVtcyhzaHVmZmxlQXJyYXkoaXRlbXMpKX0+U2h1ZmZsZTwvYnV0dG9uPlxuICAgICAgPGRpdiBjbGFzc05hbWU9XCJ3cmFwcGVyXCI+XG4gICAgICAgIDxkaXYgY2xhc3NOYW1lPVwiYm94LXdyYXBwZXJcIj57bGlzdE9uZX08L2Rpdj5cbiAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJib3gtd3JhcHBlclwiPntsaXN0VHdvfTwvZGl2PlxuICAgICAgPC9kaXY+XG4gICAgPC9tYWluPlxuICApO1xufVxuIn0sIi9pbmRleC5qcyI6eyJjb2RlIjoiaW1wb3J0IFJlYWN0LCB7IFN0cmljdE1vZGUgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGNyZWF0ZVJvb3QgfSBmcm9tIFwicmVhY3QtZG9tL2NsaWVudFwiO1xuaW1wb3J0IFwiLi9zdHlsZXMuY3NzXCI7XG5cbmltcG9ydCBBcHAgZnJvbSBcIi4vQXBwXCI7XG5cbmNvbnN0IHJvb3QgPSBjcmVhdGVSb290KGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKFwicm9vdFwiKSk7XG5yb290LnJlbmRlcihcbiAgPFN0cmljdE1vZGU+XG4gICAgPEFwcCAvPlxuICA8L1N0cmljdE1vZGU+XG4pOyJ9LCIvcHVibGljL2luZGV4Lmh0bWwiOnsiY29kZSI6IjwhRE9DVFlQRSBodG1sPlxuPGh0bWwgbGFuZz1cImVuXCI+XG4gIDxoZWFkPlxuICAgIDxtZXRhIGNoYXJzZXQ9XCJVVEYtOFwiPlxuICAgIDxtZXRhIG5hbWU9XCJ2aWV3cG9ydFwiIGNvbnRlbnQ9XCJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wXCI+XG4gICAgPHRpdGxlPkRvY3VtZW50PC90aXRsZT5cbiAgPC9oZWFkPlxuICA8Ym9keT5cbiAgICA8ZGl2IGlkPVwicm9vdFwiPjwvZGl2PlxuICA8L2JvZHk+XG48L2h0bWw+In0sIi9wYWNrYWdlLmpzb24iOnsiY29kZSI6IntcbiAgXCJkZXBlbmRlbmNpZXNcIjoge1xuICAgIFwicmVhY3RcIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1kb21cIjogXCJeMTguMC4wXCIsXG4gICAgXCJyZWFjdC1zY3JpcHRzXCI6IFwiXjQuMC4wXCJcbiAgfSxcbiAgXCJtYWluXCI6IFwiL2luZGV4LmpzXCIsXG4gIFwiZGV2RGVwZW5kZW5jaWVzXCI6IHt9XG59In19LCJ0ZW1wbGF0ZSI6InJlYWN0In0sImRpc3BsYXkiOnsiY29tcG9uZW50cyI6eyJQcmV2aWV3Ijp7InR5cGUiOiJwcmV2aWV3IiwiZGF0YSI6eyJzaG93TmF2aWdhdG9yIjpmYWxzZSwic2hvd09wZW5JbkNvZGVTYW5kYm94IjpmYWxzZSwic2hvd1JlZnJlc2hCdXR0b24iOnRydWUsInNob3dTYW5kcGFja0Vycm9yT3ZlcmxheSI6ZmFsc2UsInJlbGF0aXZlUGF0aCI6IiJ9fX0sImdlbmVyYWwiOnsiYWN0aXZlRmlsZSI6IiIsInZpc2libGVGaWxlcyI6W10sInRoZW1lIjoiTm8gVGhlbWUifSwibGF5b3V0cyI6eyJzbWFsbCI6IidQcmV2aWV3JyJ9fX0=" height="550" width="100%"></iframe>

<p>The first thing to note is that the order of the boxes is changing in both lists. The issue is that only the first list is keeping track of the <code>input</code> elements correctly. Since you can't use an array index to uniquely identify a box, React can't update the DOM correctly.</p>
<h2 id="heading-how-do-you-pick-a-good-key">How do you pick a <em>good</em> key?</h2>
<p>Here are some basic things to keep in mind.</p>
<h3 id="heading-choose-a-key-based-on-your-data-source">Choose a key based on your data source</h3>
<ul>
<li><p>Data from a database <strong>→</strong> DB IDs</p>
</li>
<li><p>Data locally <strong>→</strong><a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID">crypto.randomUUID()</a></p>
</li>
</ul>
<h3 id="heading-choose-a-unique-key">Choose a unique key</h3>
<p>A key must be unique so that React can differentiate between elements. While this must hold for sibling elements (i.e. elements in the <em>same</em> array), there's no requirement for keys to be unique across an entire app:</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">List</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> items = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
    <span class="hljs-keyword">return</span> (
        &lt;&gt;
            &lt;div&gt;
                List One
                {items.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> &lt;span key={item}&gt;{item}&lt;/span&gt;)}
            &lt;/div&gt;
            &lt;div&gt;
                List Two
                {items.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> &lt;span key={item}&gt;{item}&lt;/span&gt;)}
            &lt;/div&gt;
        &lt;/&gt;
    );
}
</code></pre>
<p>The keys in <strong>List One</strong> and <strong>List Two</strong> aren't unique.</p>
<p>But that's not a problem because they're separate lists. As long as the keys are unique within a <em>single</em> list, React can handle things correctly.</p>
<h3 id="heading-keys-must-be-stable">Keys must be stable</h3>
<p>React needs to be able to consistently identify elements. This isn't possible if the keys you use aren't stable. This means that your keys <strong>can't</strong> change.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">List</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> items = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
    <span class="hljs-keyword">return</span> (
        &lt;div&gt;
            {items.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> (
                &lt;span key={<span class="hljs-built_in">Math</span>.random()}&gt;{item}&lt;/span&gt;
            ))}
        &lt;/div&gt;
    );
}
</code></pre>
<p>In this example, <code>Math.random</code> ensures that we have unique keys. The issue is that the keys aren't stable. Every time the <code>List</code> component re-renders, <code>Math.random</code> runs again, and the keys change.</p>
<p>From React's perspective, these are <em>different</em> elements.</p>
<p>All React can do is remove the elements and recreate them. Compared to updating the elements in place, this is a more expensive operation.</p>
<h3 id="heading-keys-should-go-on-top-level-elements">Keys should go on top-level elements</h3>
<p>When you map an array of data to an array of elements, it's important to put the key on the outermost element. React won't traverse the component hierarchy to find a key, so it won't be able to identify elements correctly.</p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">List</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> items = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];

    <span class="hljs-comment">// Wrong</span>
    <span class="hljs-keyword">const</span> listOne = items.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> (
        &lt;div&gt;
            &lt;span key={item}&gt;{item}&lt;/span&gt;
        &lt;/div&gt;
    ));

    <span class="hljs-comment">// Right</span>
    <span class="hljs-keyword">const</span> listTwo = items.map(<span class="hljs-function"><span class="hljs-params">item</span> =&gt;</span> (
        &lt;div key={item}&gt;
            &lt;span&gt;{item}&lt;/span&gt;
        &lt;/div&gt;
    ));

    <span class="hljs-comment">// ...render</span>
}
</code></pre>
<p>The key has to go on the <code>div</code> instead of the <code>span</code> so that React can use it.</p>
]]></content:encoded></item></channel></rss>