<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Elisabeth Leonhardt</title>
    <description>The latest articles on Forem by Elisabeth Leonhardt (@frontenddeveli).</description>
    <link>https://forem.com/frontenddeveli</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F751291%2F8bbd4d28-b750-4823-8887-741be1b5d800.png</url>
      <title>Forem: Elisabeth Leonhardt</title>
      <link>https://forem.com/frontenddeveli</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/frontenddeveli"/>
    <language>en</language>
    <item>
      <title>How to run GitHub Actions locally</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Wed, 19 Nov 2025 15:45:41 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/how-to-run-github-actions-locally-28nh</link>
      <guid>https://forem.com/frontenddeveli/how-to-run-github-actions-locally-28nh</guid>
      <description>&lt;p&gt;This week, I made an update in our codebase that required a few changes in tests. I fixed them, tested locally and everything was fine. Proud of my accomplishment, I opened a PR, only to see that the tests started failing when running inside the GitHub Action.&lt;/p&gt;

&lt;p&gt;I could have committed a bunch of log messages to the failing tests to find the cause of the issue but that seemed too tedious and I didn't want my coworkers to be able to see all my &lt;code&gt;console.log('do we get here?')&lt;/code&gt; either. So, to maintain the appearance that I am a capable developer, I configured GitHub Actions locally and here is how:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I installed &lt;code&gt;act&lt;/code&gt; &lt;a href="https://nektosact.com/" rel="noopener noreferrer"&gt;https://nektosact.com/&lt;/a&gt; - I used Brew&lt;/li&gt;
&lt;li&gt;You need Docker, so the actions can run inside a container. I had Rancher already installed&lt;/li&gt;
&lt;li&gt;(optional) install the "GitHub Local Actions" extension if you are using VSCode or Cursor and prefer a UI&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I was using cursor and after installing everything, I could see the required components were installed and running and the extension did already detect my workflows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmryps49h1ocx5loohlh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmryps49h1ocx5loohlh.png" alt="Components available" width="411" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30imvegqctkftvoyaq3l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F30imvegqctkftvoyaq3l.png" alt="Workflows for GitHub Actions" width="411" height="119"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When starting the GitHub Actions with the extension, I got the following error: &lt;code&gt;Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevk32atq7b9b71tl80y1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fevk32atq7b9b71tl80y1.png" alt="Error message when starting the workflow locally" width="700" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To solve this, I just copied the command that the extension creates and added a DOCKER_HOST env variable in front, like so:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DOCKER_HOST=$(docker context inspect --format '{{.Endpoints.docker.Host}}') act --workflows ".github/workflows/backend.yaml" --secret-file "" --var-file "" --input-file "" --eventpath ""&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;To run just one simple job and not the whole workflow (maybe you don't need to lint right now) you just add a --job option:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DOCKER_HOST=$(docker context inspect --format '{{.Endpoints.docker.Host}}') act --workflows ".github/workflows/backend.yaml" --job "test" --secret-file "" --var-file "" --input-file "" --eventpath ""&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;The output was quite large in my case, so I redirected everything into log.txt, asked Cursor to go over it for me and find the critical error messages&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;DOCKER_HOST=$(docker context inspect --format '{{.Endpoints.docker.Host}}') act --workflows ".github/workflows/backend.yaml" --job "test" --secret-file "" --var-file "" --input-file "" --eventpath "" &amp;gt; log.txt&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;This way, I found the issue quickly and looked like a hacker in the process. Hope this helps you too.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>fullstack</category>
      <category>github</category>
      <category>devops</category>
    </item>
    <item>
      <title>Unexpected Interview Question: Why do we need lock files?</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Fri, 06 Dec 2024 12:26:54 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/unexpected-interview-question-why-do-we-need-lock-files-3f8a</link>
      <guid>https://forem.com/frontenddeveli/unexpected-interview-question-why-do-we-need-lock-files-3f8a</guid>
      <description>&lt;p&gt;A package-lock.json file is like the Customer Success Manager at your company: They are both present in every project but you are not sure why and what exactly they do. And you are scared of conflicts with them: especially lock file merge conflicts.&lt;/p&gt;

&lt;p&gt;In this article, you are going to learn what's inside a lock file, why it helps with the &lt;em&gt;it works on my machine&lt;/em&gt; problem and why it can even enhance the security of your project.&lt;/p&gt;

&lt;p&gt;⚠️ &lt;em&gt;I use npm for my examples, but the concepts also apply for yarn or other package managers.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How dependencies work
&lt;/h2&gt;

&lt;p&gt;It turns out, your favourite libraries and packages are often not only built with magic, but with other packages we call dependencies. So when installing for example express in your project, you actually install express and with it everything express devs used to build express.&lt;/p&gt;

&lt;p&gt;This is why your node-modules folder suddenly has stuff you did never install: Because the dependencies of your dependencies and their dependencies and their dependencies dependencies are all sitting together making your incredible project work as intended.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93yxtilcl6vr8m911wr8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93yxtilcl6vr8m911wr8.jpeg" alt="What you install vs. what you think you install" width="500" height="759"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tools like npm, pnpm, yarn or bun are actually taking really good care about this so you don't have to: They resolve cyclical dependencies, they deduplicate dependencies if one is required twice and they also have a way to keep dependencies separate if two packages have the same but need different versions. Cool stuff, I know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving the &lt;em&gt;It works on my machine&lt;/em&gt; problem
&lt;/h2&gt;

&lt;p&gt;Let's assume you &lt;del&gt;abandoned&lt;/del&gt; finished your last side project and are starting a new one. You install express and your package.json looks like this now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"side-project-i-wont-abandon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; 
    &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; 
          &lt;/span&gt;&lt;span class="nl"&gt;"express"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.18.0"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two weeks later, you tell a friend about your cool new side project and he wants to join, so he downloads the code and runs &lt;code&gt;npm install&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;❓Question 1: Will his local version of express be a different one than yours because of the caret which allows upgrading of minor versions and patches? It could be for example 4.18.3...&lt;/p&gt;

&lt;p&gt;❓Question 2: What about the dependencies of express? If some updates took place, will these be reflected in his node modules?&lt;/p&gt;

&lt;p&gt;😅 Answer: It depends on whether you have a lock file or not.&lt;/p&gt;

&lt;p&gt;When you first install all the dependencies, a lock file is created. It saves all the exact versions installed plus the versions of the dependencies. Here is a very small example of express with body-parser as it's dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"side-project-i-wont-abandon"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lockfileVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requires"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"express"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.18.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"resolved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://registry.npmjs.org/express/-/express-4.18.2.tgz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"integrity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha512-..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"requires"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"body-parser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.19.0"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"body-parser"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.19.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"resolved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"integrity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha512-..."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When somebody now runs npm install in another environment with that lock file present, the versions that will be installed will be the ones specified in the lock file.&lt;/p&gt;

&lt;p&gt;Therefore, if a lockfile is present, the answer to both questions is NO: the local versions of your friend will be the exact same versions you also have, because they are fixed in the lock file.&lt;/p&gt;

&lt;p&gt;If &lt;em&gt;no&lt;/em&gt; lock file is present, both answers will be YES - there is a possibility that the installed versions will differ - which could impact the functioning of the project. Or: It works on your machine and I have unexplainable errors I have no idea how to debug.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8w3u2ljhfqd9q7k01qh6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8w3u2ljhfqd9q7k01qh6.jpg" alt="They are called lockfiles because they lock versions" width="561" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does the lock file improve security?
&lt;/h2&gt;

&lt;p&gt;Imagine that Henry the Hacker has been highly productive and managed to get access to the express npm account. He changed the version 4.18.2 to contain a vulnerability to exploit the projects installing it. (Something like this has happened for example in 2018: &lt;a href="https://blog.npmjs.org/post/180565383195/details-about-the-event-stream-incident]" rel="noopener noreferrer"&gt;event stream incident&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;If a third person helping on your project would run &lt;code&gt;npm install&lt;/code&gt; now, version 4.18.2 would be downloaded, because that's the version specified in the lock file. &lt;em&gt;However&lt;/em&gt; the lock file also contains a SHA512 hash of the gzipped package. Since adding the vulnerability changed the SHA512 Hash, npm will not proceed with the installation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm ERR! Integrity check failed &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="s2"&gt;"https://registry.npmjs.org/express/-/express-4.18.2.tgz"&lt;/span&gt;
npm ERR! Expected sha512-&amp;lt;original &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; but received sha512-&amp;lt;tampered &lt;span class="nb"&gt;hash&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;the npm error that makes Henry the Hacker mad&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Therefore, your project is secure, as long as you don't update dependencies. 🤯&lt;/p&gt;

&lt;p&gt;But wasn't updating dependencies important to maintain your project secure? Well... yes, but also: not always and not right away. If you are interested, I can write about that another time.&lt;/p&gt;

&lt;p&gt;Hope this made the topic a little more understandable.&lt;/p&gt;

&lt;p&gt;Are there other topics in programming that you just accepted without questioning how they work and why they are there? I would love to hear about them in the comments. ☺️&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@scottrodgerson?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Scott Rodgerson&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/black-metal-locked-gate-gEa4wsI-JFY?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>What advice would you give your Junior self?</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Sat, 23 Nov 2024 20:15:38 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/what-advice-would-you-give-your-junior-self-p9d</link>
      <guid>https://forem.com/frontenddeveli/what-advice-would-you-give-your-junior-self-p9d</guid>
      <description>&lt;p&gt;what were you struggling with in your first role(s) as a junior dev? How did you overcome it? What would you do differently?&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Make table rows clickable as links with tanstack-table and CSS</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Sun, 19 Nov 2023 14:46:41 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/make-table-rows-clickable-as-links-with-tanstack-table-and-css-31bl</link>
      <guid>https://forem.com/frontenddeveli/make-table-rows-clickable-as-links-with-tanstack-table-and-css-31bl</guid>
      <description>&lt;p&gt;You want your entire table row to be a real clickable link? Here, I present you with a practical and accesible solution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2zi4fgq0m0zd0brlxh2o.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2zi4fgq0m0zd0brlxh2o.gif" alt="gif of the result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Link to the codesandbox: &lt;a href="https://codesandbox.io/p/sandbox/busy-brattain-2t34fm?file=%2Fsrc%2Fcomponents%2FTable.tsx%3A1%2C1&amp;amp;layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522clp4f2afl000a2067l6eod13o%2522%252C%2522sizes%2522%253A%255B68.08510638297872%252C31.914893617021278%255D%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522clp4f2afl0003206792wbne95%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522SHELLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522SHELLS%2522%252C%2522id%2522%253A%2522clp4f2afl00072067r47lzkym%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522vertical%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522clp4f2afl00092067t0osnxvi%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B56.71842353%252C43.28157647%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522clp4f2afl0003206792wbne95%2522%253A%257B%2522id%2522%253A%2522clp4f2afl0003206792wbne95%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clp4f2afk00022067x7e3xk4g%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252FREADME.md%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%252C%257B%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252Fsrc%252FApp.tsx%2522%252C%2522id%2522%253A%2522clp4f3tyh00b0206737mjkyju%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%252C%257B%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252Fsrc%252Fcomponents%252FTable.tsx%2522%252C%2522id%2522%253A%2522clp4f4sn700pv2067wuspt554%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%252C%257B%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252Fsrc%252FApp.css%2522%252C%2522id%2522%253A%2522clp4fiuu40alm2067bigyq6b7%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%252C%257B%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252Fsrc%252Findex.css%2522%252C%2522id%2522%253A%2522clp5id3m803j42067daorh2rl%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%255D%252C%2522activeTabId%2522%253A%2522clp4f4sn700pv2067wuspt554%2522%257D%252C%2522clp4f2afl00092067t0osnxvi%2522%253A%257B%2522id%2522%253A%2522clp4f2afl00092067t0osnxvi%2522%252C%2522activeTabId%2522%253A%2522clp4f2afl00082067wty6ob7a%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clp4f2afl00082067wty6ob7a%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TASK_PORT%2522%252C%2522taskId%2522%253A%2522dev%2522%252C%2522port%2522%253A5173%252C%2522path%2522%253A%2522%252F%2522%257D%252C%257B%2522type%2522%253A%2522SANDBOX_INFO%2522%252C%2522id%2522%253A%2522clp5e8gtk004a2067tvhgd0xv%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%255D%257D%252C%2522clp4f2afl00072067r47lzkym%2522%253A%257B%2522id%2522%253A%2522clp4f2afl00072067r47lzkym%2522%252C%2522activeTabId%2522%253A%2522clp4f2n6400792067u2wc73c0%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clp4f2afl00042067ue0uvqet%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522dev%2522%257D%252C%257B%2522id%2522%253A%2522clp4f2afl000520679183s03n%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522build%2522%257D%252C%257B%2522id%2522%253A%2522clp4f2afl00062067qqmq8pbl%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522preview%2522%257D%252C%257B%2522id%2522%253A%2522clp4f2n6400792067u2wc73c0%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TERMINAL%2522%252C%2522shellId%2522%253A%2522clp4f2n9h00cyefipg69y7hzf%2522%257D%255D%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D" rel="noopener noreferrer"&gt;Check it out!&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create your table
&lt;/h2&gt;

&lt;p&gt;Use whatever technologies you like for this: I went with React and tanstack-table, because that’s what I currently use at work.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;ranking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ranking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://google.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Google&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ranking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://bing.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Bing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ranking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://yahoo.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yahoo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columnHelper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createColumnHelper&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Items&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;columnHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ranking&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;columnHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;columnHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getValue&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReactTable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;getCoreRowModel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;getCoreRowModel&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;thead&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeaderGroups&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;headerGroup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;headerGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;headerGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"table-head"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isPlaceholder&lt;/span&gt;
                    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
                    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;flexRender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columnDef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                      &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;thead&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRowModel&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"data-rows"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getVisibleCells&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"table-cell"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;flexRender&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;columnDef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;table&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Step 2: Add an additional column to your table
&lt;/h2&gt;

&lt;p&gt;I added it on the left side, but that’s indifferent. Here, it’s filled with yellow, so you can see it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwwv6z9suscyjo4voe9go.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwwv6z9suscyjo4voe9go.png" alt="Table with one additional column"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 3: Put your link inside the empty column
&lt;/h2&gt;

&lt;p&gt;In the small column, put the link that every row should lead to inside an anchor tag.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;

&lt;span class="p"&gt;...&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;thead&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeaderGroups&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;headerGroup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;headerGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*⬇️ add an extra column here */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"extra-column"&lt;/span&gt; &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Search Engine Links"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;headerGroup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;th&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"table-head"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
...
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tbody&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRowModel&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;tr&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"data-rows"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/*⬇️ add a cell with an empty link for every row
              below the empty column header */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;
                  &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                  &lt;span class="na"&gt;aria-label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
                  &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"row-link"&lt;/span&gt;
                &lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;td&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getVisibleCells&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Step 4: Position the link so it spans over the whole row
&lt;/h2&gt;

&lt;p&gt;Now, that the link is part of the table, let’s make it overlap over the whole row with CSS:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;

&lt;span class="nt"&gt;tr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.row-link&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.data-rows&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#a8a5a5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frw91ibigg8twcsutdzb1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frw91ibigg8twcsutdzb1.png" alt="Whole clickable rows in yellow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I added a yellow background and borders to every element, so it’s easier to see. With the absolute positioning in relation to the table row, the anchor tag spans over the whole row and makes it clickable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hexg658umo8bat3eqvf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hexg658umo8bat3eqvf.png" alt="Red outline for better accessibility"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By adding an adequate outline to the anchor tag, the focus is also including the whole row and making it therefore great to tab through. 🥳&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚠️Notes about Accessibility⚠️
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🧑🏽‍🦯 The empty column header and the empty anchor tags can be confusing for people using screen readers, this is why I added an aria-attribute to them.&lt;/li&gt;
&lt;li&gt;⤵️ I customized the outline for focus-visible, so the links are clearly marked as something clickable and accessible over the keyboard.&lt;/li&gt;
&lt;li&gt;🎨 Mine is of course an example to show the concept - always check for sufficient contrast when choosing your colors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I love this solution - it's so elegant! Hope this helped you!&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>a11y</category>
    </item>
    <item>
      <title>4 Steps to Mitigate Interruptions as a Developer So You Can Finally Keep Up with Your Tasks + Recover Your Attention Span</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Fri, 17 Nov 2023 10:12:15 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/4-steps-to-mitigate-interruptions-as-a-developer-so-you-can-finally-keep-up-with-your-tasks-recover-your-attention-span-28an</link>
      <guid>https://forem.com/frontenddeveli/4-steps-to-mitigate-interruptions-as-a-developer-so-you-can-finally-keep-up-with-your-tasks-recover-your-attention-span-28an</guid>
      <description>&lt;p&gt;What do you think is the most challenging part of being a Developer?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Solving hard problems?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fixing nasty bugs?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Balancing requirements and expectations with technical possibilities?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For me, it's neither of those. It's having to do all of them while constantly being interrupted by Slack messages or Meetings. When I think about what I did during the workday and my answer is: Well, I was mostly on Slack, that feels terribly frustrating.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ibF8SOpc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/byjjbouv2molx3y0r9qs.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ibF8SOpc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/byjjbouv2molx3y0r9qs.gif" alt="Overwhelmed office worker crying" width="480" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thanks to Cal Newport's work, here is the system I built for myself that has reduced interruptions and increased my focus:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Hardware fix: Get slack out of sight and mute notifications
&lt;/h2&gt;

&lt;p&gt;If you want to avoid eating cookies, the worst way to go about it is leaving a huge cookie jar right in front of you on your desk.&lt;/p&gt;

&lt;p&gt;This applies to Slack too: Ideally, close it completely. If that's not an option, hide it so you don't constantly see the blue or red circle on your screen.&lt;/p&gt;

&lt;p&gt;Also, mute all the channels where lot's of random discussions take place:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The general channel&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;All the HR channels&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Yes, the pets channel has to be muted too!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mute chats from people that interrupt you for things that can wait, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Your manager&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The intern that need help every 2 minutes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your work friends - get together over coffee!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tGJBkbR2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ok8vkquv7ei4b7a9z4q.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tGJBkbR2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3ok8vkquv7ei4b7a9z4q.gif" alt="Piece and Quiet" width="400" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Set up slack-times, designated office hours and a solid note-taking system
&lt;/h2&gt;

&lt;p&gt;Now that you silenced everything, you will still get slack messages: how do you handle those?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;During the day, constantly take notes of your questions or items you want to discuss. Everything that you would normally ask on slack right away now goes into your notes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Estimate the time you need to read and answer Slack messages and schedule daily blocks in your calendar. Make an experiment of only answering in these scheduled blocks. Bonus points if you only appear online during these blocks.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Setup office hours and invite everybody to bring their questions and concerns. Have your notes ready so you can ask your questions right away.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--w-NTi-7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/daktpiyaatc8n5w1zegf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--w-NTi-7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/daktpiyaatc8n5w1zegf.gif" alt="Anybody got any questions" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Get people to get used to your slack-times and office hours
&lt;/h2&gt;

&lt;p&gt;Getting people to come to my office hours was easy.&lt;/p&gt;

&lt;p&gt;But they sometimes show up unprepared and later continue messaging me during the day because they forgot to mention the most important items during the checkpoint.&lt;/p&gt;

&lt;p&gt;Here are some things I do to reinforce my system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I tell people I miss their important messages due to the amount of noise in slack &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I tell people I am in the middle of something and I will have more headspace during my office hours&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I delay answers or I don't answer at all&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If it seems to be very urgent and important, I ask directly: Can we take a look at that during office hours or is this a matter of life and death?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some caveat here: If you do all this, but also are the one that constantly asks messages on Slack yourself, people won't take you seriously. Take notes and ask all the questions in your scheduled meetings.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. If you can, take advantage of your time zone and start earlier or later than others
&lt;/h2&gt;

&lt;p&gt;My most productive days are Saturdays and Sundays.&lt;/p&gt;

&lt;p&gt;Because I just know that nobody will message me and I can focus so easily. The same principle however also applies when I work during hours nobody else is working. This is one reason why I shifted my working day to start 2 hours earlier than my coworkers, taking advantage of living in a different time zone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fyyRuLPR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n58qoqr98b5mg1r1378a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fyyRuLPR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n58qoqr98b5mg1r1378a.gif" alt="somebody happily typing on their keyboard" width="550" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This option is not available to everybody, but I wanted to mention it since it improved my productivity a lot.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want more?
&lt;/h2&gt;

&lt;p&gt;Cal Newport's work has completely changed how I look at deep work and productivity.&lt;/p&gt;

&lt;p&gt;If you liked this article, here are a few resources that you might find interesting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.newyorker.com/tech/annals-of-technology/e-mail-is-making-us-miserable"&gt;Email is making us miserable&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.newyorker.com/culture/cultural-comment/slack-is-the-right-tool-for-the-wrong-way-to-work"&gt;Slack is the right tool for the wrong way to work&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://calnewport.com/"&gt;Cal Newports Website&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Did this help? Do you have other strategies? Let me know in the comments!&lt;/p&gt;

</description>
      <category>focus</category>
      <category>productivity</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>5 Important Concepts to Master the useState Hook in React 18</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Sun, 05 Nov 2023 20:46:19 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/5-important-concepts-to-master-the-usestate-hook-in-react-18-2dlb</link>
      <guid>https://forem.com/frontenddeveli/5-important-concepts-to-master-the-usestate-hook-in-react-18-2dlb</guid>
      <description>&lt;p&gt;If you started coding not too long ago, you probably learned React with a bunch of tutorials and hands on guides.&lt;/p&gt;

&lt;p&gt;I know we should all read the docs first, but they can be hard to understand for complete newcomers. Coding tutorials, on the other hand, can be superficial and skip over some more advanced concepts, which leaves you making stupid mistakes or writing inefficient code. They also can create the illusion of completeness: You finished the tutorial, therefore now you know and understand everything, right?&lt;/p&gt;

&lt;p&gt;After more than 4 years of experience with React, here are some concepts about the React useState hook that I learned the hard way:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Always update state with the setState function
&lt;/h2&gt;

&lt;p&gt;You probably did know that, but do you know why that is?&lt;/p&gt;

&lt;p&gt;In React, the value stored as state should be treated as immutable. If you want to update your state, you are required to use the setState function with a new value. If you try to update your state directly, React will fail to notice the update and will not rerender your component nor update the state itself.&lt;/p&gt;

&lt;p&gt;If you want to store state inside a component that doesn't trigger rerenders, use the useRef hook.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. There are two ways to initialize state
&lt;/h2&gt;

&lt;p&gt;Did you know you could initialize state with a function too?&lt;/p&gt;

&lt;p&gt;Remember that the initial value is evaluated only the first time the component renders. If you pass in a function as an argument, it can't have parameters and it should be self-contained - no randomness or side effects are allowed. Pros refer to these as 'pure functions'.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ do this:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;createEmptyTodo&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// 🚩 not this:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;createRandomUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use this approach when you need to initialize state with the result of a computationally expensive function or if you want to avoid placing an entire function inside the useState initializer.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. You can use the previous state in your setter function
&lt;/h2&gt;

&lt;p&gt;Sometimes we forget about the asynchronous nature of the useState hook and try to update it several times inside the same event, ending up with unexpected behavior.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 🚩 this won't work as expected!&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;setAge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// setAge(42 + 1)&lt;/span&gt;
  &lt;span class="nx"&gt;setAge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// setAge(42 + 1)&lt;/span&gt;
  &lt;span class="nx"&gt;setAge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// setAge(42 + 1)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;React does batch setState calls together and rerenders the component afterwards, which is why we have to be careful with the values we pass into the setState function. To avoid bugs, we can pass in an updater function instead of passing in the value directly. This will keep track of the "pending state" (state that has already been updated although it's not yet rendered) and will make the previous code snippet work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ this will work&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;setAge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// setAge(42 =&amp;gt; 43)&lt;/span&gt;
  &lt;span class="nx"&gt;setAge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// setAge(43 =&amp;gt; 44)&lt;/span&gt;
  &lt;span class="nx"&gt;setAge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// setAge(44 =&amp;gt; 45)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Understanding this will save you from a lot of headache when you run into issues where your state isn't updating properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. How to Update Objects Correctly
&lt;/h2&gt;

&lt;p&gt;Remember the first part of this article, where we said that state was immutable?&lt;/p&gt;

&lt;p&gt;This is important for objects! Every time we want to update a part of an object, we have to pass in a completely new object into our set function. The most common way of doing this is by using the spread operator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Replace state with a new object&lt;/span&gt;
&lt;span class="nx"&gt;setForm&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Juan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also remember that you can overwrite existing object properties by adding them a second time to the object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* this will set {
  firstName: 'Juan',
  lastName: 'Gonzales'
} */&lt;/span&gt;
&lt;span class="nx"&gt;setForm&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sara&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Gonzales&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Juan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. You can reset the component's state with the key prop
&lt;/h2&gt;

&lt;p&gt;In React, you normally use the key prop whenever you need to render an array of items.&lt;/p&gt;

&lt;p&gt;But you can also give individual components a key prop and change it when you want to reset their state. Whenever the key changes, the whole component and its children are newly created and therefore all existing state vanishes. If you need to reset the state from inside the component, the best way to do this is to store the original state in a variable and use it for overwriting the modified state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;FormComponent&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectedProduct&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectedProduct&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can read more about his behavior in the &lt;a href="https://react.dev/learn/preserving-and-resetting-state"&gt;official react docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Knowing and understanding the basics will accelerate your coding journey. Do you want me to analyze more hooks? Let me know in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Use TanStack-Query to Write Cleaner React Code as a Junior Developer</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Tue, 24 Oct 2023 19:35:04 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/how-to-use-tanstack-query-to-write-cleaner-react-code-as-a-junior-developer-2k88</link>
      <guid>https://forem.com/frontenddeveli/how-to-use-tanstack-query-to-write-cleaner-react-code-as-a-junior-developer-2k88</guid>
      <description>&lt;p&gt;Showing data obtained by external sources is a core part of most frontend applications.&lt;/p&gt;

&lt;p&gt;Most of the data comes from an API or a CMS. If the amount of data to be fetched is small, this can easily done with react-native hooks in combination with the fetch api. However, if the data flow between client and server grows more complicated, we are at risk of creating tons of unmaintainable spaghetti code.&lt;/p&gt;

&lt;p&gt;To avoid this, we need a better tool for the job: React Query.&lt;/p&gt;

&lt;p&gt;Here are the steps to get started with react query to improve your code:&lt;/p&gt;

&lt;h3&gt;
  
  
  1.  Understand the difference between client state and server state
&lt;/h3&gt;

&lt;p&gt;Client state lives in the browser, like form state, dark mode or whether a modal is opened or closed. Server state lives probably in some database and is served asynchronously.&lt;/p&gt;

&lt;p&gt;Always use React Query for server state. Use native hooks like useState or useReducer for client state.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Install React-Query in your project
&lt;/h3&gt;

&lt;p&gt;Follow the &lt;a href="https://tanstack.com/query/latest/docs/react/installation"&gt;installation instructions&lt;/a&gt; and don't forget to set up the &lt;a href="https://tanstack.com/query/latest/docs/react/quick-start"&gt;query client provider&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  3. In your code, replace data fetching code and states with the useQuery hook
&lt;/h3&gt;

&lt;p&gt;Instead of writing this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rickandmortyapi.com/api/character&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Network response was not ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;write this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fetchRickAndMortyCharacters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rickandmortyapi.com/api/character&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Network response was not ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rickAndMortyCharacters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;fetchRickAndMortyCharacters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data you need to render will be in the destructured &lt;code&gt;data&lt;/code&gt;. Here is an example of how you can render the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Error: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;No data available&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;character&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Convert the useQuery hook into a custom hook
&lt;/h3&gt;

&lt;p&gt;For better reusability, abstract this into a custom hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;useRickAndMortyCharacters&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rickAndMortyCharacters&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;fetchRickAndMortyCharacters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;now, from any component, you can just call the custom hook like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useRickAndMortyCharacters&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it! If you want to go any further, read the docs and play around with different &lt;a href="https://tanstack.com/query/latest/docs/react/reference/useQuery"&gt;query options&lt;/a&gt; or with &lt;a href="https://tanstack.com/query/latest/docs/react/guides/mutations"&gt;mutations&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading!&lt;/p&gt;

</description>
      <category>react</category>
      <category>beginners</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Setup SonarQube locally for your Next.js project</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Tue, 22 Aug 2023 16:43:16 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/setup-sonarqube-locally-for-your-nextjs-project-497m</link>
      <guid>https://forem.com/frontenddeveli/setup-sonarqube-locally-for-your-nextjs-project-497m</guid>
      <description>&lt;h2&gt;
  
  
  What is SonarQube?
&lt;/h2&gt;

&lt;p&gt;SonarQube is an inspection tool that analyzes aspects of your code, like code quality, code smells but also tech dept and security vulnerabilities. It's a good addition to Prettier and Eslint to ensure code quality.&lt;/p&gt;

&lt;p&gt;Sometimes, adding SonarQube to your CICD or even paying SonarCloud is out of scope and out of budget for your project. In this guide, I show you how to set it up locally:&lt;/p&gt;

&lt;p&gt;⚠️ All the instructions I give are tested on a Debian based distribution, you might have to adjust them slightly for other operating systems! ⚠️&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install the SonarLint extension
&lt;/h2&gt;

&lt;p&gt;SonarLint works together with Eslint and prettier to notify you about possible issues as you are coding. Install the extension in your editor it to make your life easier: &lt;a href="https://www.sonarsource.com/products/sonarlint/" rel="noopener noreferrer"&gt;https://www.sonarsource.com/products/sonarlint/&lt;/a&gt;&lt;br&gt;
For VSCode, it looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy686avbzuqabxmyg5759.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy686avbzuqabxmyg5759.png" alt="SonarLint extension for vscode"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Setup the SonarQube server
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;In a location of your choice, create a docker-compose.yml file with the following content:
```yaml
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;version: "3"&lt;br&gt;
services:&lt;br&gt;
  SonarQube:&lt;br&gt;
    image: sonarqube:community&lt;br&gt;
    depends_on:&lt;br&gt;
      - db&lt;br&gt;
    environment:&lt;br&gt;
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonar&lt;br&gt;
      SONAR_JDBC_USERNAME: sonartest&lt;br&gt;
      SONAR_JDBC_PASSWORD: sonartest&lt;br&gt;
    volumes:&lt;br&gt;
      - SonarQube_data:/opt/sonarqube/data&lt;br&gt;
      - SonarQube_extensions:/opt/sonarqube/extensions&lt;br&gt;
      - SonarQube_logs:/opt/sonarqube/logs&lt;br&gt;
    ports:&lt;br&gt;
      - "9000:9000"&lt;br&gt;
  db:&lt;br&gt;
    image: postgres:12&lt;br&gt;
    environment:&lt;br&gt;
      POSTGRES_USER: sonartest&lt;br&gt;
      POSTGRES_PASSWORD: sonartest&lt;br&gt;
      POSTGRES_DB: sonar&lt;br&gt;
    volumes:&lt;br&gt;
      - postgresql:/var/lib/postgresql&lt;br&gt;
      - postgresql_data:/var/lib/postgresql/data&lt;br&gt;
volumes:&lt;br&gt;
  SonarQube_data:&lt;br&gt;
  SonarQube_extensions:&lt;br&gt;
  SonarQube_logs:&lt;br&gt;
  postgresql:&lt;br&gt;
  postgresql_data:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This contains the images for two containers: The first one is the SonarQube server and the second one is a database that SonarQube needs to store all its configurations.
- Start up the docker-compose with the command: `docker-compose up -d`

### Fixing SonarQube Errors Part 1
In the beginning, you will maybe notice that the SonarQube container dies and if you execute the command `docker compose logs`, you will see the following error message: `ecl-frontend-SonarQube-1  | bootstrap check failure [1] of [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]`

- To solve this, change the vm.max_map_count setting in /etc/sysctl.conf with the following command: `echo "vm.max_map_count=262144" | sudo tee -a /etc/sysctl.conf`
- Then, reload the configurations: `sudo sysctl -p`
- For Docker on Windows, you can go to the Docker Desktop settings and under Deamon, switch to Advanzed. There, you can add the following configuration:
```json


{
  "sysctls": {
    "vm.max_map_count": "262144"
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;You will have to restart Docker Desktop afterward for the change to be available.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fixing SonarQube Errors Part 2
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Most likely, the SonarQube container will still fail with the error: &lt;code&gt;FATAL: database "sonar" does not exist&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To fix this, get a shell inside the PostgreSQL container &lt;code&gt;docker-compose exec db psql -U sonartest&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;And once inside the container, create the sonar database with the command: &lt;code&gt;CREATE DATABASE sonar;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then, exit the psql shell with &lt;code&gt;\q&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Now that the database is created, you can run &lt;code&gt;docker-compose up -d&lt;/code&gt; again or restart the SonarQube service with &lt;code&gt;docker-compose restart SonarQube&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The SonarQube server should now be available at &lt;code&gt;localhost:9000&lt;/code&gt;the default credentials are username: admin and password: admin&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Run SonarQube in your project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;First, follow the steps to install the sonar scanner for your operating system: &lt;a href="https://docs.sonarcloud.io/advanced-setup/ci-based-analysis/sonarscanner-cli/" rel="noopener noreferrer"&gt;https://docs.sonarcloud.io/advanced-setup/ci-based-analysis/sonarscanner-cli/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a file with the name sonar-project.properties in your project root and adjust it accordingly:
```bash
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Organization and project keys are displayed in the right sidebar of the project homepage
&lt;/h1&gt;

&lt;p&gt;sonar.organization=my_organization&lt;br&gt;
sonar.projectKey=my_project&lt;br&gt;
sonar.host.url=&lt;a href="https://sonarcloud.io" rel="noopener noreferrer"&gt;https://sonarcloud.io&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  --- optional properties ---
&lt;/h1&gt;

&lt;h1&gt;
  
  
  defaults to project key
&lt;/h1&gt;

&lt;h1&gt;
  
  
  sonar.projectName=My project
&lt;/h1&gt;

&lt;h1&gt;
  
  
  defaults to 'not provided'
&lt;/h1&gt;

&lt;h1&gt;
  
  
  sonar.projectVersion=1.0
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Path is relative to the sonar-project.properties file. Defaults to .
&lt;/h1&gt;

&lt;h1&gt;
  
  
  sonar.sources=.
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Encoding of the source code. Default is default system encoding
&lt;/h1&gt;

&lt;h1&gt;
  
  
  sonar.sourceEncoding=UTF-8
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Enter the server on [localhost:9000](http://localhost:9000) and create a new project
- Select a name and a baseline branch for the project

![SonarQube Name selection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j12snfttx41h18dcz6uk.png)

- Choose how SonarQube should analyze your code changes. In my case, I choose the reference branch option because we are working with feature branches, but you will have to adapt this option to your workflow.

![SonarQube analization pattern](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nea7qn2vtszpcas6n9sb.png)

- In the next step, you will be asked how you want to analyze your repository. Since we are configuring a local setup, let’s select “locally”


![Local Analysis with SonarQube](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t9dijrqhnazdx8vn9mq4.png)

- Now, generate a token for your project. This will link your SonarQube server with your code.
- Once you have your token, SonarQube will let you choose a programming language and an operating system and based on that, will give you the command you can execute to run your analysis.
![Select Programming Language and OS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rxb7av8qvi700zvir7ox.png)

- Run the command that SonarQube gives you at the root of your project.
- After running the command, SonarQube should show the project results in your Dashboard. Click on the project and inspect its findings. Make sure to click on “Overall Code” to be able to see problems in your whole codebase and not only for the latest changes.


![SonarQube results](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fghbi4ff2it2mpbo7h9x.png)



That’s it - you did successfully configure SonarQube on your local machine. As long as you don’t delete the volumes of the docker-compose, the data will stay even if you turn off your machine or kill the docker containers.

Is there a better way? Let me know in the comments! I hope this was helpful!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
    </item>
    <item>
      <title>You don’t need a company training plan</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Wed, 05 Jul 2023 09:13:42 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/you-dont-need-a-company-training-plan-afk</link>
      <guid>https://forem.com/frontenddeveli/you-dont-need-a-company-training-plan-afk</guid>
      <description>&lt;p&gt;In software, I see a lot of effort and money going into training. Companies tend to have ramp-ups or boot camps for newcomers, courses and training plans for developers, and different training for managers and the product area. At most of the companies I worked for, I had to fill out forms about my technical abilities, but also about my training wants and needs. Normally, I see a training plan listed in job offers alongside other perks. There is no doubt that companies want to train their personnel and are willing to spend time and resources on it.&lt;/p&gt;

&lt;p&gt;I am big into training: I love attending workshops and taking courses, I have written training plans for people at my company and I have even given company-wide training as part of the developer training plan. Last year I was even given a special mention in a company email as a person who had given lots of talks and training at the company…&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_ScdQzw5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nmqyb4tishv6jt1d300n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_ScdQzw5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nmqyb4tishv6jt1d300n.png" alt="Mention of me giving lots of training" width="637" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, I feel there is a lot of money and effort wasted: First, I want to explore why and afterward, propose a better approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I am not a fan of company training plans:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Training plans are a product of randomness and luck&lt;/strong&gt;: While training plans tend to be listed as a perk in the job description, whether you end up having a training plan or not, depends hugely on your manager and/or your tech lead. This adds a lot of randomness and luck to your professional growth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skill levels are sometimes not correctly evaluated&lt;/strong&gt;: The evaluation of your current and future skill levels tends to be very little specific. I have been told my React skills were a 3 out of 5 and was asked to improve to have at least a 4. No further specifications were given.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Management has no interest in progress:&lt;/strong&gt; Often, there is no space to discuss feedback on improving and learning in general. Results are only discussed during performance reviews, that take place once or twice a year. At that point (aka when it’s too late) your manager has already decided whether you reached your goal or not to justify your salary increase or the lack thereof. Real improvement needs regular feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Training is sometimes used as a break:&lt;/strong&gt; In the training I gave, I had between 30-50 attendees. But only a handful would participate, most of them would sit muted in the meeting and disappear for the quiz at the end. I am not judging the people that want a break, but if that’s what people attend training for, we won’t improve code quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Training is sometimes not added to Sprint estimates:&lt;/strong&gt; Training takes time and mental headspace away from the Sprint. If this is not accounted for in your planning, you might be less inclined to attend training or to take courses. That further exacerbates the problem that people who already have difficulties keeping up with their tasks won’t improve because they have no time to study.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Giving training can burn the teachers out:&lt;/strong&gt; Preparing training is hard work. Yes, even in times of ChatGPT. I have to think about the content, how to structure the content, how to make the content understandable, and how to create examples that are applicable on a day-to-day basis. I have to think of exercises, homework, and exam questions. I have to prepare a repository for people to play with. It’s also a little nerve-racking to expose yourself, your knowledge, and the limitations of said knowledge. If all of this is just added on top of the normal tasks, volunteer teachers can be discouraged from giving training.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The results of training and training plans are hard to measure:&lt;/strong&gt; Never have I ever heard somebody say: “The quality of X in our project did improve thanks to your training.” Or “I finally got quicker with Y thanks to what you taught me”. But shouldn’t that be the goal? It’s nice to know people liked my training, but it feels more important to know that people use the concepts and work more efficiently.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that the ranting part is over, let’s formulate some proposals to improve training efficiency for everybody:&lt;/p&gt;

&lt;h2&gt;
  
  
  If you need a training plan:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Assume responsibility:&lt;/strong&gt; I want you to understand that your training and learning are only your responsibility. Don’t whine about your company not giving you an adequate training plan, access to courses, or whatever. You don’t want a random person to decide whether you improve professionally or not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create your training plan yourself:&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Make a list of training needs and wants:&lt;/strong&gt; Create a new notion document or .txt file or whatever suits you. Is there something you are really bad at? (Is it CSS?) Write it down. Something that’s part of your tasks but you dread doing it and you always do it wrong. Write that down too. Something that would make your day 100 times easier if you just knew more? (Could be git…) Some article you liked but you never read? Find it in your infinite supply of bookmarks you never look at. Something that interests you but you never took the time to try it? You guessed it, it goes on the list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Order and prioritize:&lt;/strong&gt; I like to prioritize my list a little bit and distinguish between short learning projects (reading an article or watching a Youtube video) and longer learning projects (going through an entire course). I also like to evaluate how much impact would this knowledge have on my day-to-day tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Now, work on your plan!&lt;/strong&gt; Sometimes, you have 15 minutes in between meetings. Instead of scrolling Instagram, read this article you bookmarked years ago. Find half an hour at the end of the day to go through your Udemy course. Don’t get overwhelmed if there is a lot on your plan and you have little time. Just try to get into the habit of reading/studying something every week.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share your plan with your tech lead and your manager:&lt;/strong&gt; If you are in luck, they are interested and can even give you feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Or: Don’t share your plan with your tech lead or manager:&lt;/strong&gt; Sometimes, the people who manage you are not interested or don’t take the time. That’s ok. Don’t dwell too much on it and make the best out of your current situation. Sometimes you also want to feel free and not give too many explanations. Do what you consider the best option in your current situation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collect evidence that you worked on your training plan.&lt;/strong&gt; Keep a log of what you did and the results that produced. Even if they are tiny wins. For yourself, but also for your performance reviews. If you went through a CSS course and therefore are producing better CSS, this is something you should mention in your 1:1s. Mention new tiny wins every time you can. &lt;strong&gt;Keep in mind that you have to give visibility to the extra effort you put in, nobody else will notice.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  If you are somebody who should be writing training plans:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Make it a priority:&lt;/strong&gt; I know, you have little time. But if people improve, everybody can work better together and you can delegate more. I have been able to delegate plenty of tasks to juniors and even interns but just giving them a few hours to take a $10 Udemy course.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask them to write their plan and review it.&lt;/strong&gt; Give them nice feedback and maybe some help on how and where to start. Everybody is different, some people need more guidance than others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ASK AND LISTEN:&lt;/strong&gt; I want people to come to me and ask questions, but that sometimes doesn’t happen. Ask how things are going with the plan regularly. Listen to what they have to say, just listen. Ask them how they are going to solve the issues they have. Only make suggestions when asked. You don’t want to do their work, you want to enable them to solve whatever stands in their way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If somehow possible, give them time to work on their training plan:&lt;/strong&gt; I know that’s a slippery slope since people sometimes use this as an excuse not to work, but they also don’t work on their training plan. So you will have to use your best judgment with this one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep track of improvements you see:&lt;/strong&gt; Imagine you put a lot of effort into something at work and your superior doesn’t even notice. Ask if the person itself feels improvements but also talk about the improvements you see. If possible, regularly, not only once a year.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;An adult in the workforce should be able to take responsibility for his professional development instead of exposing it to the randomness of managers and company policies. On the other hand, superiors can give employees better opportunities to develop by showing interest and giving constructive feedback. If everybody takes care of his training in a proactive manner, randomness is removed, improvements are easier to track, and human resources doesn’t have to play guessing games on what to do regarding training.&lt;/p&gt;

&lt;p&gt;What are your thoughts? Do you have different or better solutions? Let me know down below in the comments.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>productivity</category>
      <category>codenewbie</category>
    </item>
    <item>
      <title>Simple mutations with TanStack Query and Next.js</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Mon, 24 Apr 2023 20:21:07 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/simple-mutations-with-tanstack-query-and-nextjs-4b0m</link>
      <guid>https://forem.com/frontenddeveli/simple-mutations-with-tanstack-query-and-nextjs-4b0m</guid>
      <description>&lt;p&gt;Mutations with TanStack Query have always been a little scary - at least until I decided to give them a try. After using them a lot, there is no turning back.&lt;/p&gt;

&lt;p&gt;So today I want to show you how you can write your first mutation and guide you through the little pitfalls that can happen along the way.&lt;/p&gt;

&lt;p&gt;The code for this example is in &lt;a href="https://github.com/elisabeth-leonhardt/tanstack-query-pocs" rel="noopener noreferrer"&gt;this repository&lt;/a&gt;, which contains a collection of TanStack proof of concepts I wrote. The mutation we are talking about &lt;a href="https://github.com/elisabeth-leonhardt/tanstack-query-pocs/blob/28d05af5d6567c7f17f1b74c939cc4179c20579d/pages/mutationWithRefetch.js" rel="noopener noreferrer"&gt;is here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are mutations for?
&lt;/h2&gt;

&lt;p&gt;Mutations are designed to be used for everything that's not a GET operation, so we can use them to POST, PUT and/or DELETE data. To make it simple, I set up a mock database with json-server and a few todo-items that our beloved characters from the Rick and Morty universe will have to do during the episodes. Don't forget to execute &lt;code&gt;npm run json-server&lt;/code&gt; to start it up.&lt;/p&gt;

&lt;p&gt;We want the result to look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcrlp00akyjyp4kfpzwny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcrlp00akyjyp4kfpzwny.png" alt="Final Rick and Morty todo list"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a new todo
&lt;/h2&gt;

&lt;p&gt;Let's start with writing the function for the actual POST first. This should be very straightforward. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:8000/todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;nanoid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;done&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This just uses fetch to send a post request with the todo passed in as an argument and returns the result. An error is thrown if something goes wrong.&lt;/p&gt;

&lt;p&gt;Now, we can create a form to write our todos into:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onFormSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid grid-cols-1 gap-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex gap-4 items-end"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;htmlFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"user"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xl font-bold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            User:
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-black flex-1"&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"user"&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"user"&lt;/span&gt;
            &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;changeTodo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex gap-4 items-end"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;htmlFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"task"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xl font-bold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            Task:
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-black flex-1"&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"task"&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"task"&lt;/span&gt;
            &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;changeTodo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"justify-self-end"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          create todo
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;of course, this also requires some state and functions that handle state change:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setTodo&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;changeTodo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="nx"&gt;newObject&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;setTodo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;newObject&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;now that this is set up, let's look at the mutation itself. The useMutation hook has only one mandatory argument, which is the function that posts to our API and which has to return a Promise. In our case, that would be:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createTodoMutation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, the only thing left to do is to tell the form to actually mutate data every time a todo is submitted. You can do this for example in the onSubmit handler:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onFormSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;createTodoMutation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So let's recap:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The user types a todo into the field and clicks submit.&lt;/li&gt;
&lt;li&gt;In the submit handler, the default is prevented and the mutate function for the useMutation hook is called. As an argument, it takes the data you want to pass to the createTodo function.&lt;/li&gt;
&lt;li&gt;The createTodo function makes the API call and now you should see that json server indicates the POST request in his logs. Also, your new todo should be visible inside the db.json file.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  List all todos
&lt;/h2&gt;

&lt;p&gt;That's all nice and good, but we still can't see our todos on the page, the form only allows us to submit new ones. So let's add some quick code to visualize our todos.&lt;/p&gt;

&lt;p&gt;We need a function to fetch from our API:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchTodos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:8000/todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In our component, we can use the useQuery hook to obtain our todos:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;fetchTodos&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;To make my code a litte cleaner, I created an extra todo component:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"bg-white text-black rounded-lg grid grid-cols-[auto_1fr_auto] items-center gap-4 py-2 px-4"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt;
        &lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;changeTodoStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"profile picture"&lt;/span&gt;
        &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"rounded-full"&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And now we only need to iterate over our todos and show them like so:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"loader"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid grid-cols-2 gap-4 pt-8"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSuccess&lt;/span&gt;
          &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Todo&lt;/span&gt; &lt;span class="na"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Todo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;
          &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  We are done, right?
&lt;/h2&gt;

&lt;p&gt;If you tried this, you would expect the new todo to appear automatically after you created it, but this doesn't happen. That's because we never told our useQuery hook to update it's query. Since the database changed, we have to find a way for the hooks to communicate with each other: Couldn't maybe the useMutation hook tell the useQuery hook that is has to update the cache?&lt;/p&gt;

&lt;p&gt;Yes it can and that's what we are going to do. Let's change our previous useMutation hook for the following:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQueryClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;changeTodoMutation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updateTodo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invalidateQueries&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;queryKeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We are deciding to invalidate the queries that contain the queryKey "todos", which is a declarative way to ask TanStack query to update the corresponding data. We could also do something like `queryClient.refetch({queryKeys: ["todos"]}), which would be imperative but the recommended way is just to invalidate the cache and let the library handle the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That's a very basic example I have used a lot: Posting something and immediately refetching it to keep the UI updated. Maybe you already suspect how to update a single todo? Try it yourself and then check the solution in the repository.&lt;br&gt;
_&lt;/p&gt;

&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@kmuza?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Carlos Muza&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/hpjSkU2UYSU?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>tanstackquery</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I tried Remix and it is too good to be true</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Fri, 09 Sep 2022 13:01:42 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/i-tried-remix-and-it-is-too-good-to-be-true-4im</link>
      <guid>https://forem.com/frontenddeveli/i-tried-remix-and-it-is-too-good-to-be-true-4im</guid>
      <description>&lt;p&gt;&lt;a href="https://www.bitlogic.io/blog/como-evaluar-nuevas-tecnologias" rel="noopener noreferrer"&gt;Spanish Translation/Traducción al Español&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I struggle to finish user stories.&lt;br&gt;
Before any PR or before assigning the story to QA, I open all of the code again. I read it line by line. Especially the parts where I had to compromise, where I wasn’t happy about how I did things, but I also didn’t know how to improve. I think: “If I just knew a little more, if I just had more experience, my code could be more performant, more elegant, or more readable…”&lt;br&gt;
Of course, I submit the PR, and the tasks are closed on time. However, a part in my head doesn’t want to accept the “Done” and it saves the problem somewhere in a dark corner.&lt;br&gt;
And after weeks, or sometimes months, I am reading an article, watching a video, or listening to an explanation when suddenly my head goes: “Now I know!”&lt;br&gt;
I can’t always go back to the code and improve it, but I discovered a new way of solving a problem, which makes me a better programmer.&lt;br&gt;
When I started learning about Remix, I had been using Next.js for almost 2 years. I also have experience with Gatsby. While reading the Remix docs, I had so many “Now I know!” experiences that I felt the need to share them. Like a woodworker needs specific tools to build furniture, as programmers, we need to know patterns, techniques, libraries, and frameworks so our code is easily scalable, maintainable, and can stand the test of time.&lt;br&gt;
In the following sections, I want to go over my investigation process and why I believe that Remix is a valuable addition to my toolbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1: Collecting Information
&lt;/h2&gt;

&lt;p&gt;To understand why Remix is a good tool to have in mind for a future project, it’s important to understand the alternative technologies with their advantages and disadvantages. Here is a quick summary of my own experience mixed with what I learned about Remix:&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages and Disadvantages of SSG
&lt;/h3&gt;

&lt;p&gt;Static websites are the perfect use case for server-side generation. In the React world, a very common tool for developing static sites is Gatsby. After developing the site, it can be built and deployed, for example with services like Netlify, taking advantage of the CDN and thus reducing latency for the end-user. Problems start to arise if we want to do small changes, like fixing typos or adding a new banner to the home page. After the modification in the code, the whole page has to be rebuilt and redeployed.&lt;br&gt;
Depending on the sites complexity and the amount of media that needs optimizing, rebuilding plus redeploying can take a considerable amount of time: Kent C. Dodds talks about this issue in his presentation about Remix: His built times were so long that they produced a timeout in Netlify.&lt;br&gt;
To solve the mentioned problems, incremental static generation (ISR) was developed. This technology allows us to only rebuild the pages that changed. But if the required change affects a component present in all blog articles, its advantages can be diminished.&lt;br&gt;
It is also possible to add dynamic data to static sites, but they have to be fetched on the client side. Static content has to be downloaded, javascript has to be executed, and dynamic content has to be fetched and then inserted into the DOM. This can take some time and therefore cause unexpected visual jumps on the site, also called cumulative layout shift (CLS). Unless a placeholder like a spinner or a skeleton is shown until the content appears, this can cause a bad user experience and can lead to your page failing core web vitals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Advantages and Disadvantages of SSR
&lt;/h3&gt;

&lt;p&gt;When a site has so much dynamic content that it is not feasible anymore to implement a statically generated site, server-side rendering is the answer. Upon request, the server generates the rendered content and sends it over to the client, thus making it possible to change content constantly. In the React World, Next.js is a framework frequently used in that case.&lt;br&gt;
Since content is already rendered on the server side, cumulative layout shift is also taken care of. But we lose the advantages of the CDN and loading times can become an issue depending on a variety of factors such as server response times and latency between server and client.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remix: the best of both worlds
&lt;/h3&gt;

&lt;p&gt;If we don’t want to make compromises between the two technologies, Remix can be a good option because of the following:&lt;br&gt;
With Remix, you can only build SSR applications, but it is built to take advantage of distributed infrastructure as a service like Cloudflare Workers or Fly, so you host your app as close to the user as possible.&lt;br&gt;
Remix discourages client-side data fetching, which eliminates the need to ship libraries such as SWR, tanstack-query, or apollo to the browser. This of course implies a smaller bundle size and lots of saved mobile data for users who browse with their phones.&lt;br&gt;
Since Remix pages are server-side rendered, layout shifts are taken care of.&lt;br&gt;
I found several more aspects where Remix could introduce a performance benefit:&lt;br&gt;
Some sites just become interactive when their javascript finished loading. This can confuse the user in the first seconds on the page if he tries to type into an input that is not responding. It is Remix’s philosophy to progressively enhance the site with javascript, not enable the site with javascript. This implies that inputs, menus, and buttons are immediately responsive, without depending on javascript.&lt;br&gt;
Compared to Next.js, Remix can prefetch dynamic sites, for example, a result page while the user types in a search, thus making results appear almost instantaneously. This is possible thanks to Remix being a compiler, a server framework, and a client framework, therefore Remix can prepare the required components and fetch the necessary data while the user is still typing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 2: Try it out
&lt;/h2&gt;

&lt;p&gt;With all that in mind, I started to dig deeper into the documentation and I created a repository to put the concepts into practice.&lt;/p&gt;

&lt;p&gt;You can find the repository with all the code examples &lt;a href="https://github.com/elisabeth-leonhardt/remix-introduction" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a better learning experience, Remix provides a tutorial that guides you through the process of building a full-stack application from scratch with Remix. With that, I was able to play around with the following concepts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File-based nested routing: With the help of the React Router documentation and the Remix tutorial, one part of my app was built just to play around with the concept of nested routing that Remix introduces. It facilitates layout composition: if your whole page needs a header and a footer and an administrative part of your page also needs a list of companies in a side-menu, Remix allows adding a nested layout for the /invoices domain with ease.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kenmzmt7c8gc4yunu67.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kenmzmt7c8gc4yunu67.png" alt="Nested Routing ilustration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;(Kind of) Modular CSS and Tailwind: In Remix, stylesheets directly inserted into the HTML as link tags and CSS-in-JS solutions are discouraged. This supports the philosophy of only using javascript to progressively enhance the page. Although Remix is a SAP, it is possible to code-split stylesheets so they are only downloaded for a page that the user is visiting and therefore feel like modular CSS to the developers. I also tried Tailwind, which was a breeze to configure and made the little styling I added very convenient.&lt;/li&gt;
&lt;li&gt;Data loaders: Since data fetching should be done on the server, it is necessary to implement so-called data loaders which later make data available to the client with the help of a useLoaderData hook. I implemented a loader for a REST API and one for GraphQL and they were incredibly easy and straightforward to write.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RICK_AND_MORTY_REST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://rickandmortyapi.com/api/character&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://rickandmortyapi.com/api/character&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GetCharacters&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gql&lt;/span&gt;&lt;span class="s2"&gt;`
  {
    characters(page: 2) {
      results {
        name
        id
        image
        status
        location {
          name
        }
      }
    }
  }
`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;GetCharacters&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Form Handling: One of Remix particularities consists in how it handles forms since it is possible to execute form handlers on the server side. By doing that, we avoid useState hooks for our input components, client-side javascript for event handlers, and the famous event.preventDefault(). As a consequence, the input is responsive right from the start.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;task&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assignee&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assignee&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fieldErrors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;validateTaskLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;assignee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;validateNameLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;assignee&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fieldErrors&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;badRequest&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;fieldErrors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;assignee&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:8000/todos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;task&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;assignee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assignee&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Error boundaries. While it is possible to anticipate failures and handle them, it is not easy to manage unexpected failures. Normally, when an unforeseen failure occurs, the screen just goes blank without any indication of error for the end user. To avoid that kind of behavior, React 16 implemented Error Boundaries, which allow capturing unexpected failures and showing them only inside the predefined Error Boundary while maintaining the rest of the application usable.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ErrorBoundary&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;invoiceId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useParams&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'text-red-500'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      We are sorry, we couldn't display the invoice with id: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;invoiceId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fullwg9vwcuraz4szkqwl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fullwg9vwcuraz4szkqwl.png" alt="Illustration of Error Boundary"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 3: Teach it!
&lt;/h2&gt;

&lt;p&gt;With all that freshly acquired knowledge, I was starting to get excited to share it with others. I made the repository public and put together a set of slides to give a talk at my company. On the day of the talk, everybody attending was able to clone the repository and get their hands dirty with the code while I was explaining. I chose this approach to provide a more practical and hands-on introduction to the subject, but it was also important for me that everybody attending would have a working example of all the concepts to continue experimenting. We made a live transmission on Youtube, and if you understand Spanish, you can watch the recording here: &lt;a href="https://www.youtube.com/watch?v=8Ag-lLXvR0Q" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=8Ag-lLXvR0Q&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It’s a little unfair to compare Next.js and Gatsby with Remix since Remix had the chance to learn from the mistakes and inefficiencies of its predecessors. With that said, after my research, I can say that the creators of Remix did an excellent job in creating a framework that provides great performance combined with awesome developer experience. In my case, I will have Remix in mind when defining a stack for a new project, so I can offer the optimal solution for the particular use case in question.&lt;/p&gt;

</description>
      <category>remix</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>How to use client-side only packages with SSR in Gatsby and Next.js</title>
      <dc:creator>Elisabeth Leonhardt</dc:creator>
      <pubDate>Sat, 07 May 2022 23:55:58 +0000</pubDate>
      <link>https://forem.com/frontenddeveli/how-to-use-client-side-only-packages-with-ssr-in-gatsby-and-nextjs-3pfa</link>
      <guid>https://forem.com/frontenddeveli/how-to-use-client-side-only-packages-with-ssr-in-gatsby-and-nextjs-3pfa</guid>
      <description>&lt;p&gt;So you started a project in Gatsby or Next.js to take advantage of static site generation or server-side rendering and its performance benefits because that's what Youtube/StackOverflow/some course told you to do. You are coding along happily until this error appears:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ERROR &lt;span class="c"&gt;#95312 &lt;/span&gt;
&lt;span class="s2"&gt;"window"&lt;/span&gt; is not available during server side rendering.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maybe, you are a heroic senior dev who just understands this error, fixes it in 2 minutes, and goes on with his work. (Why are you even reading this?) If this is not the case, here is a down-to-earth explanation and a few ideas on how to solve it.&lt;/p&gt;

&lt;p&gt;If you just want the fix: &lt;a href="https://github.com/elisabeth-leonhardt/gatsby-csr-test"&gt;Here&lt;/a&gt; is the repository with the solution for Gatsby and &lt;a href="https://nextjs.org/docs/advanced-features/dynamic-import"&gt;here&lt;/a&gt; is what you need for Next.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Basics first: Understanding SSR versus CSR
&lt;/h2&gt;

&lt;p&gt;I had lots of trouble understanding what exactly the difference between the two is, so I hope to enlighten you with an analogy:&lt;br&gt;
 Remember the last time you went to IKEA: you chose a beautiful bookshelf for your home, fought with your partner over it, got a heavy box from IKEAs warehouse-like part, made up with your partner over a hotdog, and then went home and assembled your purchase (without insulting the instructions, of course).&lt;/p&gt;

&lt;p&gt;This is how client-side rendering works: Your browser requests a page, gets a nice block of code, pulls out his tools (Javascript, in this case), and builds the page together. Ikea in this analogy is the server, and your home is the client. Since you assemble your bookshelf client-side, it takes you a little longer and maybe a support call until you can enjoy your new furniture, but for Ikea, it was convenient to provide you with one of its products.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/OMeGDxdAsMPzW/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/OMeGDxdAsMPzW/giphy.gif" width="500" height="292"&gt;&lt;/a&gt;&lt;br&gt;How our browser should not assemble the webpage
&lt;br&gt;
 &lt;br&gt;
Assuming, you are not a handyman and you decide to pay IKEA to deliver your bookshelf already assembled to your home. You won't be able to enjoy the hotdog, but it's worth the evaded fight with your partner. You just request what you need and once you get it delivered, it is ready to go.&lt;/p&gt;

&lt;p&gt;This is how server-side rendering works: the server we requested the page from executes all the code in a node server: your browser just has to paint it, no javascript needed. This is why you can disable javascript in your browser's dev tools, and server-side rendered pages will still appear flawlessly. It's a more expensive option for IKEA (and the owner of the webpage) since they need resources to assemble and deliver your request, but the user experience is better unless you really like these hotdogs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/oCjCwnuLpiWbfMb1UA/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/oCjCwnuLpiWbfMb1UA/giphy.gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;
Perfect User Experience



&lt;p&gt;  &lt;/p&gt;
&lt;h3&gt;
  
  
  Can you mix server side and client side rendering?
&lt;/h3&gt;

&lt;p&gt;Maybe IKEA delivers your bookshelf assembled but you realize the distance between shelves isn't just right. You will have to take out a screwdriver and make some adjustments, no big deal. If something similar happens on a webpage, like a browser conditionally rendering a modal based on preferences set in local storage: is it still server-side rendered? Or does it now count as client-side rendered?&lt;/p&gt;

&lt;p&gt;Kind of both, right?&lt;/p&gt;

&lt;p&gt;You can add client-side parts to server-side rendered pages. Maybe you clicked "Don't show me this dialog every time" and the page is adjusted on the client-side based on data stored in the browser. Or the page realizes an API call for targeted products based on your locally stored preferences.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/ZvkHeRqClNgUE/giphy-downsized-large.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/ZvkHeRqClNgUE/giphy-downsized-large.gif" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;
Some tiny adjustments



&lt;p&gt; &lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the error
&lt;/h2&gt;

&lt;p&gt;The error we see here is caused by the following: Some of our code is thought to be client-side rendered and assuming certain globals like window or document are available because the code is meant to be executed in the browser. This can be caused by our code or third-party libraries.&lt;/p&gt;

&lt;p&gt;But if the code is server-side rendered, it is not executed by the browser, it is executed by a node server and globals like window and document are not available: hence the error we see.&lt;/p&gt;
&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Use the useEffect hook or just ask for window
&lt;/h3&gt;

&lt;p&gt;That's the easiest solution: if you just need to access window to scroll somewhere or render some special component only on the client-side, you can do it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;TestComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I get executed in the browser and the client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSSR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isSSR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I am only being executed in the browser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;I&lt;/span&gt; &lt;span class="nx"&gt;am&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isSSR&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;only&lt;/span&gt; &lt;span class="nx"&gt;rendered&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="nx"&gt;side&lt;/span&gt;&lt;span class="o"&gt;!&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. @loadable/component in Gatsby
&lt;/h3&gt;

&lt;p&gt;The library &lt;a href="https://loadable-components.com/"&gt;@loadable/component&lt;/a&gt; allows you to dynamically import components to your project, so they don't get rendered on the server. The following component uses leaflet, a library similar to google maps that only supports client-side rendering:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MapContainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Marker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Popup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TileLayer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-leaflet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;leaflet&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;leaflet/dist/leaflet.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;31.41528&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;64.18156&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;L&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Icon&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;iconUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/marker-icon.png&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;map__container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MapContainer&lt;/span&gt;
          &lt;span class="nx"&gt;center&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;defaultPosition&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;zoom&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TileLayer&lt;/span&gt;
            &lt;span class="nx"&gt;attribution&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;amp;copy; &amp;lt;a href="http://osm.org/copyright"&amp;gt;OpenStreetMap&amp;lt;/a&amp;gt; contributors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
            &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Marker&lt;/span&gt; &lt;span class="nx"&gt;position&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;defaultPosition&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;myIcon&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Popup&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="nx"&gt;Entrá&lt;/span&gt; &lt;span class="nx"&gt;en&lt;/span&gt; &lt;span class="nx"&gt;la&lt;/span&gt; &lt;span class="nx"&gt;galería&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Estamos&lt;/span&gt; &lt;span class="nx"&gt;en&lt;/span&gt; &lt;span class="nx"&gt;frente&lt;/span&gt; &lt;span class="nx"&gt;de&lt;/span&gt; &lt;span class="nx"&gt;Nunatak&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Te&lt;/span&gt;
              &lt;span class="nx"&gt;esperamos&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Popup&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Marker&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/MapContainer&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To be able to use this component in Gatsby, I used @loadable/component like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;loadable&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@loadable/component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// import Map from './Map'; // uncomment this line to see the Gatsby build error&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;MapWrapper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;loadable&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Map&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// comment this line to see the Gatsby build error&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Map&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead, clone the &lt;a href="https://github.com/elisabeth-leonhardt/gatsby-csr-test"&gt;project&lt;/a&gt; and play around with the different types of imports. Then try running: &lt;code&gt;npm run build&lt;/code&gt; so you can see the result in your terminal.&lt;/p&gt;

&lt;p&gt;Gatsby also mentions some alternative solutions in the &lt;a href="https://www.gatsbyjs.com/docs/using-client-side-only-packages/"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Dynamic loading in Next.js
&lt;/h3&gt;

&lt;p&gt;Next.js has its own mechanism to dynamically import components on the client-side only: &lt;a href="https://nextjs.org/docs/advanced-features/dynamic-import"&gt;check out the docs&lt;/a&gt;. This is how I added the leaflet map in Next.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/dynamic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;styles&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../styles/Contacto.module.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;FindMe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../components/Map&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// replace '@components/map' with your component's location&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ssr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// This line is important. It's what prevents server-side render&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Map&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;lt;Map /&amp;gt;&lt;/code&gt; component is exactly the same as in the Gatsby project. To try this, just spin up a Next.js project and import the Map component together with its wrapper into a server-side generated page.&lt;/p&gt;

&lt;p&gt;As always, I hope this helped! Let me know what you think in the comments and have a great day!&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>nextjs</category>
      <category>webdev</category>
      <category>react</category>
    </item>
  </channel>
</rss>
