<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sebastien Castiel</title>
    <description>The latest articles on DEV Community by Sebastien Castiel (@scastiel).</description>
    <link>https://dev.to/scastiel</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%2F4578%2F91cc1974-1c5a-4f3a-b033-ca35d693b439.png</url>
      <title>DEV Community: Sebastien Castiel</title>
      <link>https://dev.to/scastiel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/scastiel"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Wed, 03 Sep 2025 17:28:24 +0000</pubDate>
      <link>https://dev.to/scastiel/-1jd8</link>
      <guid>https://dev.to/scastiel/-1jd8</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/scastiel/seven-hours-zero-internet-and-local-ai-coding-at-40000-feet-4ab0" class="crayons-story__hidden-navigation-link"&gt;Seven Hours, Zero Internet, and Local AI Coding at 40,000 Feet&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/scastiel" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F4578%2F91cc1974-1c5a-4f3a-b033-ca35d693b439.png" alt="scastiel profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/scastiel" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Sebastien Castiel
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Sebastien Castiel
                
              
              &lt;div id="story-author-preview-content-2817614" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/scastiel" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F4578%2F91cc1974-1c5a-4f3a-b033-ca35d693b439.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Sebastien Castiel&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/scastiel/seven-hours-zero-internet-and-local-ai-coding-at-40000-feet-4ab0" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 3 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/scastiel/seven-hours-zero-internet-and-local-ai-coding-at-40000-feet-4ab0" id="article-link-2817614"&gt;
          Seven Hours, Zero Internet, and Local AI Coding at 40,000 Feet
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/vibecoding"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;vibecoding&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/scastiel/seven-hours-zero-internet-and-local-ai-coding-at-40000-feet-4ab0" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;60&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/scastiel/seven-hours-zero-internet-and-local-ai-coding-at-40000-feet-4ab0#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              13&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>programming</category>
      <category>ai</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Seven Hours, Zero Internet, and Local AI Coding at 40,000 Feet</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Wed, 03 Sep 2025 17:23:45 +0000</pubDate>
      <link>https://dev.to/scastiel/seven-hours-zero-internet-and-local-ai-coding-at-40000-feet-4ab0</link>
      <guid>https://dev.to/scastiel/seven-hours-zero-internet-and-local-ai-coding-at-40000-feet-4ab0</guid>
      <description>&lt;p&gt;Seven hours on a plane, zero internet, and a curiosity about local AI coding. That's how I found myself downloading 13GB of AI models in an airport lounge, preparing for what would become an accidental experiment in offline development.&lt;/p&gt;

&lt;p&gt;This wasn't a research project or a formal comparison of existing solutions. I was heading back from vacation with a long flight ahead and wanted to avoid getting bored. The idle question nagging at me: what would it be like to have a coding assistant totally local, or at least that I can run on my own infrastructure, without paying for tokens or subscriptions? Plus, it sounded fun.&lt;/p&gt;

&lt;p&gt;So I decided to find out where we are on this path.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Threw Together Before Takeoff
&lt;/h2&gt;

&lt;p&gt;I started from &lt;a href="https://github.com/scastiel/nexttemplate" rel="noopener noreferrer"&gt;a basic Next.js project&lt;/a&gt; with the dependencies I like: TailwindCSS, Zod, ts-pattern, etc. My goal was to use a stack I already know very well, so I wouldn't be fighting unfamiliar tools while experimenting with local AI.&lt;/p&gt;

&lt;p&gt;Then, with the quest for a local LLM, came the real trial and error. I tried &lt;a href="https://mistral.ai/article/announcing-devstral-ai" rel="noopener noreferrer"&gt;Devstral&lt;/a&gt; from Mistral, &lt;a href="https://ollama.com/library/qwen3" rel="noopener noreferrer"&gt;Qwen3&lt;/a&gt;, with &lt;a href="https://github.com/charmbracelet/crush" rel="noopener noreferrer"&gt;Crush&lt;/a&gt;, &lt;a href="https://opencode.ai/" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt; and &lt;a href="https://github.com/tcsenpai/ollama-code" rel="noopener noreferrer"&gt;Ollama-code&lt;/a&gt;. After several failed attempts, &lt;a href="https://openai.com/index/introducing-gpt-oss/" rel="noopener noreferrer"&gt;gpt-oss&lt;/a&gt; was actually the only one I was able to make work, and only with OpenCode&lt;sup id="fnref1"&gt;1&lt;/sup&gt;. It's not that I did extensive research to compare solutions – this was purely what I could get functioning in the time I had.&lt;/p&gt;

&lt;p&gt;So I set up Ollama with gpt-oss downloaded – all 13GB of it. My machine is a MacBook Pro M4 Pro with 24GB of RAM, which I should mention upfront because the hardware requirements turn out to matter quite a bit.&lt;/p&gt;

&lt;p&gt;For the project itself, I chose something deliberately simple: an app to track subscriptions to recurring services like Netflix or your gym. Basic CRUD operations, nothing fancy. I decided to make the app rely on the browser's local storage to persist data, instructing the assistant to create an abstraction on top of it so I could migrate to a proper API or Supabase later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Seven Hours at 40,000 Feet
&lt;/h2&gt;

&lt;p&gt;For my experiment, I used the approach I described in &lt;a href="https://betweentheprompts.com/40000-feet/" rel="noopener noreferrer"&gt;Turning Claude Code Into My Best Design Partner&lt;/a&gt;: first I ask the assistant to plan the feature, following up with changes if necessary. When the plan looks good, I ask to put it in a Markdown file in a plans folder. Then I ask it to start implementation using the plan and update the plan document with progress, making it a living document.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;I was pleasantly surprised that the &lt;strong&gt;planning&lt;/strong&gt; was more than okay, at least for my basic use case. Maybe it would be more complicated for a more complex feature, but here I accepted the suggested plans almost at first try!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;speed&lt;/strong&gt; reality hit quickly: the local setup was about 4-5x slower than Claude Code&lt;sup id="fnref2"&gt;2&lt;/sup&gt;, so you have to be patient. Good thing planes have movies to watch between the prompts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;strong&gt;energy consumption&lt;/strong&gt; was the real surprise. Not only were my thighs quite burning from the laptop heat, but the power offered by the plane – with its shitty plug – wasn't able to compensate for the energy that the local model needed. I had to take breaks at some points to let my laptop's battery charge. Forget about battery-powered coding sessions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Finally, the &lt;strong&gt;code quality&lt;/strong&gt; was usually 95% correct, but fixing the remaining 5% was tedious. Various issues kept cropping up: ESLint problems, using &lt;code&gt;any&lt;/code&gt; instead of proper types, or some UI elements that got removed from one iteration to another, like buttons that would just disappear. In a professional project, I would have fixed these issues myself for speed reasons. Here, I wanted to go all in with the AI-assisted approach, but the AI was perfectly capable of fixing the problems – it just took a lot of time and iterations.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&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%2Fhrzde5um5d5vdh68y7di.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%2Fhrzde5um5d5vdh68y7di.png" alt="Subscriptions list in the resulting app" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;👆 The subscriptions list in the resulting app. Not an app I would use everyday, but it works :)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;During my 7-hour flight, I worked on the app for about 3-4 hours (the rest being meals, naps and movies). I was able to build a basic app with a form to add a subscription, a subscription list showing the total for the current month plus expired and future subscriptions, an edit button, and a way to delete subscriptions. The UI was very basic and not very UX-friendly, but it worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Reality Check
&lt;/h2&gt;

&lt;p&gt;When I started using OpenCode with gpt-oss, I was pleasantly surprised to see that it just worked. Not perfect, but pretty good. And all on my machine only! No need to check my Claude quotas or API keys usage. There was something liberating about that complete independence.&lt;/p&gt;

&lt;p&gt;The latency that a local model implies, plus the energy consumption, make it hard to imagine going fully local for now. The hardware requirements are real – this isn't running smoothly on your average developer laptop. But let's hope performance and efficiency will improve over time.&lt;/p&gt;

&lt;p&gt;Finally, I would have expected to have more documented options at my disposal for local AI coding. I was able to find some blog posts, some outdated, but official documentation was lacking with OpenCode&lt;sup id="fnref3"&gt;3&lt;/sup&gt;, Crush&lt;sup id="fnref4"&gt;4&lt;/sup&gt;, and similar tools.&lt;/p&gt;

&lt;p&gt;The next thing I'd like to try is using OpenAI's GPT-OSS with OpenCode, but not locally: either from OpenAI's API, or from a server with more power than my laptop.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Actually Means
&lt;/h2&gt;

&lt;p&gt;Looking at this experiment, the most important takeaway for practitioners is a mix between "local AI coding is surprisingly viable today" and "it's still too early but getting close."&lt;/p&gt;

&lt;p&gt;It's really about having the option to go local, plus the hope that someday the models will be more efficient and the hardware more AI-optimized, so that everything can run locally and smoothly without consuming too much energy or destroying batteries.&lt;/p&gt;

&lt;p&gt;The current state is encouraging for simple projects but still feels early for serious professional work. The trade-offs are real: you need serious hardware, you'll burn through battery life, and you'll move slower than with cloud-based tools. But the foundation is there, and it's more solid than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Back to Reality
&lt;/h2&gt;

&lt;p&gt;Landing, turning WiFi back on, returning to Claude Code – the speed difference was immediately apparent. But something had shifted during those seven hours offline. Local AI coding moved from "maybe someday" to "definitely possible, with caveats."&lt;/p&gt;

&lt;p&gt;This vacation experiment accidentally revealed where we are on the path to truly local development assistance. We're not there yet, but we can see it from here. And sometimes, that's the most interesting place to be.&lt;/p&gt;




&lt;p&gt;This post was first published on the blog &lt;a href="https://betweentheprompts.com" rel="noopener noreferrer"&gt;Between the Prompts&lt;/a&gt;. Check it out and subscribe to the newsletter to be the first reading the new posts 😉.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;I used &lt;a href="https://apidog.com/blog/gpt-oss-locally/" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; to set up OpenCode with gpt-oss using Ollama. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Only my personal feeling, it's hard to get a precise benchmark. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;See this &lt;a href="https://opencode.ai/docs/providers/#ollama" rel="noopener noreferrer"&gt;short section about setting up OpenCode with Ollama&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;This &lt;a href="https://github.com/charmbracelet/crush?tab=readme-ov-file#local-models" rel="noopener noreferrer"&gt;section in Crush's documentation&lt;/a&gt; explains how to use it with Ollama, but all models I've tried using generated errors related to tool-calling… ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>vibecoding</category>
    </item>
    <item>
      <title>Turning Claude Code Into My Best Design Partner</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Wed, 27 Aug 2025 13:56:13 +0000</pubDate>
      <link>https://dev.to/scastiel/turning-claude-code-into-my-best-design-partner-59a3</link>
      <guid>https://dev.to/scastiel/turning-claude-code-into-my-best-design-partner-59a3</guid>
      <description>&lt;p&gt;When I first started using Claude Code, I had a naive approach to working with it. I would describe the task directly in the prompt, press Enter, and cross my fingers. If the agent made mistakes, I would tell it how to fix them. For small tasks, this can be good enough, but as the task grows in complexity, this approach reveals several significant drawbacks.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Simple Doesn't Scale
&lt;/h2&gt;

&lt;p&gt;The first problem is that the conversation becomes the only source of truth about the task. This means a new message can override instructions from an old one, but it isn't always clear when this happens, which can cause mistakes by the agent.&lt;/p&gt;

&lt;p&gt;Additionally, the context size for the agent is limited. The more the conversation grows, the more information from the beginning can be "forgotten," even though Claude Code has a capacity for "compacting" the conversation that's supposed to improve this issue.&lt;/p&gt;

&lt;p&gt;For these reasons, I started experimenting with an approach I'd heard about: asking Claude Code to start by writing a plan document. This document becomes the source of truth instead of a sprawling conversation. When I find the plan document good enough, I've taken the habit of clearing the conversation to start fresh with just the plan as context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the Initial Plan
&lt;/h2&gt;

&lt;p&gt;My first prompt is usually to give Claude Code a description of the feature it should implement (or bug it should fix, or the refactoring it should do) with all the details I have in mind. If I already have an idea of the implementation, I can give it some pointers to existing files for reference. However, I try not to give it too many implementation instructions because I want it to make suggestions and contribute to the design process.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I want to implement a query builder. The page will be displayed as two columns. In the first one, a first box will let the user select a view (for now only one view: "Volume Metrics"), a second box to select fields ("field 1", "field 2", "field 3"), and a third one to add filters (don't fill it yet). The right column will display first the query as human readable, then a table with the query results. (to be continued)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I also try to refer to existing plans for features implemented previously. I don't have a formal template, but I don't really need one as long as I have other plans to reference as examples.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(continuing) Check out the previous plan in @plans/chat-playground.md to know about routing and architecture details. (to be continued)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I expect to see several key elements in the document. First, a rephrasing of the feature description I gave it, which helps ensure we're aligned on requirements. Second, details on how the feature will be implemented—usually it includes some pieces of code or pseudo-code without me having to tell it to do so. Finally, commands to run to make sure the code quality is acceptable, including type checking, linting, and tests.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(continuing) Write a plan in @plans/query-builder.md, and let me validate it before starting the implementation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Collaborative Design Process
&lt;/h2&gt;

&lt;p&gt;Sometimes, I'm not satisfied with the suggested implementation. In this case, instead of updating the plan, I tell it why it's wrong, expecting it to change its approach.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The page should be a subroute of /explore, not /review. Also, it should be accessible only to users with the "admin" role.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It also happens that after a few back-and-forth exchanges, I realize the first suggested approach was better than the one I had in mind. This process is much more efficient than if I had started writing the code by myself and realized my approach was wrong later.&lt;/p&gt;

&lt;p&gt;It's a bit like discussing the plan with a colleague each time I'm about to start a new feature. More specifically, it's like challenging my implementation plan with a junior colleague (or one who doesn't know the codebase as well) who will question my choices. The dynamic reminds me of the rubber duck debugging technique, where explaining your approach helps you think through problems. However, it won't suggest a radically different approach unless I specifically ask it to, which I have never tried.&lt;/p&gt;

&lt;p&gt;But the plan document isn't just a blueprint for the implementation. I discovered that Claude Code is much more efficient when it considers it as a living document.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Living Document Approach
&lt;/h2&gt;

&lt;p&gt;The key insight is that I don't just ask Claude Code to write the plan—I also ask it to make it a living document while implementing the feature. I explicitly ask it to update the plan during implementation because the implementation process, and especially the type checking, linting, or test processes, can reveal that some parts of the original plan were incorrect.&lt;/p&gt;

&lt;p&gt;I've developed the habit of asking it to check that the plan is up to date each time it commits code, treating plan updates the same way as running quality checks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make sure the plan is up to date, and commit changes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This living document approach solves a fundamental problem with AI development: context limits. With an up-to-date plan document, I can start a fresh conversation and simply ask Claude Code to continue the implementation. This usually works great—just the document is usually enough context for a new session to pick up exactly where the previous one left off.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Continue the implementation documented in @plans/query-builder.md.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  My Review Process
&lt;/h2&gt;

&lt;p&gt;When the implementation starts, I review changes along the way to ensure everything is progressing correctly. However, if I'm satisfied with the progress, I can let it continue without checking as often. When reviewing the final code, the updated plan document gives me helpful hints about the technical choices that were made during implementation.&lt;/p&gt;

&lt;p&gt;Surprisingly, I think that the fact that I need to plan my features carefully before rushing into implementation is making me a better developer overall. This happens simply because it forces me to document the implementation and think it through before jumping into code. I also find myself explaining my reasoning more clearly because I have to write it down for the AI, whereas with colleagues I would typically discuss things in person or via video call.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Chaos to System
&lt;/h2&gt;

&lt;p&gt;This workflow systematically addresses the fundamental problems I encountered with the naive approach: it creates a clear source of truth, eliminates context limit issues, and forces better architectural thinking. The living document becomes both the specification and the implementation log, creating a complete record of not just what was built, but why and how it was built.&lt;/p&gt;

&lt;p&gt;The result is a development process that's more thoughtful, more documented, and more reliable.&lt;/p&gt;

&lt;p&gt;AI isn't just serving as implementer.&lt;/p&gt;

&lt;p&gt;It becomes a collaborative design partner.&lt;/p&gt;




&lt;p&gt;This post was first published on the blog &lt;a href="https://betweentheprompts.com/?ref=dev.to"&gt;Between the Prompts&lt;/a&gt;. Check it out and subscribe to the newsletter to be the first reading the new posts 😉.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>vibecoding</category>
      <category>claudecode</category>
    </item>
    <item>
      <title>13 tips for better Pull Requests and Code Review</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Tue, 17 Oct 2023 15:42:40 +0000</pubDate>
      <link>https://dev.to/scastiel/13-tips-for-better-pull-requests-and-code-review-43oj</link>
      <guid>https://dev.to/scastiel/13-tips-for-better-pull-requests-and-code-review-43oj</guid>
      <description>&lt;p&gt;In many years of writing and reviewing code, I learned a few secrets to craft better pull requests and review code more efficiently.&lt;/p&gt;

&lt;p&gt;I poured all these secrets into my new book &lt;a href="https://scs.tl/book-pr" rel="noopener noreferrer"&gt;Pull Requests and Code Review&lt;/a&gt;, but you’ll find here a preview with these 13 tips, that you can already use in your developer activity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can you think of more tips? Share them in comments 😉&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Create your PR before the code is ready for review
&lt;/h2&gt;

&lt;p&gt;A PR draft helps you organize your ideas and document your progress, while you’re still working on your feature.&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%2Fu5oj0osgi7j8bcg1n1lq.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%2Fu5oj0osgi7j8bcg1n1lq.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Make people want to review your PR
&lt;/h2&gt;

&lt;p&gt;The best way to get a quick and efficient review is to keep your PR small and well documented (with all the necessary context). You’ll also increase you chances for future PRs by delivering great code now!&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%2Fqx6qhp46kovom6c5yuqy.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%2Fqx6qhp46kovom6c5yuqy.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;💡 Find all these tips with and more, with more real-life examples and actionable insights in my free book &lt;a href="https://scs.tl/book-pr" rel="noopener noreferrer"&gt;Pull Requests and Code Review: Strategies, Techniques, and Best Practices for Collaboration&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Be your PR’s first reviewer
&lt;/h2&gt;

&lt;p&gt;Put yourself in the shoes of your reviewer, anticipate questions and use comment your own code when you think it can help them.&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%2F5k0jrb5ca9vl4pwbkhov.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%2F5k0jrb5ca9vl4pwbkhov.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Assign the right reviewers to your PR
&lt;/h2&gt;

&lt;p&gt;Don’t assign your PR to the entire world. Choose your reviewers carefully to get a relevant review, without waiting too long for approval.&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%2F5s7xlypm3qirclreqve9.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%2F5s7xlypm3qirclreqve9.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Be responsive to comments
&lt;/h2&gt;

&lt;p&gt;Be open to feedback, ask for clarification, say when you disagree (with respect), and always respond to comments.&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%2F5qan1jhryuj5hjvnpczj.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%2F5qan1jhryuj5hjvnpczj.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  6. If you want people to review your PRs, you have to review theirs
&lt;/h2&gt;

&lt;p&gt;Everybody has many PRs to review and little free time to. If you review other PRs, you increase the chances of getting yours reviewed too.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. You can review code even if you are a junior developer
&lt;/h2&gt;

&lt;p&gt;As a junior developer, you can let others know when you don’t understand part of the code, as it should be understandable by any developer in the team.&lt;/p&gt;

&lt;p&gt;More about it in my post &lt;a href="https://scastiel.dev/code-review-junior-developer" rel="noopener noreferrer"&gt;How to give code review as a junior developer?&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%2F7heqvp4dwuc1vozhx5tc.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%2F7heqvp4dwuc1vozhx5tc.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Check the right things during code review
&lt;/h2&gt;

&lt;p&gt;Code review’s goal is to check for bugs and edge cases, and challenge the implementation. It should neither be used to nitpick about minor formatting or styling preferences, nor for large-scale architectural discussions.&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%2Fsokwg94us6insi8466nx.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%2Fsokwg94us6insi8466nx.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Use the right tone in your comments
&lt;/h2&gt;

&lt;p&gt;Use “why not” instead of “you should”, be open and positive and always suggest an alternative when you ask for a change.&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%2Fpwd2s6xl8kwmje7hqojk.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%2Fpwd2s6xl8kwmje7hqojk.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Be clear about whether a change is required for you to approve the PR or not
&lt;/h2&gt;

&lt;p&gt;Not all comments require a change, and not all asked changes are required for the PR to be approved. Be clear in your comment if the change isn’t urgent.&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%2Fywktvmbinkz5cqbtig6i.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%2Fywktvmbinkz5cqbtig6i.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Review your review before submitting it
&lt;/h2&gt;

&lt;p&gt;Before making your review public, re-read each comment: check for the tone you use and ensure you provide all the context to help the PR submitter.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Approve the PR when the submitter made all the changes you asked
&lt;/h2&gt;

&lt;p&gt;You don’t want the reviewer to wait for your approval two days after they made all the changes you asked. When you review it, assume you’ll approve it as soon as all the fixes are made.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. Some conflicts can’t be solved in comments
&lt;/h2&gt;

&lt;p&gt;When a comment thread becomes a debate in your PR, you’d better cut it off and suggest to continue the discussion elsewhere, e.g. in a Slack thread. If necessary, dedicate a meeting to it, and/or involve a third-party.&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%2F6ixt6am853fc7c10dacz.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%2F6ixt6am853fc7c10dacz.jpeg" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;That’s it! What did you think of these tips? Can you think of one tip you’d like to give other developers?&lt;/p&gt;

&lt;p&gt;Share them here in comments 👇&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you liked these tips and want to learn some more, check out my book &lt;a href="https://scs.tl/book-pr" rel="noopener noreferrer"&gt;Pull Requests and Code Review&lt;/a&gt;, it’s free!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>beginners</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>The simplest example to understand Server Actions in Next.js</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Tue, 08 Aug 2023 19:26:12 +0000</pubDate>
      <link>https://dev.to/scastiel/the-simplest-example-to-understand-server-actions-in-nextjs-5533</link>
      <guid>https://dev.to/scastiel/the-simplest-example-to-understand-server-actions-in-nextjs-5533</guid>
      <description>&lt;p&gt;Server Actions are a new feature in Next.js. The first time I heard about them, they didn’t seem very intuitive to me. Now that I’m a bit more used to them, let me contribute to making them easier to understand.&lt;/p&gt;

&lt;p&gt;I tried to create the smallest possible example to help developers understand what they are and what they can be used for. The result is this example, which consists of two files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// client-component.tsx&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&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;useEffect&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="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&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;getInformationFromTheServer&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;./actions&lt;/span&gt;&lt;span class="dl"&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;ClientComponent&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;info&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setInfo&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="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;getInformationFromTheServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;World&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="nf"&gt;setInfo&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="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;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      This comes from the server: &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;code&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;info&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;code&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="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 typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// actions.ts&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&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;getInformationFromTheServer&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="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="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`Hello &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="s2"&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 use case is as follows: In a client component, we’d like to get some information from the server. Without server actions, we’d have to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Either get this information as a prop passed by the parent component (assuming it is a server component),&lt;/li&gt;
&lt;li&gt;Or create an API route and call it from the client component using &lt;code&gt;fetch&lt;/code&gt;. Server actions are similar to the second option, but with a shortcut: the API route will be created by Next.js for us.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In our example, the server action is the function &lt;code&gt;getInformationFromTheServer&lt;/code&gt;, declared in &lt;em&gt;actions.ts&lt;/em&gt; (server actions cannot be defined in the same file as a client component). Like any server action:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is declared as &lt;code&gt;async&lt;/code&gt; (it has to, even if it doesn’t perform any async operation),&lt;/li&gt;
&lt;li&gt;It uses the directive &lt;code&gt;'use server'&lt;/code&gt; (either at the top of the file or the top of the function).&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;💡 Tiny interruption in this post: if you’d like to learn how to use React and Next.js to create powerful applications, check out my &lt;a href="https://learn.scastiel.dev" rel="noopener noreferrer"&gt;online course&lt;/a&gt; or my &lt;a href="https://scastiel.dev/react-workshop" rel="noopener noreferrer"&gt;live workshop&lt;/a&gt; 😉.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;From the client component, we can call the function like any other side effect (async) function: in a &lt;code&gt;useEffect&lt;/code&gt; or an event callback.&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%2Fhdsvxn8ix9mcbuw9t7zb.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%2Fhdsvxn8ix9mcbuw9t7zb.png" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It looks like we’re just calling a function, so you might wonder why everybody is making such a fuss about server actions… Look closer: &lt;strong&gt;from a client component, we call a function stored on the server&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This means that it isn’t &lt;em&gt;just&lt;/em&gt; a function call: there is actually an HTTP request made to the API to call the function. You can see it in the Network tab of your DevTools:&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%2Fk2lyqz3ybfckcgqc8ft1.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%2Fk2lyqz3ybfckcgqc8ft1.png" width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The endpoint is at the same path as your current page but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uses the &lt;code&gt;POST&lt;/code&gt; method,&lt;/li&gt;
&lt;li&gt;Needs some headers, such as the &lt;code&gt;Next-Action&lt;/code&gt; one, containing some kind of ID for the server action,&lt;/li&gt;
&lt;li&gt;Gets the parameters to pass to the function from the request body (as JSON).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you copy the server action ID from the headers, you can even call the API endpoint using cURL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ curl 'http://localhost:3001/getting-info-from-server' \
  -H 'Content-Type: text/plain;charset=UTF-8' \
  -H 'Next-Action: da9f55acc16563503a57a4fdfe567f8770898818' \
  -X POST --data-raw '["World"]'
0:["$@1",["development",null]]
1:"Hello World!"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: In Chrome DevTools, at the time I’m writing this post, it looks like it isn’t possible to see the result of the request, as it is sent using HTTP streaming. It works well with cURL 😉.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As you can see, there is nothing magical about server actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js takes our &lt;code&gt;getInformationFromTheServer&lt;/code&gt; function,&lt;/li&gt;
&lt;li&gt;It makes it callable through a specific API endpoint,&lt;/li&gt;
&lt;li&gt;From the client component, when we think we are calling the function we defined, we actually call a function generated by Next.js which deals with the necessary API call for us.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;If you are familiar with distributed computing patterns, you can notice that server actions offer a &lt;a href="https://en.wikipedia.org/wiki/Remote_procedure_call" rel="noopener noreferrer"&gt;remote procedure call (RPC)&lt;/a&gt; pattern for Next.js applications.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The cURL example shows something very important about server actions: anyone can call existing server actions with any parameter they want! This means that &lt;strong&gt;the parameters received by the server actions must be validated&lt;/strong&gt;, just like any parameter sent to a classic API route.&lt;/p&gt;

&lt;p&gt;This is especially counter-intuitive when using TypeScript. Even if you declare that your function &lt;em&gt;must&lt;/em&gt; get a &lt;code&gt;string&lt;/code&gt; parameter, this function will be made available to the rest of the world, even to big bad hackers who want to send a number instead.&lt;/p&gt;

&lt;p&gt;With that in mind, server actions still offer a nice pattern to get information from the server, especially in a client component. From there, we can imagine cool and more complex use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mutating data on the server (e.g. in a database),&lt;/li&gt;
&lt;li&gt;Sending an email,&lt;/li&gt;
&lt;li&gt;Processing data entered in a form, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And, like in any other API endpoint, we can, of course, check user authentication to ensure we don’t allow anything for anyone.&lt;/p&gt;




&lt;p&gt;I hope that server actions are now a bit clearer to you!&lt;/p&gt;

&lt;p&gt;When you understand how they work, you can find many resources about how to use them in real-life applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions" rel="noopener noreferrer"&gt;In the Next.js documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://scastiel.dev/server-components-actions-react-nextjs" rel="noopener noreferrer"&gt;On my blog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vercel.com/blog/understanding-react-server-components#server-actions-react%E2%80%99s-first-steps-into-mutability" rel="noopener noreferrer"&gt;In a section of this article on Vercel’s blog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Build a Guestbook from the 2000s with React Server Components and Server Actions</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Wed, 05 Jul 2023 12:51:05 +0000</pubDate>
      <link>https://dev.to/scastiel/intro-to-react-server-components-and-actions-with-nextjs-j1j</link>
      <guid>https://dev.to/scastiel/intro-to-react-server-components-and-actions-with-nextjs-j1j</guid>
      <description>&lt;p&gt;React is living something these days. Although it was created as a client UI library, it can now be used to generate almost everything from the server. And we get a lot from this change, especially when coupled with Next.js.&lt;/p&gt;

&lt;p&gt;In this short tutorial, we’ll use &lt;strong&gt;React Server Components&lt;/strong&gt; and &lt;strong&gt;Server Actions&lt;/strong&gt; to build something from the early '00s: a guestbook!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I bootstrapped a Next.js app with TypeScript and the app router. As I don’t want to care about the CSS, I used &lt;a href="https://watercss.kognise.dev/" rel="noopener noreferrer"&gt;Water.css&lt;/a&gt;. And to store the messages, I went with &lt;a href="https://vercel.com/blog/vercel-storage#vercel-kv-a-durable-redis-database" rel="noopener noreferrer"&gt;Vercel KV&lt;/a&gt; (which may not be the right tool for this kind of use case, but is plenty enough for the purpose of this post). The complete source code of the demo project is &lt;a href="https://github.com/scastiel/server-components-actions-demo" rel="noopener noreferrer"&gt;available on GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fetching the messages from a Server Component
&lt;/h2&gt;

&lt;p&gt;Let’s start with displaying the existing messages. With a classic React component, we’d have to declare a local state, perform a &lt;code&gt;fetch&lt;/code&gt; request in a &lt;code&gt;useEffect&lt;/code&gt;, etc. With Next.js’ server-side rendering, we could declare a &lt;code&gt;getServerSideProps&lt;/code&gt; function to fetch the messages first.&lt;/p&gt;

&lt;p&gt;With server components, we can fetch the messages from the component itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="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;MessageList&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;messages&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;fetchMessages&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;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Last Messages&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&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;messages&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;id&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="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;date&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;blockquote&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;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="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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;content&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`–&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="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;formatDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&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;small&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;blockquote&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;/&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;Looks odd to you? It should! The component is declared &lt;code&gt;async&lt;/code&gt;, and fetches data from its body, without any &lt;code&gt;useEffect&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;This is what server components let us do. As a server component can be rendered only on the server, we can perform asynchronous operations such as querying a database, making HTTP requests, or even read from the filesystem.&lt;/p&gt;

&lt;p&gt;As a consequence, notice how the component is easy to read, even by someone who’s not as familiar with React as you are. We fetch the data, and we display it. That’s it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if fetching the messages takes a few seconds?&lt;/strong&gt; Does it mean that the page will take that much time to load? Fortunately, no. We can take advantage of React’s &lt;code&gt;Suspense&lt;/code&gt; to display a nice loading message while the component is still fetching the data.&lt;/p&gt;

&lt;p&gt;In the page displaying messages, we can wrap our &lt;code&gt;MessageList&lt;/code&gt; component with &lt;code&gt;Suspense&lt;/code&gt;, with a fallback message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="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="nf"&gt;MessagesPage&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;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&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="nc"&gt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback&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;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading messages…&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="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MessageList&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;Suspense&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;For a deeper dive in Server Components and Suspense, have a look at my previous post &lt;a href="https://scastiel.dev/view-counter-react-server-components" rel="noopener noreferrer"&gt;Display a view counter on your blog with React Server Components&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So this is how we can read data from a server component, but is there something similar for writing data so we can post new messages as easily?&lt;/p&gt;

&lt;h2&gt;
  
  
  Submitting forms with Server Actions
&lt;/h2&gt;

&lt;p&gt;Server components let us fetch data without having to set up a flow with &lt;code&gt;useEffect&lt;/code&gt;. We can do something similar to submit data from a form, thanks to Server Actions.&lt;/p&gt;

&lt;p&gt;Let’s start with this basic form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MessageForm&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;form&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;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;New Message&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&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;"name"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Your name:&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;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&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;"name"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&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;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;"content"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Your message:&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;textarea&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;"content"&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;"content"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;minLength&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;3&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;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="p"&gt;&amp;gt;&lt;/span&gt;Send&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;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;As you can see, there is nothing fancy here: two fields, a button, and some validation attributes.&lt;/p&gt;

&lt;p&gt;To submit this form, we usually have two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;use the &lt;code&gt;action&lt;/code&gt; attribute to use an API endpoint handling the form data,&lt;/li&gt;
&lt;li&gt;manually make a &lt;code&gt;fetch&lt;/code&gt; request to call this API endpoint.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the first scenario, the page would reload when the user submits the form, which is something we may want to avoid. The second scenario is the one we tend to prefer in React apps, but is a bit more complex to set up.&lt;/p&gt;

&lt;p&gt;With server actions, we can get the best of both worlds. We can create a function that will be called &lt;strong&gt;on the server&lt;/strong&gt; when the form is submitted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;submitMessageForm&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="nx"&gt;FormData&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="s1"&gt;use server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;content&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="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="s1"&gt;content&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="nx"&gt;formData&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="s1"&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By adding &lt;code&gt;'use server'&lt;/code&gt; in the function body, we declare it as a &lt;strong&gt;server action&lt;/strong&gt;. We can then associate it with the form by using the &lt;code&gt;action&lt;/code&gt; attribute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="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;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;submitMessageForm&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;Note: to use server actions in a Next.js project, you need to enable them in your &lt;em&gt;next.config.js&lt;/em&gt; file:&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="nx"&gt;experimental&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;serverActions&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What about form data validation? We declared some validation attributes on the form itself, but as always it doesn’t prevent us of validating the data on the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Validation example using a Zod schema&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formDataSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;content&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="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="s1"&gt;content&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="nx"&gt;formData&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="s1"&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;postMessage&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can make another nice improvement to our server action. By adding a call to Next.js’ &lt;code&gt;revalidatePath&lt;/code&gt; function, we can tell the app that some resources need to be reloaded. Said differently, we can &lt;strong&gt;refresh the messages&lt;/strong&gt; displayed on the page after we created the new one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;postMessage&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="nf"&gt;revalidatePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// assuming the messages are displayed at root&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what our form looks like now:&lt;/p&gt;


  


&lt;p&gt;We can see that the message is added to the list without needing to refresh the page. But the form is not reset after being submitted, which can be a little annoying.&lt;/p&gt;

&lt;p&gt;Additionally, you probably noticed that the form validation is quite minimalist: we use the HTML validation attributes for the client validation, and a Zod schema for the server one. How nice would it be to use the same validation on both sides?&lt;/p&gt;

&lt;p&gt;Good news: it is not because we use server actions that we can’t use cool client features to handle forms. Let’s see how.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improving the form with client features
&lt;/h2&gt;

&lt;p&gt;So far, our form doesn’t use any client feature. Actually, it is still a server component (that can be used with JavaScript disabled). To add some cool features to it, let’s start by making it a client component.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adding the &lt;code&gt;'use client'&lt;/code&gt; directive at the top of the file,&lt;/li&gt;
&lt;li&gt;moving the server action to a new &lt;em&gt;actions.ts&lt;/em&gt; file, as we can’t declare server actions in client files.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To improve our form, I will use the &lt;a href="https://react-hook-form.com/" rel="noopener noreferrer"&gt;react-hook-form&lt;/a&gt; library. Here is how to use it on our form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MessageForm&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;register&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MessageFormData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;shouldUseNativeValidation&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="na"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;zodResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formDataSchema&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;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="nf"&gt;handleSubmit&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;submitMessageForm&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="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;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;New Message&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&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;"name"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Your name:&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;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&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;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;"content"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Your message:&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;textarea&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;"content"&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content&lt;/span&gt;&lt;span class="dl"&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;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="p"&gt;&amp;gt;&lt;/span&gt;Send&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;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;By using &lt;code&gt;register&lt;/code&gt; on each field of the form, we let the library handle the form local state and validation. Note that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we use the existing Zod schema to define the validation rules (thanks to the &lt;a href="https://github.com/react-hook-form/resolvers#Zod" rel="noopener noreferrer"&gt;Zod resolver&lt;/a&gt; for &lt;em&gt;react-hook-form&lt;/em&gt;),&lt;/li&gt;
&lt;li&gt;by setting &lt;code&gt;shouldUseNativeValidation: true&lt;/code&gt;, we rely on the browser API to display the validation errors in the form (a feature I love!).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We need to change a bit the server action to comply with the new way data is sent by react-hook-form (a plain object instead of a &lt;code&gt;FormData&lt;/code&gt; object):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="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;submitMessageForm&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="nx"&gt;MessageFormData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;formDataSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&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="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;postMessage&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="nf"&gt;revalidatePath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important note:&lt;/strong&gt; we may think that because we call the function directly from the form, we don’t have to care about validating the form data (the parameter). &lt;strong&gt;We do need to care about it!&lt;/strong&gt; Although Next.js handles it for us, there is still an HTTP request made to an API endpoint to call the server action. And it is very easy to make this call manually with invalid data.&lt;/p&gt;

&lt;p&gt;This is why we still check that the data is conform with the Zod schema at the top of the function, even if TypeScript lets us think the data is already a &lt;code&gt;MessageFormData&lt;/code&gt;. But at least, we can use the same validation logic both on the client and the server.&lt;/p&gt;

&lt;p&gt;So now we handle the validation a bit better. We can still even improve the form.&lt;/p&gt;

&lt;p&gt;First, let’s reset the form after it is submitted. &lt;em&gt;react-hook-form&lt;/em&gt; provides a &lt;code&gt;reset&lt;/code&gt; function that we can call right after calling the server action (which returns a promise):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MessageForm&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="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="nx"&gt;reset&lt;/span&gt;&lt;span class="p"&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;useForm&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MessageFormData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&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;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="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;await&lt;/span&gt; &lt;span class="nf"&gt;submitMessageForm&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;reset&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;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final improvement: if the submission takes a couple of seconds, it is a good practice to tell the user the form is being submitted, and prevent them from clicking the submit button again. And again, &lt;em&gt;react-hook-form&lt;/em&gt; provides the information we need: an &lt;code&gt;isSubmitting&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MessageForm&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="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="na"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isSubmitting&lt;/span&gt; &lt;span class="p"&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;useForm&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MessageFormData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&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;{&lt;/span&gt;&lt;span class="cm"&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;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;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isSubmitting&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;isSubmitting&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sending…&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="s1"&gt;Send&lt;/span&gt;&lt;span class="dl"&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;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&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;Now, thanks to client features, we handle validation the same way on the client and the server, we tell the user the form is being submitted, and reset the form when it is done. Tiny improvements but great for the user experience, as well as the developer experience.&lt;/p&gt;

&lt;p&gt;Here is how our final guestbook looks like:&lt;/p&gt;


  


&lt;p&gt;To be fair, these last improvements we made come with a cost: it isn’t possible anymore to use the form without having JavaScript enabled. I’ll let you decide whether it is worth it or not.&lt;/p&gt;




&lt;p&gt;So, in the end, what do Server Actions offer us, and how do they compare to Server Components?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Server Components&lt;/strong&gt; let us &lt;strong&gt;fetch data&lt;/strong&gt; without creating an additional endpoint and calling it from the client,&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server Actions&lt;/strong&gt; let us &lt;strong&gt;post data&lt;/strong&gt; without creating an additional endpoint and calling it from the client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I love about server component and actions is that they hide some logic, without making anything &lt;em&gt;magic&lt;/em&gt;, and make the code way easier to understand. To me, they even make it easier to learn React, a point of view I developed in my post &lt;a href="https://scastiel.dev/better-learning-path-react" rel="noopener noreferrer"&gt;A better learning path for React with server components&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s a bit early to say they will change the way we build web apps on the long term, but I would take that bet!&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to Give Code Review as a Junior Developer?</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Sat, 24 Jun 2023 17:29:06 +0000</pubDate>
      <link>https://dev.to/scastiel/how-to-give-code-review-as-a-junior-developer-31gb</link>
      <guid>https://dev.to/scastiel/how-to-give-code-review-as-a-junior-developer-31gb</guid>
      <description>&lt;p&gt;“The code must be reviewed by senior developers.” “A review by a junior is nice, but their approval is worth nothing.” If you’ve never heard these phrases, you’re lucky. Of course, they’re completely wrong.&lt;/p&gt;

&lt;p&gt;As a junior developer, participating in code reviews offers a valuable learning opportunity and a chance to contribute to the team’s success.&lt;/p&gt;

&lt;p&gt;In this post, I’ll explore how you can provide effective code review feedback as a junior developer.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Do you need guidance in your journey as a junior developer? I can help you learn skills, be more confident, and reach your dream job! &lt;a href="https://scastiel.dev/mentoring" rel="noopener noreferrer"&gt;Check out my mentoring services&lt;/a&gt;&lt;/em&gt; 😉&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Code Review?
&lt;/h2&gt;

&lt;p&gt;Code reviews serve several purposes that benefit both individual developers and the team as a whole. Here are a few key reasons why code review is essential:&lt;/p&gt;

&lt;h3&gt;
  
  
  Learn from the Code
&lt;/h3&gt;

&lt;p&gt;Code reviews expose you to different coding styles, techniques, and approaches. By reviewing code written by other developers, more experienced or not, you can gain valuable insights and enhance your own skills.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ensure Good Practices
&lt;/h3&gt;

&lt;p&gt;Code reviews help maintain coding standards and best practices within the project. By identifying deviations from established guidelines, you contribute to the overall code quality and maintainability.&lt;/p&gt;

&lt;p&gt;For instance, if your team decided that the files should be organized by business modules, and a developer did not follow this convention in their pull-request, then code review is the right way to tell them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check for Bugs or Edge Cases
&lt;/h3&gt;

&lt;p&gt;Through careful examination, code reviews help catch potential bugs, logic errors, or vulnerabilities that might have been overlooked during development. Identifying and addressing these issues early on saves time and minimizes future complications.&lt;/p&gt;

&lt;p&gt;Note that depending on what your team agreed on, it might or might not be a responsibility of the reviewer to manually test the changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge the Implementation
&lt;/h3&gt;

&lt;p&gt;Code reviews encourage critical thinking and provide an opportunity to challenge the design decisions or suggest alternative approaches. By sharing your perspective, even as a junior developer, you contribute to the team’s collective problem-solving capabilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Code Review Should Not Be Used For
&lt;/h2&gt;

&lt;p&gt;While code review is a valuable process, it’s important to understand its scope and limitations. As a junior developer, it’s helpful to know what code review should not be primarily used for:&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Formatting and Linting
&lt;/h3&gt;

&lt;p&gt;Code reviews are not the place to nitpick about minor formatting or styling preferences. Automated tools and linting processes can handle these concerns, allowing code reviews to focus on more substantial issues: &lt;a href="https://eslint.org/" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt;, &lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt;, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Big Architecture Discussion
&lt;/h3&gt;

&lt;p&gt;Code reviews are typically not the ideal place for large-scale architectural discussions. While providing input on architectural decisions can be valuable, it’s better to address these concerns in separate discussions or meetings.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Expected from a Junior Developer
&lt;/h2&gt;

&lt;p&gt;As a junior developer participating in code reviews, you have a crucial role to play. Here’s what is expected from you:&lt;/p&gt;

&lt;h3&gt;
  
  
  Read the Code Carefully and Learn from It
&lt;/h3&gt;

&lt;p&gt;Take the time to thoroughly understand the code being reviewed. Absorb the logic, patterns, and techniques used by more experienced developers, as this will help improve your own skills.&lt;/p&gt;

&lt;p&gt;You’ll also learn about the specific project you’re working on, how the application was designed, its technical debt, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Speak Up if Something Isn’t Clear
&lt;/h3&gt;

&lt;p&gt;Don’t hesitate to ask questions or seek clarification if you encounter sections of code that are unclear or confusing. Effective communication ensures everyone is on the same page and helps prevent misunderstandings.&lt;/p&gt;

&lt;p&gt;It should be possible for every developer, including junior, to understand every piece of code in the project. If you can’t understand some code, it might be because it wasn’t written in the clearest way.&lt;/p&gt;

&lt;p&gt;Note that sometimes, the code is hard to understand for good reasons, for instance to handle performance issues. In that case, comments in the code can be used to make it easier for other developers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Share Alternative Perspectives
&lt;/h3&gt;

&lt;p&gt;As a junior developer, you bring a fresh perspective to the code review process. If you believe there’s a different, more efficient, or elegant way to implement something, respectfully share your thoughts and encourage discussion.&lt;/p&gt;

&lt;p&gt;Don’t think you are not experienced enough to challenge a senior developer’s implementation because you just learned the tech. Sometimes, asking why a developer didn’t follow the practice you just learned leads to insightful discussion!&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the Right Tone in Your Comments
&lt;/h2&gt;

&lt;p&gt;Providing feedback in a positive and constructive manner is crucial for maintaining a healthy and collaborative team environment. The following tips apply to developers of all levels of experience, but they’re especially relevant for junior developers challenging senior developers’ code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use "Why Not" Instead of "You Should"
&lt;/h3&gt;

&lt;p&gt;Frame your suggestions or alternative approaches as questions rather than commands. This approach encourages discussion and allows the developer to explain their thought process.&lt;/p&gt;

&lt;p&gt;Example: you notice that a developer did something that the framework’s documentation deprecates. Instead of telling “You should do it this way instead of that way”, consider “I notice the documentation suggest not doing it that way. Why not doing it this way instead?”.&lt;/p&gt;

&lt;h3&gt;
  
  
  Be Open and Positive
&lt;/h3&gt;

&lt;p&gt;Embrace a supportive tone in your comments, highlighting the positive aspects of the code and recognizing the developer’s effort. By fostering a positive atmosphere, you encourage continuous improvement and inspire your peers.&lt;/p&gt;

&lt;p&gt;Example: “I wasn’t aware we could implement the function this way, thanks for helping me learn! Just wondering if it couldn’t cause performance issues, due to…”&lt;/p&gt;

&lt;h3&gt;
  
  
  If You Think Something is Wrong, Suggest an Alternative
&lt;/h3&gt;

&lt;p&gt;When you add a comment to suggest a change, always try to propose a specific way to make the change. For instance, if you think a variable or function name is wrong &lt;a href="https://martinfowler.com/bliki/TwoHardThings.html" rel="noopener noreferrer"&gt;(naming things is hard)&lt;/a&gt;, don’t just tell “Please give a better name to the variable/function”. Instead, you can tell “I think &lt;em&gt;thisName&lt;/em&gt; or &lt;em&gt;thatName&lt;/em&gt; would make more sense, what do you think?”&lt;/p&gt;




&lt;p&gt;As a junior developer, participating in code reviews is an invaluable opportunity to enhance your skills, contribute to code quality, and collaborate effectively with your team.&lt;/p&gt;

&lt;p&gt;By approaching code review with a desire to learn, sharing constructive feedback, and fostering a positive environment, you can make a meaningful impact on the development process.&lt;/p&gt;

&lt;p&gt;Embrace code reviews as a chance to grow and improve, both individually and as part of a team.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover photo by &lt;a href="https://unsplash.com/fr/@anniespratt" rel="noopener noreferrer"&gt;Annie Spratt&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>codenewbie</category>
      <category>programming</category>
    </item>
    <item>
      <title>How well should I know React before applying to my first job?</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Sun, 18 Jun 2023 14:53:06 +0000</pubDate>
      <link>https://dev.to/scastiel/how-well-should-i-know-react-before-applying-to-my-first-job-3dmm</link>
      <guid>https://dev.to/scastiel/how-well-should-i-know-react-before-applying-to-my-first-job-3dmm</guid>
      <description>&lt;p&gt;A recurring question I hear from people learning React: “how do I know if I’m ready to apply to junior React developer jobs?”. Very often, they’re ready before they know.&lt;/p&gt;

&lt;p&gt;If you’ve built a couple of applications using React (your portfolio, a side project…), I bet you’re ready! (If you haven’t, what not checking out &lt;a href="https://scastiel.dev/react-workshop" rel="noopener noreferrer"&gt;my React workshop&lt;/a&gt;? 😇)&lt;/p&gt;

&lt;p&gt;In this article, I listed some things you don’t have to know, and some you do. My goal is to prepare you to your first technical interview.&lt;/p&gt;

&lt;p&gt;Let’s start with what you &lt;em&gt;think&lt;/em&gt; you might need to know but definitely don’t.&lt;/p&gt;

&lt;h2&gt;
  
  
  You don’t have to know everything
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It’s okay not to know how React works internally&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;React is complex, and not many developers know how it works internally. Although it’s a fascinating topic, don’t feel you have to learn it unless you want to apply at Meta to work &lt;em&gt;on&lt;/em&gt; React.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s okay not to have dozens of projects with React&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Showing you worked on a couple projects is more than enough. The more different they are, the better, especially if you’re able to tell how they were different and how it made the challenges different too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s okay not to have built a huge project with React&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Of course, if you worked on a big project, it’s an advantage. But don’t do it just to increase your chances at your first job interview, it isn’t worth it. If you get the job, you might work on a big project you’ll tell about at the interview for your next job!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s okay not to know how to bootstrap a complex React app&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do you know how to bootstrap an app with the right &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt; command? It’s enough! Actually, looking for it in the documentation is enough. Bootstraping an entreprise app is a useless skill, as only one person will do it in any project, and will probably forget about it before the next project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s okay not to know how to set up a deployment pipeline for React&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Setting up a deployment pipeline is not something expected from a junior developer. Plus, all applications are different, and a pipeline at one company might differ from the one at another company.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s okay not to know Redux/Xstate/Tailwind/Next.js…&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It may be seen as an advantage to have used an external library in a React project, but there’s a huge chance that the one you learned is not the one used where you’re applying. So don’t overthink it too much.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It’s okay to go find answers on the web&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Senior developers go find answers on Google, StackOverflow or ChatGPT all the time! No one will expect you to know how to build an application without using resources you can find on the web.&lt;/p&gt;




&lt;p&gt;Alhought it’s okay to go on the web to find some information, example, answers, there are basics you should know very well before going to your first technical interview:&lt;/p&gt;

&lt;h2&gt;
  
  
  But there are some things you should know
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Know JavaScript&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I know it isn’t technically React, but if you don’t master JavaScript’s basics during the interview, it isn’t a good start. Know how to declare constants, functions, how to use the spread syntax to update arrays and objects, or how to use promises…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know how to create a component&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You should know how to create a new component (as a function) with basic HTML content, and display it in your browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know how to display data conditionnaly, and how to perform iterations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Be prepared to use the ternary operator &lt;code&gt;? :&lt;/code&gt; to display data depending on a condition, and to use &lt;code&gt;.map&lt;/code&gt; to display several elements from an array (and don’t forget to use the &lt;code&gt;key&lt;/code&gt; prop).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know how to add a local state to the component&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useState&lt;/code&gt; is the most useful hook, and you should know how to use it: how to declare a state, how to update it, how to deal with complex states such as arrays or objects…&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know how to add some form inputs in it&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dealing with a text input is something you’ll have to do in many apps. Better be able to show you can do it without leaving your IDE.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know how to update the state when the user types something&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Displaying an input is nice, but you should also know how to use the &lt;em&gt;change&lt;/em&gt; event to update the state accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know how to perform a &lt;code&gt;fetch&lt;/code&gt; request when the component is loaded&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maybe you won’t have to use &lt;code&gt;fetch&lt;/code&gt;, but you should at least know how to call an async function in the component… well, in a &lt;code&gt;useEffect&lt;/code&gt;, without forgetting to define the dependency array.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know how to update the state depending on the &lt;code&gt;fetch&lt;/code&gt; request result&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Storing the result of an async request (using a combination of &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useEffect&lt;/code&gt;) is a very common pattern. If you show you can do it eyes closed, the recruiter will be impressed!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know how to extract some logic in another component, passing props&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Components are at the core of React, so you should know how to create a new component accepting props, and use it in the one you already created.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Know what you don’t know&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even if you don’t know how to use them, be able to mention the other hooks that exist and what they can be used for: &lt;code&gt;useReducer&lt;/code&gt;, &lt;code&gt;useRef&lt;/code&gt;… You can also read about server-side rendering, global state management libraries such as Redux…&lt;/p&gt;




&lt;p&gt;Do you feel more confident now? Even if you don’t know some of the points I mentioned, you must admit it’s not that much: if you learned React already, you shouldn’t need too much time to learn what you’re missing.&lt;/p&gt;

&lt;p&gt;Also, it’s okay if you don’t do perfectly well during the interview. The most important is that you don’t feel blocked. If you do, tell the interviewers and they’ll unblock you.&lt;/p&gt;

&lt;p&gt;Hopefully you feel more prepared now; or at least you know what to prepare next 😊.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you need any help to get ready for your first (or next) job interview, have a look at the &lt;a href="https://scastiel.dev/mentoring" rel="noopener noreferrer"&gt;mentoring services&lt;/a&gt; I’m offering! I can help by answering questions, reviewing your portfolio, and even make you pass a mock interview 😉.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Cover photo by &lt;a href="https://unsplash.com/fr/@lautaroandreani" rel="noopener noreferrer"&gt;Lautaro Andreani&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>interview</category>
      <category>job</category>
    </item>
    <item>
      <title>A better learning path for React with server components</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Wed, 14 Jun 2023 13:27:45 +0000</pubDate>
      <link>https://dev.to/scastiel/a-better-learning-path-for-react-with-server-components-5ebn</link>
      <guid>https://dev.to/scastiel/a-better-learning-path-for-react-with-server-components-5ebn</guid>
      <description>&lt;p&gt;I’ve always loved sharing my knowledge. When I love a language, library or framework, more than exploring every tiny detail of it, I think about how I can help people learn it and like it as much as I do.&lt;/p&gt;

&lt;p&gt;This is why a while ago I wrote a book in French about React, then a course about React hooks, and finally an ebook about Next.js last fall (see my &lt;a href="https://scastiel.dev/books" rel="noopener noreferrer"&gt;books&lt;/a&gt; page).&lt;/p&gt;

&lt;p&gt;But the world of programming is evolving fast, and the recent new features in React and Next.js shuffle everything. Not in a way that everything you know already is now wrong, but because you can do much more than before, and learn things completely differently.&lt;/p&gt;

&lt;p&gt;With the recently-introduced &lt;a href="https://nextjs.org/docs/getting-started/react-essentials" rel="noopener noreferrer"&gt;React Server Components&lt;/a&gt; (RSC), it is now possible to create components that will be rendered on the server. My goal here is not to explain how to use them or what new features they bring, but to propose a new way of learning React they enable.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PS: I am now offering a workshop called &lt;a href="https://scastiel.dev/react-workshop" rel="noopener noreferrer"&gt;Learn React the Modern Way&lt;/a&gt; teaching React the way I propose here. There is also an &lt;a href="https://learn.scastiel.dev" rel="noopener noreferrer"&gt;online course&lt;/a&gt; if you prefer 😉.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is the way we use to teach/learn React and Next.js:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start with React’s client features to build a Single Page Application: local state, effects…&lt;/li&gt;
&lt;li&gt;Continue with Next.js to create full-stack apps, with Server-Side Rendering&lt;/li&gt;
&lt;li&gt;Pursue with React Server Components to add more logic on the backend&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is how most people went from client-only React to RSC, because Next.js was created after React, and RSC only became real a few months ago.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if we took advantage of React Server Components not only to improve how we use React, but also how we help people learn it from the beginning?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think about it. Why not starting with React Server Components, then moving to client features?&lt;/p&gt;

&lt;p&gt;It could look like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Learn Next.js as a full-stack framework, using React as a rendering engine&lt;/li&gt;
&lt;li&gt;Discover React’s client features to add interactivity to your pages&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the first part of the journey, you could learn how to create React components on the server, and fetching data directly from inside the component.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nc"&gt;PostsPage&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;posts&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;fetchPosts&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;ul&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;posts&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;post&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;))}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&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;If you aren’t used to React Server Components, this piece of code might seem weird. But for someone who doesn’t know React yet, the logic is quite easy to understand. You fetch posts, then you display them.&lt;/p&gt;

&lt;p&gt;Using a plain client component, you’d have to create a state with &lt;code&gt;useState&lt;/code&gt;, then fetch the posts inside a &lt;code&gt;useEffect&lt;/code&gt;. Explaining everything you need to understand the code would take much longer.&lt;/p&gt;

&lt;p&gt;And don’t get me wrong: it’s important to know how to perform a request from a client component. Just, maybe a bit later, when you learn React’s client features. But you can already learn so much without hearing the word “hook”: components, JSX, conditionals &amp;amp; iterations, styling, routing, fetching data…&lt;/p&gt;

&lt;p&gt;I understand this path I’m proposing can seem counter-intuitive. And to be honest, it’s still a bit early to know for sure whether it’s better than the &lt;em&gt;classic&lt;/em&gt; way. I am glad to see that &lt;a href="https://twitter.com/shadcn/status/1660338653305114625" rel="noopener noreferrer"&gt;I’m not the only one&lt;/a&gt; thinking it might be:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/shadcn/status/1660338653305114625" rel="noopener noreferrer"&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%2F1eeilgnaq7mq95mqf8ac.png" alt="@shadcn: “An interesting side effect of RSC is that React is now actually easier to teach/learn. If you’ve used React for a while, yes it requires a mental model switch. But for beginners, it makes so much sense: fetch data, render and style it - all in one place. If you need to do something in the browser, " width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I just finished designing a workshop called &lt;a href="https://scastiel.dev/react-workshop" rel="noopener noreferrer"&gt;Learn React the Modern Way&lt;/a&gt; to help beginners learn React and Next.js, and will experiment this new path. I strongly believe it will make the learning journey way easier for people who know HTML, CSS and JavaScript.&lt;/p&gt;

&lt;p&gt;Can’t wait to keep you posted about how it goes 😊&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover photo by &lt;a href="https://unsplash.com/@foxxmd" rel="noopener noreferrer"&gt;Matt Duncan&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>learning</category>
      <category>frontend</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Create OG images for your blog with Next.js</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Thu, 10 Nov 2022 15:17:18 +0000</pubDate>
      <link>https://dev.to/scastiel/create-og-images-for-your-blog-with-nextjs-d5f</link>
      <guid>https://dev.to/scastiel/create-og-images-for-your-blog-with-nextjs-d5f</guid>
      <description>&lt;p&gt;At the same time I released the new design for this blog using Next.js, &lt;a href="https://vercel.com/" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt; announced a new library to generate OpenGraph (OG) images. You know, these images you can see when posting some links on Twitter.&lt;/p&gt;

&lt;p&gt;Since my blog posts had a cover image associated, it sounded natural to me to use this image. But I often wonder if it was worth it to generate an image with additional content such as the post title or its date. It was pretty complicated to generate an image.&lt;/p&gt;

&lt;p&gt;Fortunately, &lt;a href="https://vercel.com/docs/concepts/functions/edge-functions/og-image-api" rel="noopener noreferrer"&gt;Vercel’s OG library&lt;/a&gt; now makes it much easier. Let’s see how by implementing OG image generation for an imaginary blog.&lt;/p&gt;

&lt;p&gt;👉 &lt;em&gt;If you are curious about Next.js and want to learn it, check out by book &lt;a href="https://amzn.to/3E0DilE" rel="noopener noreferrer"&gt;Serverless Web Applications with React and Next.js&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;📺 &lt;em&gt;If you prefer video content, I created a short video with the content of this post:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/CZRlC3nXeyg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;@vercel/og&lt;/code&gt;, &lt;code&gt;satori&lt;/code&gt;, and Edge functions
&lt;/h2&gt;

&lt;p&gt;Generating OG images is made possible by the &lt;code&gt;@vercel/og&lt;/code&gt; library, itself relying on &lt;a href="https://github.com/vercel/satori" rel="noopener noreferrer"&gt;&lt;code&gt;satori&lt;/code&gt;&lt;/a&gt; (also made by Vercel). This package allows you to create an image from HTML and CSS code (using JSX). &lt;code&gt;@vercel/og&lt;/code&gt; adds a layer to make it easier to use, for instance by loading a default font so you don’t have to.&lt;/p&gt;

&lt;p&gt;On the client, such generation does not sound complicated, and we’ve known how to do it for a long time using a &lt;code&gt;canvas&lt;/code&gt; element. But on the server, you had to start a headless Chrome browser, which was complex and expensive. &lt;code&gt;satori&lt;/code&gt; doesn’t work that way, and can generate images efficiently on the client, or in serverless functions.&lt;/p&gt;

&lt;p&gt;Even better, it can run on &lt;em&gt;Edge functions&lt;/em&gt;, and that is what &lt;code&gt;@vercel/og&lt;/code&gt; does. &lt;a href="https://vercel.com/docs/concepts/functions/edge-functions" rel="noopener noreferrer"&gt;Edge functions&lt;/a&gt; are a special kind of serverless functions, that are free from all the classic Node.js runtime features. You can only use the basic JavaScript features, from the V8 runtime, so Edge functions comes with some limitations, but are extremely fast, and their execution cheaper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate an image with &lt;code&gt;@vercel/og&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You can follow this tutorial on any Next.js project. I’ll show how to start from an empty Next.js application, just generated with the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;yarn create next-app hello-og &lt;span class="nt"&gt;--typescript&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this project, we’ll use a serverless function to generate OG images, accessible at the endpoint &lt;code&gt;/api/og&lt;/code&gt;. Let’s create it in a new &lt;code&gt;/pages/api/og.tsx&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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;next/server&lt;/span&gt;&lt;span class="dl"&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;experimental-edge&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;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&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;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&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="na"&gt;hello&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="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="nx"&gt;handle&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are used to serverless functions in Next.js, you might notice some differences:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The exported &lt;code&gt;config&lt;/code&gt; object. Here we tell Next.js that this function is an &lt;em&gt;Edge function&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;req&lt;/code&gt; parameter does not have the &lt;code&gt;NextApiRequest&lt;/code&gt; type, but &lt;code&gt;NextRequest&lt;/code&gt;. This is also because it’s an Edge function.&lt;/li&gt;
&lt;li&gt;We &lt;em&gt;return&lt;/em&gt; a response (instead of using a &lt;code&gt;res&lt;/code&gt; object passed as parameter), of type &lt;code&gt;NextResponse&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Still, if you run the application and call the endpoint, you will get the expected JSON result, as with any classic function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl http://localhost:3000/api/og
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"hello"&lt;/span&gt;:true&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let’s update this function to return an image using &lt;code&gt;@vercel/og&lt;/code&gt;. We first need to add the dependency to our project with &lt;code&gt;yarn add @vercel/og&lt;/code&gt;. Then we can generate a basic image containing the text “Hello World!”:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ImageResponse&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;@vercel/og&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&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;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ImageResponse&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;Hello World!&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="c1"&gt;// 1200x628 seems to be the right dimensions to use for OG images.&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="mi"&gt;1200&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;628&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;With these three lines of code returning an &lt;code&gt;ImageResponse&lt;/code&gt;, we generate an image from React code. That sounds easy, right? You probably think “this is great, I can use all my React components!”… Well, it’s not that easy 😉.&lt;/p&gt;

&lt;p&gt;Even though we use JSX here, it’s important to understand that it’s just a shorthand offered by &lt;code&gt;satori&lt;/code&gt; (which seems to use React, but not React-DOM). The JSX code you write here has some constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/vercel/satori#css" rel="noopener noreferrer"&gt;Not all CSS properties are supported&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;Only &lt;code&gt;flex&lt;/code&gt; layout is supported, and each element with multiple children must explicitely use (&lt;code&gt;display: flex&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;You can’t use &lt;code&gt;script&lt;/code&gt; or &lt;code&gt;link&lt;/code&gt; elements, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Knowing these constraints, we can still do a lot. A feature I love: you can use &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;TailwindCSS&lt;/a&gt; classes natively, using the &lt;code&gt;tw&lt;/code&gt; prop on elements (instead of &lt;code&gt;className&lt;/code&gt;). If you prefer, you can use the classic &lt;code&gt;style&lt;/code&gt; prop to specify CSS properties.&lt;/p&gt;

&lt;p&gt;Let’s assume we want to display a post title, an author name, and a date that we’ll receive from query parameters. To get these parameters, we can’t use &lt;code&gt;req.query&lt;/code&gt;: in Edge functions, we have to use &lt;code&gt;req.url&lt;/code&gt;, that we can parse using the &lt;code&gt;URL&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&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;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&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="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No post title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&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="s1"&gt;author&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Anonymous&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;searchParams&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="s1"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2022-11-05T12:00:00.000Z&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;And then, we can use these parameters to add them to the image, with some TailwindCSS classes to make everything a bit more beautiful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ImageResponse&lt;/span&gt;&lt;span class="p"&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;div&lt;/span&gt; &lt;span class="na"&gt;tw&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex w-full h-full flex-col justify-end bg-slate-200 items-stretch"&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;tw&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col bg-white p-8"&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;tw&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-5xl mb-4"&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;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;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;tw&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl"&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;author&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
              &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; – &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
              &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLocaleDateString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;en-US&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;dateStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;long&lt;/span&gt;&lt;span class="dl"&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;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="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;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&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;628&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;Here is what this first version of our OG image looks like, with some parameters:&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%2Fz6l75knll2iuaxlhbq7o.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%2Fz6l75knll2iuaxlhbq7o.png" alt="First version of our OG image" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  An image inside the image
&lt;/h2&gt;

&lt;p&gt;This is nice, but it misses the most important thing we want on it: the cover image. Let’s add a &lt;code&gt;cover&lt;/code&gt; parameter, and use it in an &lt;code&gt;img&lt;/code&gt; element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cover&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;//&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;
    &lt;span class="nx"&gt;searchParams&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="s1"&gt;cover&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/cover.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ImageResponse&lt;/span&gt;&lt;span class="p"&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;div&lt;/span&gt; &lt;span class="na"&gt;tw&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex w-full h-full flex-col justify-end bg-slate-200 items-stretch"&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;img&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;cover&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;""&lt;/span&gt;
          &lt;span class="na"&gt;tw&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-1 w-full h-full"&lt;/span&gt;
          &lt;span class="na"&gt;style&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="na"&gt;objectFit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cover&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;objectPosition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;center&lt;/span&gt;&lt;span class="dl"&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;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&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;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;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1200&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;628&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;(To run the example, go get an image on &lt;a href="https://unsplash.com/" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt; and put it in &lt;em&gt;public/cover.jpg&lt;/em&gt;.)&lt;/p&gt;

&lt;p&gt;Note how we use the &lt;code&gt;host&lt;/code&gt; and &lt;code&gt;protocol&lt;/code&gt; attributes from the request’s URL, as we need to use absolute URLs for &lt;code&gt;img&lt;/code&gt; elements.&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%2F43tonpgbfq6p4i85nvr4.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%2F43tonpgbfq6p4i85nvr4.png" alt="Second version of our OG image" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This second version is much more satisfying 😊… until we deploy it on Vercel. After deployment, if you call try to generate an image, you might get a blank image. In the Vercel logs, you’ll see this kind of error:&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%2Fp182pvy4fs2m5xyr3jfv.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%2Fp182pvy4fs2m5xyr3jfv.png" alt="The error log on Vercel" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It isn’t obvious, but the issue comes from the cover image we want to include, which seems too big for &lt;code&gt;@vercel/og&lt;/code&gt; at the time I’m writing this post. We could choose to use a smaller image, but it’s pretty annoying…&lt;/p&gt;

&lt;p&gt;Good news, Next.js already provides an easy way to resize images. When using the &lt;code&gt;next/image&lt;/code&gt; component, Next.js uses an endpoint at &lt;code&gt;/_next/image&lt;/code&gt;. While we can not use the component, we can use the endpoint!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cover&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;//&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;host&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/_next/image?url=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;searchParams&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="s1"&gt;cover&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/cover.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;w=1200&amp;amp;q=75`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add OG image to your pages
&lt;/h2&gt;

&lt;p&gt;Now that we know how to generate our image, we can use it in the meta tags of the page headers. You can find the complete list of meta tags you need to define in &lt;a href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/summary-card-with-large-image" rel="noopener noreferrer"&gt;Twitter documentation&lt;/a&gt;. I like using a custom component I built in many projects of mine: &lt;a href="https://gist.github.com/scastiel/d127d5db7bf34e86cb5798cdc6ace5e3" rel="noopener noreferrer"&gt;&lt;code&gt;SeoHeaders&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you are adding OG images to a Next.js blog, I assume you already have a component in which you know the post title, author, date, and cover image. You can build the OG image URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// It must be an absolute URL, so I use an&lt;/span&gt;
&lt;span class="c1"&gt;// environment variable for the base URL.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;imageUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/og?`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`title=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`&amp;amp;author=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`&amp;amp;cover=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cover&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
  &lt;span class="s2"&gt;`&amp;amp;date=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, you can pass this URL to the &lt;code&gt;SeoHeaders&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SeoHeaders&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;
  &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;twitterAuthor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;
  &lt;span class="na"&gt;twitterSite&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NEXT_PUBLIC_BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;imageUrl&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&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;Or, if you add the &lt;code&gt;meta&lt;/code&gt; tags by yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&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;meta&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;"twitter:image"&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&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;Note that so that the image is used in social media, you need to specify the other tags: &lt;code&gt;og:type&lt;/code&gt;, &lt;code&gt;twitter:card&lt;/code&gt;, etc.&lt;/p&gt;




&lt;p&gt;This concludes this tutorial about generating and using OG images in a Next.js application! Our image is pretty minimalist, but here are a few ideas if you want to improve it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Play with opacity options (using TailwindCSS class &lt;code&gt;bg-opacity-50&lt;/code&gt; for instance) instead of having a white background for the text.&lt;/li&gt;
&lt;li&gt;Add the author’s avatar (using a second image).&lt;/li&gt;
&lt;li&gt;Add the description to the image, or an excerpt of it.&lt;/li&gt;
&lt;li&gt;If your blog allows comments or likes, you can add some counter to the image.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll find the complete source code of the example Next.js application on &lt;a href="https://github.com/scastiel/hello-og" rel="noopener noreferrer"&gt;this GitHub repo&lt;/a&gt;. Don’t forget to check out the documentation of &lt;a href="https://vercel.com/docs/concepts/functions/edge-functions/og-image-generation" rel="noopener noreferrer"&gt;Vercel’s OG Image Generation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post was first posted on my blog: &lt;a href="https://scastiel.dev/create-og-images-for-your-blog-with-nextjs" rel="noopener noreferrer"&gt;Create OG images for your blog with Next.js&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Cover photo by &lt;a href="https://unsplash.com/@theycallmenomz" rel="noopener noreferrer"&gt;Noman Shahid&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>typescript</category>
      <category>react</category>
    </item>
    <item>
      <title>Combining scripts and DSLs is Kotlin’s most underrated feature</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Thu, 24 Mar 2022 20:51:07 +0000</pubDate>
      <link>https://dev.to/scastiel/combining-scripts-and-dsls-is-kotlins-most-underrated-feature-54oj</link>
      <guid>https://dev.to/scastiel/combining-scripts-and-dsls-is-kotlins-most-underrated-feature-54oj</guid>
      <description>&lt;p&gt;I discovered Kotlin recently and realized that, for many people, it was only a new syntax for Java. But the more I play with it, the more convinced I am that a combo of two of its features is vastly underrated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The ability to create domain-specific languages (DSL), thanks to some syntactic sugar;&lt;/li&gt;
&lt;li&gt;The ability to write scripts and create interpreters for them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can easily guess how nice these features can play together. We can create a DSL, tailor it to our needs, and use it in scripts. Why scripts? Let me introduce the example I will develop here to illustrate why.&lt;/p&gt;

&lt;p&gt;Not long ago, I started keeping track of my personal finances. I wanted to keep track of the total value of everything I own (money in bank accounts, stocks, real estate), and I was disappointed in the third-party services I found. So I developed my own tool to fit my needs. &lt;a href="https://scastiel.gumroad.com/l/kotlin-net-worth" rel="noopener noreferrer"&gt;(Want to know more? I wrote a book about it!)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My program’s input had to be the values of my accounts over time (I wanted to manually write each value instead of relying on an unreliable API to fetch them automatically). To provide this input, I had quite a few options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I could store them in a database and develop tools to populate it (a GUI, a CLI…)&lt;/li&gt;
&lt;li&gt;I could put them in a JSON file&lt;/li&gt;
&lt;li&gt;Why not store them in a Kotlin file?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Providing the input in a Kotlin file sounded like a weird thing to do. On the one hand, I could benefit from IntelliJ’s autocomplete features since I would use the functions and classes I wrote. On the other hand, it meant I would have to rebuild the program each time I updated the input?? How unpractical does it seem?&lt;/p&gt;

&lt;p&gt;Here is where scripts come into play. With Kotlin, you can write a script interpreter embedding your domain logic, including a DSL, and write scripts providing the input to that logic.&lt;/p&gt;

&lt;p&gt;In this article, we’ll build a “minimum viable example”, a &lt;em&gt;net worth tracker&lt;/em&gt; to demonstrate how it can work. But if you already know Kotlin, you likely already use scripts and DSLs without even knowing it: in your &lt;em&gt;build.gradle.kts&lt;/em&gt; file, you are using the Gradle DSL in a script!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The full example is &lt;a href="https://github.com/scastiel/kt-dsl-scripts" rel="noopener noreferrer"&gt;available on GitHub&lt;/a&gt;. The &lt;a href="https://github.com/scastiel/kt-dsl-scripts" rel="noopener noreferrer"&gt;main&lt;/a&gt; branch contains the final version, and you’ll find there two branches &lt;a href="https://github.com/scastiel/kt-dsl-scripts/tree/step-1" rel="noopener noreferrer"&gt;step-1&lt;/a&gt; and &lt;a href="https://github.com/scastiel/kt-dsl-scripts/tree/step-2" rel="noopener noreferrer"&gt;step-2&lt;/a&gt; with the code of the first two sections. I removed the imports from the code examples to make the post lighter, but you can follow the link attached to code blocks to see the full file content.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create the domain logic
&lt;/h2&gt;

&lt;p&gt;We want to write a program that takes accounts and snapshots as inputs and displays a report as an output. A snapshot represents the states of several accounts at a given date, so the class contains a date attribute and a mapping between accounts and their respective value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/yckwfds4&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&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 kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/57vr36mw&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Double&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;To create a report, we need to provide a list of accounts and snapshots. Our minimal report will have two features: displaying the account list and displaying each snapshot (with, for each of them, the value of each account).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/2whmpup6&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;displayAccountList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ACCOUNTS:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"- ${it.name}"&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;fun&lt;/span&gt; &lt;span class="nf"&gt;displaySnapshots&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SNAPSHOTS:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;snapshot&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"* ${snapshot.date}:"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"  - ${account.name}: ${snapshot.balances[account]?.toString() ?: "&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="s"&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="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;As a first step, let’s use these classes in the &lt;code&gt;main&lt;/code&gt; function. We first create two accounts, then two snapshots defining a value for these accounts. Finally, we create a report and display the accounts’ and the snapshots’ list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/s8564hy5&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;checking&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Checking"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;savings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Savings"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;snapshots&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2022-01-01"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checking&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;1000.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savings&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;2000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2022-02-01"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checking&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;1200.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savings&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;2500.0&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;val&lt;/span&gt; &lt;span class="py"&gt;report&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savings&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;displayAccountList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;displaySnapshots&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;Ok, our program works, but we will improve it in two ways: first, we will make it possible to extract the content of &lt;code&gt;main&lt;/code&gt; into a script (so we don’t have to rebuild the application when we add new snapshots), then we will create some helpers (our DSL) to make it easier to edit the script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Create a script definition
&lt;/h2&gt;

&lt;p&gt;Using Kotlin to write scripts is unfortunately not a very well-documented feature. It changed a lot in the past, so the tutorials you can find are not always still valid. But it doesn’t mean we cannot enjoy it!&lt;/p&gt;

&lt;p&gt;The first step is to include some dependencies to our project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/5ase8sdr&lt;/span&gt;
&lt;span class="nf"&gt;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlin:kotlin-scripting-common:1.6.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlin:kotlin-scripting-jvm:1.6.0"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlin:kotlin-scripting-jvm-host:1.6.0"&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 write a script interpreter, we need to create two entities: a script &lt;em&gt;definition&lt;/em&gt; and a script &lt;em&gt;host&lt;/em&gt;. Let’s start with the script definition, &lt;code&gt;NwtScript&lt;/code&gt; (&lt;code&gt;nwt&lt;/code&gt; stands for &lt;em&gt;net worth tracker&lt;/em&gt;). It can be as simple as an abstract class annotated with &lt;code&gt;@KotlinScript&lt;/code&gt;. To make writing script easier, we can also provide some classes that will automatically be imported, using a &lt;code&gt;ScriptCompilationConfiguration&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/yc26svyp&lt;/span&gt;
&lt;span class="nd"&gt;@KotlinScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;fileExtension&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"nwt.kts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;compilationConfiguration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NwtScriptConfiguration&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NwtScript&lt;/span&gt;

&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;NwtScriptConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ScriptCompilationConfiguration&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;defaultImports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Report&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&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 also provide an extension for our scripts; note that, so we enjoy IntelliJ’s features such as autocomplete, this extension has to be composed of a prefix of your choice, then &lt;code&gt;.kts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can now create the script host &lt;code&gt;NwtScriptHost&lt;/code&gt; to use this script definition. This class has nothing very complex: it has a public &lt;code&gt;execFile&lt;/code&gt; method that accepts a filename, evaluates it using a &lt;code&gt;BasicJvmScriptingHost&lt;/code&gt;, and displays possible log messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/2p88u9a5&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NwtScriptHost&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;evalFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scriptFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;ResultWithDiagnostics&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;EvaluationResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;compilationConfiguration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;createJvmCompilationConfigurationFromTemplate&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;NwtScript&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;jvm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Script will have access to everything in the classpath&lt;/span&gt;
        &lt;span class="nf"&gt;dependenciesFromCurrentContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wholeClasspath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&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="nc"&gt;BasicJvmScriptingHost&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scriptFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toScriptSource&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;compilationConfiguration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;execFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&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;val&lt;/span&gt; &lt;span class="py"&gt;scriptFile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;res&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;evalFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scriptFile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&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="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;ScriptDiagnostic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Severity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DEBUG&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" : ${it.message}"&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="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exception&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;": ${it.exception}"&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="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;As the final step before we create our script, we need to tell IntelliJ about our script definition, so it knows what classes we can access and provide the autocomplete and code annotation features we like.&lt;/p&gt;

&lt;p&gt;We need to create a folder &lt;code&gt;src/main/resources/META-INF/kotlin/script/templates&lt;/code&gt; and in it an empty file named &lt;a href="https://github.com/scastiel/kt-dsl-scripts/blob/step-2/src/main/resources/META-INF/kotlin/script/templates/nwt.script.NwtScript.classname" rel="noopener noreferrer"&gt;&lt;code&gt;nwt.script.NwtScript.classname&lt;/code&gt;&lt;/a&gt; (the full class name + &lt;code&gt;.classname&lt;/code&gt;). Here is the part I’m least comfortable explaining because I found how to make it work on some forums and not in official documentation 😅.&lt;/p&gt;

&lt;p&gt;After creating the file, rebuild the application, and you may need to restart IntelliJ, invalidate caches… To be honest, I’m not sure exactly what works, but in the end, when you create a &lt;code&gt;.nwt.kts&lt;/code&gt; file (let’s say &lt;code&gt;my.nwt.kts&lt;/code&gt; at the project’s root), IntelliJ should tell you there is a script definition available for it 🎉.&lt;/p&gt;

&lt;p&gt;Put the content of &lt;code&gt;main&lt;/code&gt; in the new script file, and if everything went well, you should see that IntelliJ annotates the parameters of &lt;code&gt;Account&lt;/code&gt;, for instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/4enfpevm&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;checking&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Checking"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;savings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Savings"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;snapshotJanuary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2022-01-01"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checking&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;1000.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savings&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;2000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;snapshotFebruary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2022-02-01"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;mapOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checking&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;1200.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savings&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mf"&gt;2500.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;report&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savings&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshotJanuary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snapshotFebruary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;displayAccountList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;displaySnapshots&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To transform our program into an interpreter for our script, we can now update our &lt;code&gt;main&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/mpzhf66e&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;vararg&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&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="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"usage: &amp;lt;app&amp;gt; &amp;lt;script file&amp;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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;NwtScriptHost&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;execFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;To run it, we can update the project’s run configuration, defining “my.nwt.kts” in the &lt;em&gt;Program arguments&lt;/em&gt; field. You will notice that the application won't be rebuilt if you update the script file and rerun it!&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%2Fv7pzqbllmwrkb8mpss3g.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%2Fv7pzqbllmwrkb8mpss3g.png" alt="Update the run configuration to include the script name as argument." width="800" height="330"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is already lovely, but we can make our script better by providing high-level functions, so we don’t feel like writing code!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create builders and the DSL
&lt;/h2&gt;

&lt;p&gt;We will use a well-known design pattern to implement our DSL: &lt;a href="https://en.wikipedia.org/wiki/Builder_pattern" rel="noopener noreferrer"&gt;the Builder pattern&lt;/a&gt;. For instance, to create a &lt;code&gt;Snapshot&lt;/code&gt;, we can create a class &lt;code&gt;SnapshotBuilder&lt;/code&gt;. Its constructor takes two parameters: the snapshot’s date and a map of accounts (we will see later how we provide these accounts).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/4tuffu26&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SnapshotBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mutableMapOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;account&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid account ID"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Snapshot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;balances&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;To use the builder, we can call its &lt;code&gt;balance&lt;/code&gt; method to define the accounts’ balances; then, we call the &lt;code&gt;build&lt;/code&gt; method to get the snapshot.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;builder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SnapshotBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2022-01-01"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;snapshot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we can do better. We can define a &lt;code&gt;snapshot&lt;/code&gt; function taking two parameters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;date&lt;/code&gt;: the snapshot’s date,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;initialize&lt;/code&gt;: a function to initialize the builder. This function will receive the builder as &lt;code&gt;this&lt;/code&gt; (thanks to the call to &lt;code&gt;apply&lt;/code&gt;), meaning that we will be able to call the &lt;code&gt;balance&lt;/code&gt; method without any prefix.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SnapshotBuilder&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Snapshot&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// We’ll see soon where `accounts` comes from&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;SnapshotBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;snapshot&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2022-02-01"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2000.0&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 me, this is what makes Kotlin great for writing DSLs. When using the &lt;code&gt;snapshot&lt;/code&gt; function, you don’t realize you are creating and using a builder!&lt;/p&gt;

&lt;p&gt;Let’s create another builder to build several snapshots as a next step. This new &lt;code&gt;SnapshotsBuilder&lt;/code&gt; will use the &lt;code&gt;SnapshotBuilder&lt;/code&gt; and embed the &lt;code&gt;snapshot&lt;/code&gt; we just saw (renaming it &lt;code&gt;on&lt;/code&gt; for a better DSL, you’ll see):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/52fbfhhn&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SnapshotsBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mutableListOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SnapshotBuilder&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;snapshotBuilder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SnapshotBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snapshotBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;snapshots&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 will use this builder via another function, &lt;code&gt;snapshots&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SnapshotsBuilder&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// We’ll see soon where `accounts` comes from&lt;/span&gt;
  &lt;span class="n"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SnapshotsBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;snapshots&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2022-01-01"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2022-02-01"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1200.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2500.0&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;Let’s continue by creating a builder for reports. The &lt;code&gt;ReportBuilder&lt;/code&gt; class technically doesn’t implement the builder pattern since it doesn’t build anything (and doesn’t have a &lt;code&gt;build&lt;/code&gt; method), but it will still be helpful to us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/2v35d9fu&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReportBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;report&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;displayAccountList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;displaySnapshots&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;Same as we did for snapshots, we can create a &lt;code&gt;report&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ReportBuilder&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// We’ll see soon where `accounts` and `snapshots` come from&lt;/span&gt;
  &lt;span class="nc"&gt;ReportBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;report&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nf"&gt;snapshots&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;Last but not least, let’s create the last builder to unify them all! Our &lt;code&gt;NwtBuilder&lt;/code&gt; embeds the &lt;code&gt;snapshots&lt;/code&gt; and &lt;code&gt;report&lt;/code&gt; functions as methods and is responsible for keeping the state of our program: the &lt;code&gt;accounts&lt;/code&gt; and the &lt;code&gt;snapshots&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/2p8n8av6&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NwtBuilder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mutableMapOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;MutableList&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Snapshot&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mutableListOf&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SnapshotsBuilder&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SnapshotsBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ReportBuilder&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;ReportBuilder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;snapshots&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/3cue36re&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;nwt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;NwtBuilder&lt;/span&gt;&lt;span class="p"&gt;.()&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;NwtBuilder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialize&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;With the new &lt;code&gt;nwt&lt;/code&gt; function, we won’t need access to the classes &lt;code&gt;Account&lt;/code&gt;, &lt;code&gt;Snapshot&lt;/code&gt;, and &lt;code&gt;Report&lt;/code&gt; in the script. So we can update the script definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/mhecyc6n&lt;/span&gt;
&lt;span class="nd"&gt;@KotlinScript&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fileExtension&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"nwt.kts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compilationConfiguration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NwtScriptConfiguration&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NwtScript&lt;/span&gt;

&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;NwtScriptConfiguration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ScriptCompilationConfiguration&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// give access to our `nwt` function without importing it&lt;/span&gt;
  &lt;span class="nf"&gt;defaultImports&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"nwt.dsl.nwt"&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 we have everything we need to update our script. No more &lt;code&gt;val&lt;/code&gt;, no more explicit object creation. Only the bare minimum to provide to the interpreter the inputs (the accounts and their balances over time) and what we want as output (the report definition).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://tinyurl.com/3xh24mny&lt;/span&gt;
&lt;span class="nf"&gt;nwt&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Checking"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Savings"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;snapshots&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2022-01-01"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2000.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2022-02-01"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checking"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1200.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"savings"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;2500.0&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="nf"&gt;report&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;snapshots&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;Again, since we updated the script definition, you may need to rebuild the project, invalidate caches, restart IntelliJ, etc., to make it effective. But look at how nice it is that IntelliJ recognizes our DSL:&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%2Fgb7x65lq23y4fbaw8ldq.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%2Fgb7x65lq23y4fbaw8ldq.png" alt="IntelliJ recognizes our DSL!" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Kotlin is already widely used to write domain-specific languages. You can use it to write &lt;a href="https://docs.gradle.org/current/userguide/kotlin_dsl.html" rel="noopener noreferrer"&gt;Gradle configuration files&lt;/a&gt;, to &lt;a href="https://kotlinlang.org/docs/typesafe-html-dsl.html" rel="noopener noreferrer"&gt;create HTML documents&lt;/a&gt;, and I hope I convided you that creating your own DSL is worth it too, especially used in combination with the ability to write script interpreters.&lt;/p&gt;

</description>
      <category>kotlin</category>
    </item>
    <item>
      <title>Custom React hooks to use the browser’s APIs</title>
      <dc:creator>Sebastien Castiel</dc:creator>
      <pubDate>Mon, 18 Jan 2021 14:23:11 +0000</pubDate>
      <link>https://dev.to/scastiel/custom-react-hooks-to-use-the-browser-s-apis-2kp</link>
      <guid>https://dev.to/scastiel/custom-react-hooks-to-use-the-browser-s-apis-2kp</guid>
      <description>&lt;p&gt;A thing you often need to do in React is access the browser’s APIs. These APIs represent side effects, and most of the time, you will want to store what they return in a local state: a perfect opportunity to write some custom hooks that you’ll be able to reuse across your applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Access the local storage
&lt;/h2&gt;

&lt;p&gt;The browser’s local storage is a place you can save some values, so they are persisted for when you leave the page and go back. It’s key-value storage and its API is quite straightforward:&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="c1"&gt;// returns null if no value exists for the given key&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key&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="s1"&gt;value&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;If you are not familiar with it, you can play with it just by using the console in your browser. Try to create some values, refresh the page, and get them back. Note that you can only store string values.&lt;/p&gt;

&lt;p&gt;Here, we’ll write an improved version of the &lt;code&gt;useState&lt;/code&gt; hook that persists the value in the local storage. If the user refreshes the page, the state will be initialized with the stored value.&lt;/p&gt;

&lt;p&gt;We want our hook to be used almost the same way as &lt;code&gt;useState&lt;/code&gt;, so we will make it return the same kind of array, with the current value and a setter. It will accept as parameters the initial state value and the key used to store the value in the local storage.&lt;/p&gt;

&lt;p&gt;Let’s start by just using a classic state provided by &lt;code&gt;useState&lt;/code&gt;:&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="nx"&gt;usePersistedState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialValue&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="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="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialValue&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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&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;First thing, when setting a new value, we want to store this new value in the local storage using &lt;code&gt;localStorage.setItem&lt;/code&gt;. Let’s create a function doing this operation just after calling the original &lt;code&gt;setValue&lt;/code&gt;, and return this function in place of &lt;code&gt;setValue&lt;/code&gt;:&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="nx"&gt;setAndPersistValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&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="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAndPersistValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when the component is mounted, we want to get the currently stored value from the state, and if it exists, update our state’s value with it.&lt;/p&gt;

&lt;p&gt;At that point, we have a choice to make: what value do we want to return before we get the value from the local storage? Two solutions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We return the provided &lt;code&gt;initialValue&lt;/code&gt; and replace it with the existing value if it exists;&lt;/li&gt;
&lt;li&gt;We return &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;, then the current value if it exists, the provided &lt;code&gt;initialValue&lt;/code&gt; otherwise.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is no absolute best choice here; it depends on your need. But if you intend to distribute this hook to other people, your documentation should mention the choice you made.&lt;/p&gt;

&lt;p&gt;Here I chose the first way to do it and kept using the &lt;code&gt;initialValue&lt;/code&gt;.&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;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingValue&lt;/span&gt; &lt;span class="o"&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;{&lt;/span&gt;
    &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingValue&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is how you can do the other way:&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;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;existingValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingValue&lt;/span&gt; &lt;span class="o"&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;{&lt;/span&gt;
    &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;existingValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialValue&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our hook is complete, let’s see how to use it. We’ll create a component with an input, and use our hook to persist the value entered in the input in the local storage:&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="nx"&gt;Comp&lt;/span&gt; &lt;span class="o"&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="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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePersistedState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&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="s1"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&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;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;name&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;event&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;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don’t you find it pleasant to use our custom hook almost the same way we would use &lt;code&gt;useState&lt;/code&gt;? And that we hid in our hook most of the complexity to access the local storage so the developers using it won’t even be aware of it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Get an element’s size
&lt;/h2&gt;

&lt;p&gt;Another thing you might want to do is adapt your component's behavior depending on some element size. What would be cool is having a hook returning me the current width and height of any element I want in realtime. Let’s see how we can create such a hook.&lt;/p&gt;

&lt;p&gt;First, let’s put React aside for a minute and see how to get the size of a DOM element using plain JavaScript. Modern browsers offer an object &lt;code&gt;ResizeObserver&lt;/code&gt; that we can use for that. Its API is not the easiest to apprehend at first sight; for our use case, it consists in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating an instance of &lt;code&gt;ResizeObserver&lt;/code&gt;, passing it a callback executed each time one of the observed elements’ size has changed;&lt;/li&gt;
&lt;li&gt;Subscribe to observe each element we want to.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here is an example displaying in the console the width and height of an element each time it is modified:&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="nx"&gt;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#myElement&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// 1.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizeObserver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResizeObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entries&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;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentRect&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="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&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="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// 2.&lt;/span&gt;
&lt;span class="nx"&gt;resizeObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we loop through several &lt;code&gt;entries&lt;/code&gt; in the callback given to &lt;code&gt;RedizeObserver&lt;/code&gt;; this is because an observer can observe several elements, although we will only observe one here.&lt;/p&gt;

&lt;p&gt;Let’s come back to React: to know the size of a DOM element, we need first to get this element. We will need to use a &lt;em&gt;ref&lt;/em&gt;, via the &lt;code&gt;useRef&lt;/code&gt; hook. We saw how refs were useful in a previous lesson when dealing with async code; here is another common use case.&lt;/p&gt;

&lt;p&gt;By creating a ref with &lt;code&gt;useRef&lt;/code&gt; and passing it as the &lt;code&gt;ref&lt;/code&gt; prop of any HTML element rendered in your component, you can access the DOM element itself via &lt;code&gt;yourRef.current&lt;/code&gt;:&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="nx"&gt;inputRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;inputRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&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="c1"&gt;// logs “Hello!”&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;inputRef&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;input&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;inputRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Hello"&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;Here we need this ref to &lt;em&gt;observe&lt;/em&gt; it via our &lt;code&gt;ResizeObserver&lt;/code&gt;, so we will pass it as a parameter to our custom hook. Here is how we expect to use our hook; let’s name it &lt;code&gt;useElementSize&lt;/code&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="nx"&gt;Comp&lt;/span&gt; &lt;span class="o"&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;divRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&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;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useElementSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;divRef&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;style&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="c1"&gt;// Initial size&lt;/span&gt;
        &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&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;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// Makes the element resizeable&lt;/span&gt;
        &lt;span class="na"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;both&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// So it’s easier to resize&lt;/span&gt;
        &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1px solid #191a21&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="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;divRef&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;width&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;x&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;height&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;As you can see, we want our hook to return the width and height of the element pointed by the ref, and of course, we want these values to be updated when the user resized the element.&lt;/p&gt;

&lt;p&gt;So our hook &lt;code&gt;useElementSize&lt;/code&gt; has to keep the current element’s width and height in a local state, and returns them:&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="nx"&gt;useElementSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elementRef&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&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;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setHeight&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&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 last missing piece is to create the &lt;code&gt;ResizeObserver&lt;/code&gt; to update these local state values when the element is resized:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resizeObserver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ResizeObserver&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entries&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;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentRect&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setWidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setHeight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentRect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&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="p"&gt;})&lt;/span&gt;
  &lt;span class="nx"&gt;resizeObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Let’s disconnect the observer on unmount:&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;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resizeObserver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;elementRef&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that we subscribe to the observer in a &lt;code&gt;useEffect&lt;/code&gt; and that we &lt;em&gt;disconnect&lt;/em&gt; the observer when the component is unmounted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the user’s geolocation
&lt;/h2&gt;

&lt;p&gt;To conclude this lesson, let’s see another example of the browser’s API, which you can access very elegantly via a custom hook: the &lt;em&gt;geolocation&lt;/em&gt; API. As its name suggests, the idea is to get the user’s location, meaning the latitude and longitude of their position. Of course, this API can be used only on devices supporting it (mobile devices, modern browsers) and only if the user agreed to be geolocated.&lt;/p&gt;

&lt;p&gt;You can access this API using the &lt;code&gt;navigator.geolocation&lt;/code&gt; object, more precisely its method &lt;code&gt;getCurrentPosition&lt;/code&gt;. It accepts two callback parameters: one executed when the browser successfully returns the current position, the other when an error occurred, meaning the device does not support geolocation or the user didn’t authorize the page to get it.&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="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geolocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCurrentPosition&lt;/span&gt;&lt;span class="p"&gt;(&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;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&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="nx"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latitude&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="nx"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;longitude&lt;/span&gt;&lt;span class="p"&gt;),&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Impossible to get current position&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To return the user’s current position via a custom hook, we will apply the same pattern we used in the previous two examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep a local state with the position.&lt;/li&gt;
&lt;li&gt;Call the geolocation API in a &lt;code&gt;useEffect&lt;/code&gt; to update the state.&lt;/li&gt;
&lt;li&gt;Return its values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll introduce a small difference, though: since we want to handle the error case, we will also return a &lt;code&gt;status&lt;/code&gt; attribute indicating if we are waiting for the position (&lt;code&gt;'pending'&lt;/code&gt;), if we fetched it successfully (&lt;code&gt;'success'&lt;/code&gt;), or if an error occurred (&lt;code&gt;'error'&lt;/code&gt;).&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="nx"&gt;useGeolocation&lt;/span&gt; &lt;span class="o"&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="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;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setStatus&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLatitude&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&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;longitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLongitude&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;geolocation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getCurrentPosition&lt;/span&gt;&lt;span class="p"&gt;(&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="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setLatitude&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="nx"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setLongitude&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="nx"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;longitude&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;err&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="nf"&gt;log&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="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&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="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="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longitude&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 the components using this hook, we can then use the returned &lt;code&gt;status&lt;/code&gt; attribute to decide what to display:&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;const&lt;/span&gt; &lt;span class="nx"&gt;Comp&lt;/span&gt; &lt;span class="o"&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="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;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;longitude&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useGeolocation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Waiting for geolocation…&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;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;success&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&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;Your location: (&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;latitude&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;longitude&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;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Have you authorized me to access your geolocation?&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;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With these three custom hooks examples to use the browser’s APIs, you probably notice that the recipe is very similar. With this in mind, you are now able to write many custom hooks. It doesn’t mean that they will solve every problem, but they are an additional tool you can use to make your code cleaner, especially when you want to access features provided by the browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What are your favorite custom hooks?&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you liked this post, I talk a lot more about React and hooks in my new course &lt;a href="https://useeffect.dev/?ref=dev.to"&gt;useEffect.dev&lt;/a&gt;. Its goal is to help you understand how they work, how to debug them, and how to solve common problems they can cause.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can also &lt;a href="https://twitter.com/scastiel" rel="noopener noreferrer"&gt;follow me on Twitter (@scastiel)&lt;/a&gt;, where I regularly post about React, hooks, frontend in general, and other subjects 😉&lt;/em&gt;&lt;/p&gt;

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