<?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: Francisco Moretti</title>
    <description>The latest articles on DEV Community by Francisco Moretti (@franciscomoretti).</description>
    <link>https://dev.to/franciscomoretti</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%2F734666%2F0e082ef5-cd71-41df-9f78-9eb2b9aafeb9.png</url>
      <title>DEV Community: Francisco Moretti</title>
      <link>https://dev.to/franciscomoretti</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/franciscomoretti"/>
    <language>en</language>
    <item>
      <title>Fix mobile keyboard overlap with VisualViewport</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Mon, 25 Aug 2025 10:08:58 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/fix-mobile-keyboard-overlap-with-visualviewport-3a4a</link>
      <guid>https://dev.to/franciscomoretti/fix-mobile-keyboard-overlap-with-visualviewport-3a4a</guid>
      <description>&lt;h1&gt;
  
  
  Fix mobile keyboard overlap with dvh (no observers)
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Edited 2025-08-26&lt;/strong&gt;: The previous version recommended syncing &lt;code&gt;visualViewport.height&lt;/code&gt; to a CSS variable with an observer. That was wrong for this use case. The actual fix in Sparka is to rely on dynamic viewport units (&lt;code&gt;dvh&lt;/code&gt;). Drop the observer and use Tailwind’s &lt;code&gt;h-dvh&lt;/code&gt;. See the discussion: &lt;a href="https://github.com/FranciscoMoretti/sparka/issues/14" rel="noopener noreferrer"&gt;https://github.com/FranciscoMoretti/sparka/issues/14&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Bottom‑pinned chat inputs can get buried when the mobile keyboard opens. Modern browsers expose dynamic viewport units that account for the on‑screen keyboard. Use &lt;code&gt;dvh&lt;/code&gt; and keep the layout simple.&lt;/p&gt;

&lt;p&gt;This is used in &lt;a href="https://sparka.ai" rel="noopener noreferrer"&gt;Sparka.ai&lt;/a&gt;, you can check the code &lt;a href="https://github.com/FranciscoMoretti/sparka" rel="noopener noreferrer"&gt;here&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%2F840wvq2gosy5dkh860hz.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%2F840wvq2gosy5dkh860hz.png" alt="Update height with mobile virtual keyboard" width="800" height="657"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;h-dvh&lt;/code&gt;&lt;/strong&gt; (Tailwind) for any full‑height containers.&lt;/li&gt;
&lt;li&gt;No observers, no CSS variables, no JS.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/chat.tsx (snippet)&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex h-dvh min-w-0 flex-col"&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;/* 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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need a single element to fill the screen:&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;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"h-dvh"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dvh&lt;/code&gt; reflects the visible viewport height, adjusting when the keyboard shows.&lt;/li&gt;
&lt;li&gt;Works on iOS Safari and Android (recent versions). If you must support very old iOS, feature‑test and fall back to &lt;code&gt;100vh&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Viewport meta
&lt;/h2&gt;

&lt;p&gt;If you also want Android/Chromium to resize content with the keyboard, set &lt;code&gt;interactiveWidget: 'resizes-content'&lt;/code&gt;. This is additive to using &lt;code&gt;dvh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/layout.tsx (snippet)&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;viewport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;maximumScale&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="c1"&gt;// Disable auto-zoom on mobile Safari&lt;/span&gt;
  &lt;span class="na"&gt;interactiveWidget&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resizes-content&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&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;Details: &lt;a href="https://developer.chrome.com/docs/web-platform/viewport-meta/#interactive-widget" rel="noopener noreferrer"&gt;https://developer.chrome.com/docs/web-platform/viewport-meta/#interactive-widget&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In the wild
&lt;/h2&gt;

&lt;p&gt;This is what &lt;a href="//Sparka.ai"&gt;Sparka.ai&lt;/a&gt; ships. For context, see the issue: &lt;a href="https://github.com/FranciscoMoretti/sparka/issues/14" rel="noopener noreferrer"&gt;https://github.com/FranciscoMoretti/sparka/issues/14&lt;/a&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>ui</category>
    </item>
    <item>
      <title>Deep Research UIs - Perplexity vs. Manus vs. ChatGPT vs. Gemini</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Tue, 29 Apr 2025 18:37:53 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/deep-research-uis-perplexity-vs-manus-vs-chatgpt-vs-gemini-5cc2</link>
      <guid>https://dev.to/franciscomoretti/deep-research-uis-perplexity-vs-manus-vs-chatgpt-vs-gemini-5cc2</guid>
      <description>&lt;p&gt;Building chat applications often involves tackling complex user interfaces. This is especially true for &lt;strong&gt;"deep research"&lt;/strong&gt; tasks where &lt;strong&gt;information density is high&lt;/strong&gt;. Components like &lt;strong&gt;reports&lt;/strong&gt;, &lt;strong&gt;research steps&lt;/strong&gt;, &lt;strong&gt;sources&lt;/strong&gt;, and &lt;strong&gt;source citations&lt;/strong&gt; create unique UI challenges. This post looks at how four AI chat tools (&lt;strong&gt;Perplexity&lt;/strong&gt;, &lt;strong&gt;Manus&lt;/strong&gt;, &lt;strong&gt;ChatGPT&lt;/strong&gt;, and &lt;strong&gt;Gemini&lt;/strong&gt;) approach these challenges. By checking out their UI decisions and trade-offs, particularly using Vercel's AI SDK message schema, we can find valuable lessons for designing effective research-focused chat interfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vercel AI SDK UI message schema
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://sdk.vercel.ai/" rel="noopener noreferrer"&gt;Vercel AI SDK&lt;/a&gt; provides a structured approach for building chat interfaces through its message schema. Understanding this schema helps us analyze how different research tools implement their interfaces.&lt;/p&gt;

&lt;p&gt;A simplified version of the Vercel AI SDK UI message schema includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;UiMessage&lt;/strong&gt;: A message in the chat. It represents the entire message unit from either a user or the AI assistant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Annotation&lt;/strong&gt;: Information tied to a message that can be streamed before the response is written. These are useful for showing intermediate research steps, thinking processes, or source gathering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UiMessagePart&lt;/strong&gt;: A piece of a message like text, code, or an image. Complex research responses often combine multiple part types to create rich information displays.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified relationship between UiMessage, Annotation, and UiMessagePart&lt;/span&gt;

&lt;span class="c1"&gt;// UiMessagePart represents different parts of the message content&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TextUIPart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;text&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ToolInvocationUIPart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tool-invocation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;toolInvocation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ToolInvocation&lt;/span&gt; &lt;span class="c1"&gt;// Tool invocation is a discriminated union of tool info (results and calls)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// UiMessage represents a complete message unit in the chat&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UiMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;assistant&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;
  &lt;span class="na"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;TextUIPart&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ReasoningUIPart&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;ToolInvocationUIPart&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;SourceUIPart&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;FileUIPart&lt;/span&gt;
    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;StepStartUIPart&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="c1"&gt;// A message consists of multiple parts. A discriminated union of parts types&lt;/span&gt;
  &lt;span class="nx"&gt;annotations&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;JSONValue&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="c1"&gt;// A discriminated union of annotation types&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When implementing a research interface, developers can use these components to create different arrangements of information. Each research tool we look at makes different choices about how to structure and present information using similar underlying components.&lt;/p&gt;

&lt;p&gt;The complexity comes from balancing &lt;em&gt;information density&lt;/em&gt; with &lt;em&gt;usability&lt;/em&gt;. Research interfaces must present substantial information without overwhelming users. How each tool handles this balance is a core part of our analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Perplexity UI breakdown
&lt;/h2&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%2Ftyxypz41ubvflpeu2cdc.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%2Ftyxypz41ubvflpeu2cdc.png" alt="Perplexity UI - 1" width="800" height="604"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Perplexity&lt;/strong&gt; uses a simple approach for research presentation. Their interface emphasizes &lt;strong&gt;readability&lt;/strong&gt; while keeping the contextual richness needed for deep research tasks.&lt;/p&gt;

&lt;p&gt;The main answer or "report" appears as a standard message in the chat. It has no special formatting to distinguish it from other messages. This simplifies the UI but might make it harder for users to spot the final answer among regular conversation messages.&lt;/p&gt;

&lt;p&gt;What stands out most about Perplexity is its treatment of &lt;strong&gt;sources&lt;/strong&gt;. Sources appear in &lt;strong&gt;many places&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inline citations&lt;/strong&gt; within the text (numbered references)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;dedicated sources panel&lt;/strong&gt; at the top of each research response + available as a "Sources" tab&lt;/li&gt;
&lt;/ol&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%2Fsl58rlrkec8e60nl1ne1.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%2Fsl58rlrkec8e60nl1ne1.png" alt="Perplexity UI - 2" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This dual approach gives sources high visual prominence while maintaining the flow of the main content. Implementing this in the Vercel AI SDK schema might use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A primary &lt;code&gt;UiMessagePart&lt;/code&gt; for the text content&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Annotation&lt;/code&gt; components to hold the source references&lt;/li&gt;
&lt;li&gt;Custom styling to visually link inline citations with their corresponding entries&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key benefits:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Builds &lt;strong&gt;user trust&lt;/strong&gt; through clear attribution&lt;/li&gt;
&lt;li&gt;Makes &lt;strong&gt;fact-checking easy&lt;/strong&gt; with prominent sources&lt;/li&gt;
&lt;li&gt;Keeps &lt;strong&gt;content flow clean&lt;/strong&gt; with numbered references&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Interface can become &lt;strong&gt;cluttered&lt;/strong&gt; with many sources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perplexity also includes &lt;strong&gt;suggested follow-up questions&lt;/strong&gt; at the end of responses. They serve as navigation aids and subtle guides toward productive use of the tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Manus UI breakdown
&lt;/h2&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%2F1909qd7bgoubjs83mhb7.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%2F1909qd7bgoubjs83mhb7.png" alt="Manus UI - 1" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manus&lt;/strong&gt; takes a different approach to research interfaces. It makes the &lt;strong&gt;research process&lt;/strong&gt; itself a central part of the user experience. Their interface splits attention between the research output and the research method.&lt;/p&gt;

&lt;p&gt;"&lt;strong&gt;Research Steps&lt;/strong&gt;" occupy a significant portion of the screen real estate. This makes the process transparent to users. Unlike interfaces focusing solely on the result, Manus treats the journey as equally important.&lt;/p&gt;

&lt;p&gt;One notable feature is how Manus handles &lt;strong&gt;planning&lt;/strong&gt;. The interface displays an explicit research plan but &lt;em&gt;doesn't pause for user confirmation or feedback&lt;/em&gt;. It does ask clarifying questions when needed, though. This can create a smoother experience when it gets things right, but it depends heavily on the quality of the decision making.&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%2F22twq9vgd72yjyeyvyxw.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%2F22twq9vgd72yjyeyvyxw.png" alt="Manus UI - 2" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Manus generates &lt;strong&gt;multiple documents&lt;/strong&gt; during research, providing info about intermediate steps. While informative, this approach can reduce the visual importance of the primary document requested by the user. The final output becomes just one of several documents, not the clear focal point.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;2-panel layout&lt;/strong&gt; with a "computer view" on the right aims to show how the agent works in real time. This provides transparency but uses significant screen space. Its value diminishes during routine web browsing, which makes up most of the research process.&lt;/p&gt;

&lt;p&gt;Using the Vercel AI SDK schema, this approach might be implemented with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple &lt;code&gt;UiMessage&lt;/code&gt; components for different document types&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Annotation&lt;/code&gt; objects to represent each research step&lt;/li&gt;
&lt;li&gt;A mechanism for &lt;code&gt;Annotation&lt;/code&gt; objects representing tool calls to fetch and show the correct 'computer view' content for that step&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key benefits:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Process transparency&lt;/strong&gt; builds user trust&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Research journey visibility&lt;/strong&gt; helps understanding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time insights&lt;/strong&gt; into AI thinking&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Interface feels &lt;strong&gt;busier&lt;/strong&gt; and potentially overwhelming&lt;/li&gt;
&lt;li&gt;Requires &lt;strong&gt;complex state management&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ChatGPT UI breakdown
&lt;/h2&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%2Fx6n4ks01w3ur8tt1fdib.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%2Fx6n4ks01w3ur8tt1fdib.png" alt="ChatGPT UI - 1" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChatGPT&lt;/strong&gt; takes a more balanced approach to research interfaces. It focuses on &lt;strong&gt;user control&lt;/strong&gt; while keeping a clean presentation. It starts with a &lt;strong&gt;plan as a &lt;code&gt;TextUIPart&lt;/code&gt;&lt;/strong&gt; that needs feedback via a normal conversation message. This offers familiarity, practicality, and flexibility.&lt;/p&gt;

&lt;p&gt;Before conducting research, ChatGPT typically presents a &lt;strong&gt;research plan&lt;/strong&gt; as a regular text message part and &lt;em&gt;asks for user feedback&lt;/em&gt;. This creates a pause point that gives users agency in the research process. They can approve, modify, or redirect the plan before execution starts.&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;research steps&lt;/strong&gt;, ChatGPT initially displays them in an &lt;strong&gt;expandable sheet&lt;/strong&gt; that updates in real time. This keeps the research process visible without dominating the interface. Once research finishes, these steps collapse into a compact component within the conversation. This creates a clean history that can be referenced if needed.&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%2F5gahylb0qzc2h5j3gaq3.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%2F5gahylb0qzc2h5j3gaq3.png" alt="ChatGPT UI - 2" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final report appears as a &lt;strong&gt;&lt;code&gt;ToolInvocation&lt;/code&gt;&lt;/strong&gt; piece of the regular answer, styled like a card. This works well for shorter reports but can become cluttered for longer, more complex responses. The lack of dedicated document viewing capabilities limits the reading experience for extended content.&lt;/p&gt;

&lt;p&gt;Using the Vercel AI SDK schema, this approach might be implemented with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A normal text &lt;code&gt;UiMessagePart&lt;/code&gt; for the initial plan&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Annotation&lt;/code&gt; objects for the research steps that can toggle between expanded and collapsed states&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;ToolInvocation&lt;/code&gt; part for the final report content&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key benefits:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;User control&lt;/strong&gt; through plan feedback&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean interface&lt;/strong&gt; maintains familiarity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collapsible research steps&lt;/strong&gt; reduce clutter&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Limited support for &lt;strong&gt;longer reports&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Reading experience &lt;strong&gt;suffers&lt;/strong&gt; in standard chat format&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Gemini UI breakdown
&lt;/h2&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%2Fmnhjul7kjr3wm4nu7p9u.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%2Fmnhjul7kjr3wm4nu7p9u.png" alt="Gemini UI - 1" width="800" height="442"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini&lt;/strong&gt; offers a distinct approach to research interfaces. It has a strong focus on the &lt;strong&gt;final output&lt;/strong&gt; while still providing process transparency. The interface uses a &lt;strong&gt;two-panel design&lt;/strong&gt; that separates the conversation from research details.&lt;/p&gt;

&lt;p&gt;The planning approach in Gemini appears as a special UI element (a tool result), though the core content is primarily text with minimal formatting. Plan acceptance is handled through a button that creates a standard message response, rather than specialized UI controls.&lt;/p&gt;

&lt;p&gt;Research progress updates appear in the secondary panel labeled "&lt;strong&gt;Thoughts&lt;/strong&gt;". This provides insight into the ongoing process and creates clear separation between the conversation and the research mechanics.&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%2Fvf410v3a8jc41jl48qbl.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%2Fvf410v3a8jc41jl48qbl.png" alt="Gemini UI - 2" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When research completes, Gemini displays the document (report) in the secondary panel. Research steps are positioned below the report content. This creates a hierarchy that &lt;strong&gt;emphasizes results over process&lt;/strong&gt;. However, this arrangement requires significant scrolling to review the research steps, making them less accessible than in other interfaces.&lt;/p&gt;

&lt;p&gt;Sources receive some visual emphasis in the Gemini UI but remain integrated within the report. This approach creates a cleaner appearance but may reduce source prominence compared to interfaces like Perplexity that highlight attribution more explicitly.&lt;/p&gt;

&lt;p&gt;Using the Vercel AI SDK schema, this approach might be implemented with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;ToolInvocation&lt;/code&gt; for the initial plan&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Annotation&lt;/code&gt; objects for the "Thoughts" that update during research&lt;/li&gt;
&lt;li&gt;Another &lt;code&gt;ToolInvocation&lt;/code&gt; for the final report&lt;/li&gt;
&lt;li&gt;A separate UI component outside the main message thread for displaying both the report and research steps&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key benefits:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clear focus&lt;/strong&gt; on final output&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean conversation&lt;/strong&gt;, separate from research details&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good separation&lt;/strong&gt; of conversation and supporting information&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Drawbacks:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reduced visibility of &lt;strong&gt;research steps&lt;/strong&gt; after completion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sources less prominent&lt;/strong&gt; than other approaches&lt;/li&gt;
&lt;li&gt;Requires managing a &lt;strong&gt;more complex layout&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Comparing these four tools reveals a spectrum of UI strategies for deep research. Perplexity prioritizes source prominence and readability, Manus emphasizes process transparency, ChatGPT balances user control with a clean interface, and Gemini focuses strongly on the final report within a separated layout. Each approach involves trade-offs between information density, process visibility, user control, and interface complexity. Understanding these design choices and their implications offers valuable insights for developers building AI-powered research tools.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>ui</category>
    </item>
    <item>
      <title>What tests to write for React</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Fri, 04 Apr 2025 19:06:11 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/what-tests-to-write-for-react-56ki</link>
      <guid>https://dev.to/franciscomoretti/what-tests-to-write-for-react-56ki</guid>
      <description>&lt;p&gt;This article explores what tests you should be writing for your web applications. Inspired by Igor Luchenkov's insightful talk at React Advanced London and drawing on wisdom from industry experts, we'll look at how testing strategies have evolved from the classic Test Pyramid to the more nuanced Testing Trophy approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  The evolution of testing strategies
&lt;/h2&gt;

&lt;p&gt;For years, the dominant testing strategy in software development was &lt;strong&gt;Martin Fowler's Test Pyramid&lt;/strong&gt;. This model suggested having many unit tests at the bottom, fewer integration tests in the middle, and even fewer end-to-end tests at the top.&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%2Fqkua4fapggf18qeiyil6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqkua4fapggf18qeiyil6.jpg" alt="Testing Pyramid" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The pyramid made sense when it was created. Back then, running tests through a UI was painfully slow, expensive to set up, and notoriously brittle. A small change in the UI could break dozens of tests, requiring hours of maintenance.&lt;/p&gt;

&lt;p&gt;But times have changed. Modern testing tools have evolved dramatically, making some of the pyramid's assumptions outdated. This led &lt;strong&gt;Kent C. Dodds to propose an updated model - the Testing Trophy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Inspired by Guillermo Rauch's tweet:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;strong&gt;Write tests. Not too many. Mostly integration.&lt;/strong&gt;"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The trophy flips the script by placing &lt;strong&gt;integration tests as the largest, most important section&lt;/strong&gt;. It also adds a new foundation: static tests. This shift reflects how testing tools and web development have evolved in recent years.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Testing Trophy
&lt;/h2&gt;

&lt;p&gt;The Testing Trophy consists of four distinct layers:&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%2Fe6mzd2cmf5v4btzt7bpz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe6mzd2cmf5v4btzt7bpz.jpg" alt="Testing Trophy showing the four types of tests" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The types of tests are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;End-to-End (Full app)&lt;/li&gt;
&lt;li&gt;Integration (Component interactions)&lt;/li&gt;
&lt;li&gt;Unit (Logic in isolation)&lt;/li&gt;
&lt;li&gt;Static (Typing, linting, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Static (The Base)
&lt;/h3&gt;

&lt;p&gt;These aren't traditional tests that run your code. Instead, they check your code without executing it. &lt;strong&gt;TypeScript, ESLint, and Biome&lt;/strong&gt; fall into this category. They catch typos, type errors, and enforce coding standards with minimal effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unit (The Small Base)
&lt;/h3&gt;

&lt;p&gt;Unit tests verify that individual functions work correctly in isolation. They're fast and focused but don't tell you if your components work well together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration (The Trophy Cup)
&lt;/h3&gt;

&lt;p&gt;This is the &lt;strong&gt;largest section of the trophy for good reason&lt;/strong&gt;. Integration tests verify that multiple units work together correctly. In web development, this often means testing how components interact without mocking every dependency.&lt;/p&gt;

&lt;h3&gt;
  
  
  End-to-End (The Top)
&lt;/h3&gt;

&lt;p&gt;These comprehensive tests simulate real user behavior, often through a browser. They're the most confidence-inspiring but also the slowest and most complex to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why integration tests deserve the spotlight
&lt;/h2&gt;

&lt;p&gt;In 2016, Guillermo Rauch (creator of Socket.io and founder of Vercel) tweeted a simple yet profound message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"&lt;strong&gt;Write tests. Not too many. Mostly integration.&lt;/strong&gt;"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This wasn't just a catchy phrase - it's solid advice backed by practical experience. Integration tests strike the perfect balance between confidence and cost.&lt;/p&gt;

&lt;p&gt;Unit tests are quick to write and run, but they don't tell you if your components actually work together. End-to-end tests give you the most confidence but are slow and high-maintenance. Integration tests hit the sweet spot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They provide &lt;strong&gt;high confidence that your features work&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;They run reasonably fast&lt;/li&gt;
&lt;li&gt;They're &lt;strong&gt;more resilient to refactoring than unit tests&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;They catch real issues that users might encounter&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kent C. Dodds put it brilliantly when he said: "&lt;strong&gt;The more your tests resemble the way your software is used, the more confidence they can give you.&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;Integration tests mirror how users actually experience your software better than isolated unit tests, making them more valuable for the time invested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small examples of each test type
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Static Tests
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TypeScript will catch this error without running any code&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addNumbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;number&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;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;addNumbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Error: Argument of type 'string' is not assignable to parameter of type 'number'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ESLint rules can catch potential bugs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ESLint no-unused-vars rule&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateTotal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subtotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tax&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;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;subtotal&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;tax&lt;/span&gt;
  &lt;span class="c1"&gt;// Error: 'total' is assigned a value but never used&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Unit Tests
&lt;/h3&gt;

&lt;p&gt;Using Vitest to test a simple utility function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// utils.js&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;formatPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;amount&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;`$&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// utils.test.js&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;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&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;vitest&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;formatPrice&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;./utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;formats the price with dollar sign and two decimal places&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;formatPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$10.00&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;formatPrice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;10.5&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$10.50&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;h3&gt;
  
  
  Integration Tests
&lt;/h3&gt;

&lt;p&gt;Testing a form component with React Testing Library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// LoginForm.test.jsx&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;render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fireEvent&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;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;userEvent&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;@testing-library/user-event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;LoginForm&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;./LoginForm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;submits the form with user credentials&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nf"&gt;render&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;LoginForm&lt;/span&gt; &lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="o"&gt;=&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="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/email/i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/password/i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&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;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sr"&gt;/log in/i&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;

  &lt;span class="nf"&gt;expect&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="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;password123&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;h3&gt;
  
  
  End-to-End Tests
&lt;/h3&gt;

&lt;p&gt;Using Playwright to test the login flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// login.spec.js&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;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&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;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user can log in&lt;/span&gt;&lt;span class="dl"&gt;'&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;page&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="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[name="email"]&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;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[name="password"]&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;password123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button[type="submit"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Verify user was redirected to dashboard&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toContainText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Welcome back&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;h2&gt;
  
  
  Recommended Frameworks for each test type
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Static Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt;: Adds type-checking to JavaScript&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ESLint&lt;/strong&gt;: Identifies problematic patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Biome&lt;/strong&gt;: Modern linter and formatter&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Unit Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vitest&lt;/strong&gt;: Fast unit test runner with great React support&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Integration Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vitest&lt;/strong&gt;: Fast unit test runner with great React support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Storybook&lt;/strong&gt;: Component workshop for visual testing and development&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  End-to-End Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Playwright&lt;/strong&gt;: Modern E2E testing tool with cross-browser support&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Balancing your test portfolio for maximum value
&lt;/h2&gt;

&lt;p&gt;While the Testing Trophy offers a good template, your ideal test mix depends on your project. Consider these factors when deciding:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Application complexity&lt;/strong&gt;: More complex apps need more integration and E2E tests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team size&lt;/strong&gt;: Larger teams benefit from more tests as communication overhead increases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stability requirements&lt;/strong&gt;: Critical systems need more test coverage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development phase&lt;/strong&gt;: Early-stage products might focus on fewer, high-value tests&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Remember these practical tips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with static type checking and linting&lt;/strong&gt; - they're the cheapest way to catch errors&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't aim for 100% test coverage&lt;/strong&gt;; it often leads to testing implementation details&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on user workflows&lt;/strong&gt; rather than implementation details&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If a test is hard to write, it might be telling you something about your code design&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By following the Testing Trophy approach and adjusting for your specific needs, you'll build a test suite that gives you confidence without slowing down development.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>react</category>
      <category>bestpractices</category>
    </item>
    <item>
      <title>Next.js Authentication Best Practices in 2025</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Tue, 01 Apr 2025 08:52:16 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/nextjs-authentication-best-practices-in-2025-o00</link>
      <guid>https://dev.to/franciscomoretti/nextjs-authentication-best-practices-in-2025-o00</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: This article updates &lt;a href="https://www.franciscomoretti.com/blog/nextjs-authentication-best-practices" rel="noopener noreferrer"&gt;previous authentication guide&lt;/a&gt;, which contained outdated recommendations. Next.js has since changed its authentication guidance, particularly in light of the &lt;a href="https://nextjs.org/blog/cve-2025-29927" rel="noopener noreferrer"&gt;CVE-2025-29927&lt;/a&gt; security vulnerability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This articles containes a simplified version of the official &lt;a href="https://nextjs.org/docs/app/building-your-application/authentication" rel="noopener noreferrer"&gt;Next.js authentication guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Next.js Authentication Guidance Has Changed
&lt;/h2&gt;

&lt;p&gt;The Next.js team has significantly updated their authentication recommendations. The most important change: &lt;strong&gt;middleware is no longer considered safe for authentication&lt;/strong&gt;. Instead, the new approach focuses on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using Data Access Layers (DAL)&lt;/li&gt;
&lt;li&gt;Centralizing authentication logic&lt;/li&gt;
&lt;li&gt;Keeping auth checks close to data access&lt;/li&gt;
&lt;li&gt;Implementing proper role-based access in Server Components&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's explore these updated best practices in detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data Access Layers: The New Best Practice
&lt;/h2&gt;

&lt;p&gt;A Data Access Layer centralizes all data access logic, including authentication checks. This approach provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistent security across your application&lt;/li&gt;
&lt;li&gt;Protection against unauthorized data access&lt;/li&gt;
&lt;li&gt;Clear separation between authorization and data fetching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a simple example of a DAL implementation:&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;// app/lib/dal.ts&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;cache&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;cookies&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/headers&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;redirect&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/navigation&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;verifySession&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cookieStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cookies&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;sessionToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cookieStore&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;session-token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sessionToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Verify token with your auth provider&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&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;validateToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sessionToken&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;session&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid session&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="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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&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;verifySession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get user data using the session&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;users&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;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;email&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="p"&gt;})&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="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="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to fetch user&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="kc"&gt;null&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Problem with Middleware for Authentication
&lt;/h2&gt;

&lt;p&gt;Middleware in Next.js executes before the application renders. While this appears ideal for authentication, there are significant limitations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Static Routes Vulnerability&lt;/strong&gt;: Middleware doesn't completely protect statically generated routes, as the content is already built&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited Context&lt;/strong&gt;: Middleware has limited access to the application's full context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexities with Static Generation&lt;/strong&gt;: Auth logic in middleware can conflict with Next.js's static optimization&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Proximity Principle: Auth Checks Close to Data
&lt;/h2&gt;

&lt;p&gt;The proximity principle is now more important than ever. Keep authentication checks as close as possible to where sensitive data is accessed:&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;// Direct data access with auth check&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;fetchSensitiveUserData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&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;verifySession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unauthorized&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="c1"&gt;// Fetch and return data only after verifying&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&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;h2&gt;
  
  
  Server Components and Role-Based Authentication
&lt;/h2&gt;

&lt;p&gt;Server Components provide a powerful way to implement role-based access control:&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;// app/admin/page.tsx&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;verifySession&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;@/app/lib/dal&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;redirect&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/navigation&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;default&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;AdminDashboard&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;session&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;verifySession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&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="c1"&gt;// User is logged in but not an admin&lt;/span&gt;
    &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="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;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Admin Dashboard&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;/* Admin-only 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;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Authentication in Layouts: Proceed with Caution
&lt;/h2&gt;

&lt;p&gt;Due to partial rendering in Next.js, authentication in layouts requires special care:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Layouts don't re-render on navigation within their subtree&lt;/li&gt;
&lt;li&gt;Auth checks may not run on every route change&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of relying on layout-level checks, perform authentication checks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In your Data Access Layer&lt;/li&gt;
&lt;li&gt;In page components&lt;/li&gt;
&lt;li&gt;Directly in data fetching functions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Securing Data Access and Mutations
&lt;/h2&gt;

&lt;p&gt;Server Actions and data transfer operations need special protection as they're entry points to your application's data. Implement these practices for robust security:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Always authenticate in Server Actions&lt;/strong&gt;: Server Actions can be called directly from clients, so validate sessions before performing operations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use DTOs to control data exposure&lt;/strong&gt;: Never return complete data objects to clients - expose only what's necessary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add role-based permissions&lt;/strong&gt;: Check not just authentication but authorization for specific operations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's how to secure a Server Action:&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;// app/lib/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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;verifySession&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;@/app/lib/dal&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;updateUserProfile&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="c1"&gt;// Auth check before any operation&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&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;verifySession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Get only the fields you need&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&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="c1"&gt;// Validate input data&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&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;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Invalid input&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="c1"&gt;// Update only for authorized users&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&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="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 data retrieval, use DTOs to limit exposure of sensitive data:&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;// app/lib/dto.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;server-only&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;getUser&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;@/app/lib/dal&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;getUserProfileDTO&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&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="c1"&gt;// Authentication check&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentUser&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;getUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

  &lt;span class="c1"&gt;// Authorization check&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;admin&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;isSelf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isAdmin&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isSelf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

  &lt;span class="c1"&gt;// Get the user data&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userProfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;select&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&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;role&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="c1"&gt;// Return only appropriate fields&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userProfile&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userProfile&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="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isSelf&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isAdmin&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;userProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By consistently implementing these patterns at every data entry and exit point, you maintain a secure boundary around your application's data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context Providers: Client-Side Limitations
&lt;/h2&gt;

&lt;p&gt;React Context providers work for auth state due to Next.js's component interleaving architecture. However, there's an important limitation: React Context is not supported in Server Components and only works with Client Components.&lt;/p&gt;

&lt;p&gt;When using a Context provider for authentication:&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;// app/providers.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;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&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="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&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;AuthContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AuthProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="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="c1"&gt;// Fetch user on client&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;loadUserFromAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;loadUserFromAPI&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;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&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="nx"&gt;children&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;AuthContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useAuth&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="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AuthContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach works with Client Components that need authentication state:&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;// app/components/profile.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;useAuth&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;../providers&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;Profile&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useAuth&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;loading&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;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&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;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Please log in&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="k"&gt;return&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;Welcome, &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, any Server Components inside your component tree will be rendered on the server first, before the client-side context is available. This means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Server Components cannot access context values&lt;/li&gt;
&lt;li&gt;They cannot conditionally render based on authentication state from context&lt;/li&gt;
&lt;li&gt;You'll need to use your DAL for any auth checks in Server Components&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is why a dual approach is recommended:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use DAL for Server Components and data access&lt;/li&gt;
&lt;li&gt;Use Context for Client Components that need real-time auth state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that sharing sensitive session information between server and client components requires careful handling to prevent security issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Best Practices Summary
&lt;/h2&gt;

&lt;p&gt;Protect your Next.js app at multiple layers for maximum security:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data layer&lt;/strong&gt;: Add auth checks directly in your Data Access Layer functions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route level&lt;/strong&gt;: Check authentication in page components&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI elements&lt;/strong&gt;: Hide sensitive components when users aren't authenticated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server actions&lt;/strong&gt;: Verify authentication in all mutation functions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For best results:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Replace middleware with a Data Access Layer for auth checks&lt;/li&gt;
&lt;li&gt;Put auth checks as close as possible to where data is accessed&lt;/li&gt;
&lt;li&gt;Be careful with layout-level authentication&lt;/li&gt;
&lt;li&gt;Add proper auth verification in Server Components and Actions&lt;/li&gt;
&lt;li&gt;Use Data Transfer Objects to control exactly what data gets exposed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This multi-layered approach creates apps that stay secure and easy to maintain without adding unnecessary complexity.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/app/building-your-application/authentication" rel="noopener noreferrer"&gt;Authentication - Next.js Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nextjs</category>
    </item>
    <item>
      <title>Automate Your Job Search: Scraping 400+ LinkedIn Jobs with Python</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Mon, 20 Jan 2025 19:49:22 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/automate-your-job-search-scraping-400-linkedin-jobs-with-python-47c0</link>
      <guid>https://dev.to/franciscomoretti/automate-your-job-search-scraping-400-linkedin-jobs-with-python-47c0</guid>
      <description>&lt;p&gt;The average job seeker spends &lt;a href="https://www.linkedin.com/pulse/how-many-hours-per-week-should-one-dedicate-job-search-bob-mcintosh/" rel="noopener noreferrer"&gt;11 hours per week searching for jobs&lt;/a&gt;, according to LinkedIn. For tech roles, it's even worse, you're dealing with hundreds of postings across multiple platforms. When my partner started her job search, she was spending hours daily just scrolling through LinkedIn. There had to be a better way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;For Web developers, the market is overwhelming. A single search for "Frontend Developer" in London returned 401 results. Each posting requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5 seconds to review the title&lt;/li&gt;
&lt;li&gt;3-4 clicks to view details&lt;/li&gt;
&lt;li&gt;30-60 seconds to scan requirements&lt;/li&gt;
&lt;li&gt;Manual copy-pasting to track interesting roles&lt;/li&gt;
&lt;li&gt;Constant tab switching and back-navigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For 401 jobs, that's hours of pure mechanical work!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Automation Pipeline
&lt;/h2&gt;

&lt;p&gt;I built a three-step automation pipeline that cut the process down to 10 minutes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scrape job data using Python&lt;/li&gt;
&lt;li&gt;Filter in bulk using Google Sheets&lt;/li&gt;
&lt;li&gt;Review only the most promising matches&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 1: Smart Scraping
&lt;/h3&gt;

&lt;p&gt;I used &lt;a href="https://github.com/Bunsly/JobSpy" rel="noopener noreferrer"&gt;JobSpy&lt;/a&gt; as the base and built &lt;a href="https://github.com/FranciscoMoretti/jobsparser" rel="noopener noreferrer"&gt;JobsParser&lt;/a&gt; to handle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLI&lt;/li&gt;
&lt;li&gt;Rate limiting (to avoid LinkedIn blocks)&lt;/li&gt;
&lt;li&gt;Retry logic for failed requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's how to run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;jobsparser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Bonus:&lt;/strong&gt; search manually on LinkedIn the number of results for your search term and use it for the &lt;code&gt;--results-wanted&lt;/code&gt; parameter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jobsparser &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--search-term&lt;/span&gt; &lt;span class="s2"&gt;"Frontend Developer"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s2"&gt;"London"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--site&lt;/span&gt; linkedin &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--results-wanted&lt;/span&gt; 200 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--distance&lt;/span&gt; 25 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--job-type&lt;/span&gt; fulltime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;jobsparser&lt;/code&gt; is not in your path, you can run it as a module directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; jobsparser &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--search-term&lt;/span&gt; &lt;span class="s2"&gt;"Frontend Developer"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s2"&gt;"London"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--site&lt;/span&gt; linkedin &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--results-wanted&lt;/span&gt; 200 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--distance&lt;/span&gt; 25 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--job-type&lt;/span&gt; fulltime
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is a &lt;code&gt;CSV&lt;/code&gt; with rich data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Job title and company&lt;/li&gt;
&lt;li&gt;Full description&lt;/li&gt;
&lt;li&gt;Job type and level&lt;/li&gt;
&lt;li&gt;Posted date&lt;/li&gt;
&lt;li&gt;Direct application URL&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%2Fkixjefw84nv11zecnd5s.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%2Fkixjefw84nv11zecnd5s.png" alt="JobsParser CLI" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;JobSpy and JobsParser are also compatible with other job boards like LinkedIn, Indeed, Glassdoor, Google &amp;amp; ZipRecruiter.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Bulk Filtering
&lt;/h3&gt;

&lt;p&gt;While pandas seemed obvious (and I've given it a fair try), Google Sheets proved more flexible. Here's my filtering strategy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Time Filter&lt;/strong&gt;: Last 7 days&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Jobs older than a week have lower response rates&lt;/li&gt;
&lt;li&gt;Fresh postings mean active hiring&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Experience Filter&lt;/strong&gt;: "job_level" matching your experience:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For my partner who is looking for her first role, I filtered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Internship"&lt;/li&gt;
&lt;li&gt;"Entry Level"&lt;/li&gt;
&lt;li&gt;"Not Applicable"&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tech Stack Filter&lt;/strong&gt;: "description" contains:

&lt;ul&gt;
&lt;li&gt;The word "React"&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;More complex filters can be created to check for multiple technologies.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This cut 401 jobs down to 8 matches!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Smart Review
&lt;/h3&gt;

&lt;p&gt;For the filtered jobs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quick scan of title/company (10 seconds)&lt;/li&gt;
&lt;li&gt;Open promising &lt;code&gt;job_url&lt;/code&gt; in new tab&lt;/li&gt;
&lt;li&gt;Check the description in detail.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;I hope this tool helps you make your job search a slightly more enjoyable experience.&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback, please let me know.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>python</category>
      <category>tools</category>
    </item>
    <item>
      <title>Node.js File Writing Methods - createWriteStream vs writeFileSync</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Fri, 10 Jan 2025 09:45:46 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/nodejs-file-writing-methods-createwritestream-vs-writefilesync-11do</link>
      <guid>https://dev.to/franciscomoretti/nodejs-file-writing-methods-createwritestream-vs-writefilesync-11do</guid>
      <description>&lt;p&gt;When working with Node.js file system operations, choosing the right method to write files can significantly impact your application's performance. Let's explore two common approaches: &lt;code&gt;fs.createWriteStream()&lt;/code&gt; and &lt;code&gt;fs.writeFileSync()&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Differences at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;fs.createWriteStream(path).write(buffer)&lt;/th&gt;
&lt;th&gt;fs.writeFileSync(path, buffer)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Synchronicity&lt;/td&gt;
&lt;td&gt;Asynchronous&lt;/td&gt;
&lt;td&gt;Synchronous&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blocking&lt;/td&gt;
&lt;td&gt;Non-blocking&lt;/td&gt;
&lt;td&gt;Blocking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Better for large files or frequent writes&lt;/td&gt;
&lt;td&gt;Better for small, infrequent writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory Usage&lt;/td&gt;
&lt;td&gt;More memory-efficient for large files&lt;/td&gt;
&lt;td&gt;Loads entire content into memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error Handling&lt;/td&gt;
&lt;td&gt;Requires callback or event listeners&lt;/td&gt;
&lt;td&gt;Throws errors directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use Case&lt;/td&gt;
&lt;td&gt;Streaming large files, real-time data&lt;/td&gt;
&lt;td&gt;Quick, simple writes, small files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Control Flow&lt;/td&gt;
&lt;td&gt;Continues execution immediately&lt;/td&gt;
&lt;td&gt;Waits until write is complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backpressure Handling&lt;/td&gt;
&lt;td&gt;Handles backpressure automatically&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Using createWriteStream
&lt;/h2&gt;

&lt;p&gt;The stream-based approach is ideal for handling large files or when you need to write data frequently:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Content to write&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, Node.js file system!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Create a write stream&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;writeStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createWriteStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example1.txt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Write to the file&lt;/span&gt;
&lt;span class="nx"&gt;writeStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UTF8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// End the stream&lt;/span&gt;
&lt;span class="nx"&gt;writeStream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// Handle finish event&lt;/span&gt;
&lt;span class="nx"&gt;writeStream&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finish&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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Write completed.&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="c1"&gt;// Handle error event&lt;/span&gt;
&lt;span class="nx"&gt;writeStream&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="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="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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;An error occurred:&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="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;Program continues executing immediately.&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;h3&gt;
  
  
  Key Benefits 🎯
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Non-blocking operation&lt;/li&gt;
&lt;li&gt;  Memory efficient for large files&lt;/li&gt;
&lt;li&gt;  Automatic backpressure handling&lt;/li&gt;
&lt;li&gt;  Ideal for real-time data processing&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Using writeFileSync
&lt;/h2&gt;

&lt;p&gt;For simple, one-off writes with small files, the synchronous approach might be more straightforward:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Content to write&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, Node.js file system!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Write to the file synchronously&lt;/span&gt;
  &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example2.txt&lt;/span&gt;&lt;span class="dl"&gt;'&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;UTF8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="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;Write completed.&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;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;An error occurred:&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="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;This line waits for the write operation to complete.&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;h3&gt;
  
  
  Key Benefits 🎯
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Simple error handling with try-catch&lt;/li&gt;
&lt;li&gt;  Predictable execution flow&lt;/li&gt;
&lt;li&gt;  Perfect for configuration files or small data writes&lt;/li&gt;
&lt;li&gt;  No callback management needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Use Each Method
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Choose createWriteStream when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Working with large files&lt;/li&gt;
&lt;li&gt;  Handling real-time data streams&lt;/li&gt;
&lt;li&gt;  Memory efficiency is crucial&lt;/li&gt;
&lt;li&gt;  Non-blocking operation is required&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Choose writeFileSync when:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Writing small configuration files&lt;/li&gt;
&lt;li&gt;  Simple, one-off writes&lt;/li&gt;
&lt;li&gt;  Synchronous operation is acceptable&lt;/li&gt;
&lt;li&gt;  Working with startup configurations&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Both methods have their place in Node.js development. &lt;code&gt;createWriteStream&lt;/code&gt; shines in scenarios involving large files or frequent writes, while &lt;code&gt;writeFileSync&lt;/code&gt; is perfect for simple, small file operations. Choose based on your specific use case, considering factors like file size, write frequency, and performance requirements.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
      <category>node</category>
    </item>
    <item>
      <title>Color Highlighting for Tailwind CSS Variables in VS Code</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Fri, 03 Jan 2025 17:57:06 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/color-highlighting-for-tailwind-css-variables-in-vs-code-14gf</link>
      <guid>https://dev.to/franciscomoretti/color-highlighting-for-tailwind-css-variables-in-vs-code-14gf</guid>
      <description>&lt;p&gt;When working with Tailwind CSS variables in VS Code, having visual color indicators can significantly improve your development workflow. This is particularly useful when working with Shadcn UI components, which heavily rely on CSS variables for theming.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Install the &lt;code&gt;Color Highlight&lt;/code&gt; extension by &lt;code&gt;Sergii N&lt;/code&gt; from the &lt;a href="https://marketplace.visualstudio.com/items?itemName=naumovs.color-highlight" rel="noopener noreferrer"&gt;VS Code Marketplace&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enable HSL color highlighting by adding this to your VS Code &lt;code&gt;settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"color-highlight.matchHslWithNoFunction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;With this setup, you'll see color indicators next to your Tailwind CSS variables, making it easier to identify and work with colors in your stylesheets, especially when customizing Shadcn UI themes:&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%2Fuafady6brnvvqakpsyfo.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%2Fuafady6brnvvqakpsyfo.png" alt="Color highlighting in VS Code" width="691" height="647"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This simple configuration works with both standard CSS colors and HSL values, making your Tailwind CSS and Shadcn UI development experience more visual and intuitive.&lt;/p&gt;

</description>
      <category>tailwindcss</category>
      <category>vscode</category>
      <category>tools</category>
      <category>shadcnui</category>
    </item>
    <item>
      <title>Next.js Authentication Best Practices</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Wed, 26 Jun 2024 11:31:21 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/nextjs-authentication-best-practices-2pc7</link>
      <guid>https://dev.to/franciscomoretti/nextjs-authentication-best-practices-2pc7</guid>
      <description>&lt;h2&gt;
  
  
  What is Next.js Authentication?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;: This article contains outdated advice. Next.js has significantly changed its authentication recommendations. Please refer to our &lt;a href="https://www.franciscomoretti.com/blog/modern-nextjs-authentication-best-practices" rel="noopener noreferrer"&gt;updated authentication guide&lt;/a&gt; for current best practices.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next.js authentication is the process of verifying user identity in Next.js applications. It ensures that only authorized users can access protected routes and data.&lt;/p&gt;

&lt;p&gt;Authentication in Next.js is crucial for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Protecting sensitive user data&lt;/li&gt;
&lt;li&gt; Controlling access to specific features&lt;/li&gt;
&lt;li&gt; Personalizing user experiences&lt;/li&gt;
&lt;li&gt; Maintaining application security&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Understanding Next.js authentication best practices is key to building secure and efficient web applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Middleware vs. Page Component Authentication
&lt;/h2&gt;

&lt;p&gt;Next.js provides two main approaches to authentication: middleware and page component authentication. Let's explore both:&lt;/p&gt;

&lt;h3&gt;
  
  
  Middleware Authentication
&lt;/h3&gt;

&lt;p&gt;Middleware authentication offers several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Cleaner structure for managing authentication across routes&lt;/li&gt;
&lt;li&gt;  Preserves static rendering capabilities&lt;/li&gt;
&lt;li&gt;  Better performance, especially with JSON Web Tokens (JWTs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's a simple example of middleware authentication:&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="kd"&gt;type&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="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;function&lt;/span&gt; &lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&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;currentUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookies&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;currentUser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;value&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;currentUser&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;currentUser&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nextUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;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;matcher&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;/((?!api|_next/static|_next/image|.*&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;.png$).*)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Page Component Authentication
&lt;/h3&gt;

&lt;p&gt;Page component authentication has its own benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Can be implemented directly in the page file&lt;/li&gt;
&lt;li&gt;  Offers more flexibility for page-specific auth logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's an example of page component authentication:&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;redirect&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/navigation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;checkAuth&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;@/lib/auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProtectedPage&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;isAuthenticated&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;checkAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="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;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Protected Content&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;/* Page content here */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;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;blockquote&gt;
&lt;p&gt;💡 Tip: Choose middleware for better performance and cleaner code structure, especially for apps with many protected routes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Avoiding Authentication in Layout Components
&lt;/h2&gt;

&lt;p&gt;It's important to avoid implementing authentication checks in layout components. Why? Layout components don't re-render on client-side navigation, which could leave routes unprotected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preserving Static Rendering
&lt;/h2&gt;

&lt;p&gt;Static rendering is a key performance feature in Next.js. To preserve it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Use middleware for authentication when possible&lt;/li&gt;
&lt;li&gt; Don’t add authentication inside specific routes (pages).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Adding authentication inside a page makes it dynamic. However, many times the extra security layer benefit outweighs the performance improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication for Server Actions
&lt;/h2&gt;

&lt;p&gt;Server actions are a new feature in Next.js that allow server-side processing directly from client components. When using server actions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Implement authentication checks within the server action itself&lt;/li&gt;
&lt;li&gt; Don't rely solely on page-level or middleware authentication&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Server actions are like API routes. They could be called by an external user by calling the server action URL directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proximity Principle
&lt;/h2&gt;

&lt;p&gt;The proximity principle suggests keeping authentication checks as close as possible to where data is accessed or used. This means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Implementing checks in reusable components that handle sensitive data&lt;/li&gt;
&lt;li&gt; Adding auth logic directly in data fetching functions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's an example of applying the proximity principle:&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;fetchUserData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAuthenticated&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;checkAuth&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isAuthenticated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unauthorized&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="c1"&gt;// Fetch and return user data&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Multi-layered Approach
&lt;/h2&gt;

&lt;p&gt;A robust authentication strategy in Next.js involves multiple layers of protection:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Use middleware or page-level checks as a first line of defense&lt;/li&gt;
&lt;li&gt; Implement additional checks at the page and data access levels&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a simplified example of a multi-layered approach:&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;// Middleware (first layer)&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;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Check for auth token&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Page component (second layer)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProtectedPage&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;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Custom hook for user data&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoadingOrRedirect&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProtectedContent&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Data fetching (third layer)&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;fetchSensitiveData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Check auth and permissions before fetching&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



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

&lt;p&gt;Next.js authentication best practices involve a combination of techniques:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Use middleware for app-wide auth when possible&lt;/li&gt;
&lt;li&gt; Implement page-level checks for specific routes&lt;/li&gt;
&lt;li&gt; Apply the proximity principle for data access&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;a href="https://www.youtube.com/watch?v=kbCzZzXTjuw&amp;amp;t=23s" rel="noopener noreferrer"&gt;Next.js Authentication - Avoid these 4 mistakes (Don't do auth in layout!)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://nextjs.org/docs/app/building-your-application/authentication" rel="noopener noreferrer"&gt;Authentication - Next.js&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nextjs</category>
    </item>
    <item>
      <title>Server Side Rendering Vs Client Side Rendering Waterfall</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Thu, 20 Jun 2024 11:16:16 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/server-side-rendering-vs-client-side-rendering-waterfall-3ncn</link>
      <guid>https://dev.to/franciscomoretti/server-side-rendering-vs-client-side-rendering-waterfall-3ncn</guid>
      <description>&lt;h2&gt;
  
  
  What is Server Side Rendering (SSR)?
&lt;/h2&gt;

&lt;p&gt;Server-side rendering (SSR) is a technique where the initial HTML of a web page is generated on the server and sent to the client's browser. This approach allows for faster initial page loads and better search engine optimization (SEO) since search engines can easily crawl and index the rendered content.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Client Side Rendering (CSR)?
&lt;/h2&gt;

&lt;p&gt;Client-side rendering (CSR) is a technique where the initial HTML of a web page is minimal, and the content is rendered dynamically on the client-side using JavaScript. This approach can provide a more seamless and interactive user experience, but it may result in slower initial page loads and potential SEO challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Waterfall Effect
&lt;/h2&gt;

&lt;p&gt;The waterfall effect refers to the sequence of network requests and responses required to load a web page fully. Each request must wait for the previous one to complete, creating a cascading effect similar to a waterfall.&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%2Fyohdfge2ub3r2n19uhfu.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%2Fyohdfge2ub3r2n19uhfu.png" alt="SSR vs CSR waterfall" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Client-Side Rendering Waterfall
&lt;/h2&gt;

&lt;p&gt;In a client-side rendered React application, the waterfall effect can be more pronounced due to the multiple round trips required to fetch the necessary JavaScript files and data. Let's consider an example with a &lt;code&gt;Post&lt;/code&gt; component and a child &lt;code&gt;Comment&lt;/code&gt; component:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Initial Markup (without content)&lt;/strong&gt;: The browser receives the initial HTML markup, which contains placeholders for the React components.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Get the &lt;code&gt;Post&lt;/code&gt; JS&lt;/strong&gt;: The browser fetches the JavaScript file(s) required to render the &lt;code&gt;Post&lt;/code&gt; component.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Get the &lt;code&gt;Post&lt;/code&gt; content&lt;/strong&gt;: Once the &lt;code&gt;Post&lt;/code&gt; component is loaded, the browser fetches the data required to render the post content.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Get the &lt;code&gt;Comment&lt;/code&gt; JS&lt;/strong&gt;: The browser fetches the JavaScript file(s) required to render the &lt;code&gt;Comment&lt;/code&gt; component.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Get the &lt;code&gt;Comment&lt;/code&gt; content&lt;/strong&gt;: Finally, the browser fetches the data required to render the comment content.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This process results in a waterfall with five distinct steps, potentially leading to longer load times and a perceived delay in rendering the complete page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-Side Rendering Waterfall
&lt;/h2&gt;

&lt;p&gt;In contrast, with server-side rendering, the waterfall effect can be significantly reduced. Here's how it might look for the same &lt;code&gt;Post&lt;/code&gt; and &lt;code&gt;Comment&lt;/code&gt; components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Markup (with content)&lt;/strong&gt;: The server generates and sends the initial HTML markup with the rendered content for both the &lt;code&gt;Post&lt;/code&gt; and &lt;code&gt;Comment&lt;/code&gt; components.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Get in parallel&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;The &lt;code&gt;Post&lt;/code&gt; JS&lt;/strong&gt;: The browser fetches the JavaScript file(s) required for the &lt;code&gt;Post&lt;/code&gt; component.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The &lt;code&gt;Comment&lt;/code&gt; JS&lt;/strong&gt;: The browser fetches the JavaScript file(s) required for the &lt;code&gt;Comment&lt;/code&gt; component.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By rendering the initial content on the server, the waterfall is reduced to just two steps, potentially resulting in faster initial page loads and improved perceived performance.&lt;/p&gt;

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

&lt;p&gt;The choice between server-side rendering (SSR) and client-side rendering (CSR) in React applications can significantly impact performance, particularly when considering the waterfall effect. SSR generally results in a shorter waterfall and faster initial page loads, while CSR may be easier to achieve, it can have a longer waterfall.&lt;/p&gt;

</description>
      <category>react</category>
    </item>
    <item>
      <title>RunJS - A JavaScript and TypeScript Local Playground</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Fri, 07 Jun 2024 09:24:57 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/runjs-a-javascript-and-typescript-local-playground-4dif</link>
      <guid>https://dev.to/franciscomoretti/runjs-a-javascript-and-typescript-local-playground-4dif</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Recently, I got a license from Luke Haas to explore &lt;a href="https://runjs.app/" rel="noopener noreferrer"&gt;RunJS&lt;/a&gt;, and I'm impressed with some of the features. I've been putting it to the test and integrating it into my workflow. It's worth mentioning that there is a free version with core functionality, and the license gives you lifetime access (with updates for 1 year), which is rare these days. In this post, I'll be sharing my experience with RunJS and how it's helping me in my daily work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is RunJS?
&lt;/h2&gt;

&lt;p&gt;RunJS is a local playground for JavaScript and TypeScript. It allows you to write, execute, and see the results of your code instantly. Unlike IDEs, RunJS has a minimalistic, distraction-free interface perfect for quick experiments and learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Choose RunJS?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Minimalistic Interface:&lt;/strong&gt; RunJS offers a clean, distraction-free environment. This simplicity helps you focus on your code, making it great for presentations and screen sharing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Continuous Code Execution:&lt;/strong&gt; RunJS continuously executes your code as you type. This instant feedback loop is invaluable for testing and learning.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run JS and TS with no configuration:&lt;/strong&gt; RunJS supports both JavaScript and TypeScript out of the box. No need to run node or to setup TS. It simply works.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Use cases for RunJS
&lt;/h2&gt;

&lt;p&gt;These are some of the use cases that I've personally found useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Exploring JS Language Features
&lt;/h3&gt;

&lt;p&gt;JavaScript is weird. RunJS helps you understand them by letting you execute code snippets instantly. This is great for trying out language features or understanding tricky parts of JavaScript.&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%2Fopt7h2nl63i405slskyx.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%2Fopt7h2nl63i405slskyx.png" alt="JavaScript is weird demo" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note that it highlights some of these errors to help you avoid the fitfalls of JavaScript.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Printing the Output of Code Execution
&lt;/h3&gt;

&lt;p&gt;RunJS makes it easy to see the output of your code as you type. This feature is perfect for debugging and understanding how your code works.&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%2Fhqdceryust1f45gzp5xi.gif" 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%2Fhqdceryust1f45gzp5xi.gif" alt="Runjs output demo" width="1857" height="1020"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Executing JS/TS Without Creating a File
&lt;/h3&gt;

&lt;p&gt;With RunJS, there's no need to create files or set up a Node environment to run your code. This is ideal for quick experiments or learning sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Solving LeetCode Problems
&lt;/h3&gt;

&lt;p&gt;This is my favorite use case for RunJS. RunJS allows you to print the output of test cases and different parts of the code easily, making debugging and understanding your solution much simpler. The distraction-free interface of RunJS helps a lot here.&lt;/p&gt;

&lt;p&gt;You can code all the function with auto-completion support and error linting. It's also convenient to see the output of all the test cases at the end and check how they do without having to hit run every time.&lt;/p&gt;

&lt;p&gt;Super helpful for small problems like this one.&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%2Fy1ft3gi5la0z29exmyy9.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%2Fy1ft3gi5la0z29exmyy9.png" alt="alt text" width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Showing another small one that fits in the screen, but it's also helpful for complex problems.1&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%2Fuvendc67d1iwgar870kt.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%2Fuvendc67d1iwgar870kt.png" alt="alt text" width="800" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh and you might have noticed that the last case should be true. Wrong! This is not how array comparison works in JS. Here RunJS is helping you to understand the problem with an error message.&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%2Fbt0caoh8so06iml5819e.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%2Fbt0caoh8so06iml5819e.png" alt="alt text" width="800" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://runjs.app/" rel="noopener noreferrer"&gt;RunJS&lt;/a&gt; is a nice tool for JavaScript and TypeScript coding. Its simple interface and instant feedback make it perfect for learning, and prototyping. It's not going to replace your IDE but it can be a great addition to your workflow. By being different, and by keeping things simple, it does better than IDEs for some tasks. In my opinion, is a specialized tool that helps with communication, exploring and focus.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>tools</category>
    </item>
    <item>
      <title>useState vs useRef - When to use state and when to use ref</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Tue, 21 May 2024 12:40:45 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/usestate-vs-useref-when-to-use-state-and-when-to-use-ref-17if</link>
      <guid>https://dev.to/franciscomoretti/usestate-vs-useref-when-to-use-state-and-when-to-use-ref-17if</guid>
      <description>&lt;h2&gt;
  
  
  What is useState?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;useState&lt;/code&gt; is a React hook used for managing state in functional components. It allows you to add state to your components, making them dynamic and interactive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features of useState
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;State Management:&lt;/strong&gt; &lt;code&gt;useState&lt;/code&gt; helps manage state within a component.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Re-renders on Change:&lt;/strong&gt; The component re-renders when the state changes.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Batched Updates:&lt;/strong&gt; React batches multiple state updates for performance.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example using useState
&lt;/h3&gt;

&lt;p&gt;Here’s a simple counter that increments when a button is clicked.&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;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Counter&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;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&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="mi"&gt;0&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;increment&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="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Count: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Increment&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;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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;useState&lt;/code&gt; initializes the count at 0 and updates it each time the button is clicked, causing the component to re-render.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is useRef?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;useRef&lt;/code&gt; is another React hook, primarily used for accessing and manipulating DOM elements directly. It persists values between renders without causing re-renders.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Features of useRef
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;DOM Reference:&lt;/strong&gt; Provides a way to reference and interact with DOM elements.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;No Re-renders on Change:&lt;/strong&gt; Changing the ref value does not trigger a re-render.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Persistent Values:&lt;/strong&gt; Keeps the same object reference between renders.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Example using useRef
&lt;/h3&gt;

&lt;p&gt;Here’s an example where clicking a button focuses an input field.&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;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRef&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="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;FocusInput&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;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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;focusInput&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="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="nf"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;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;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&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;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;focusInput&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Focus Input&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;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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;FocusInput&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, &lt;code&gt;useRef&lt;/code&gt; creates a reference to the input element, allowing direct DOM manipulation to focus the input field when the button is clicked.&lt;/p&gt;

&lt;h2&gt;
  
  
  useState vs useRef: A Comparison 🔍
&lt;/h2&gt;

&lt;p&gt;Let's compare &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useRef&lt;/code&gt; based on several aspects.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;useState&lt;/th&gt;
&lt;th&gt;useRef&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Purpose&lt;/td&gt;
&lt;td&gt;State management&lt;/td&gt;
&lt;td&gt;DOM reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Return value&lt;/td&gt;
&lt;td&gt;Returns &lt;code&gt;[value, setValue]&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{ current: initialValue }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Initial value&lt;/td&gt;
&lt;td&gt;Required&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Re-render on change&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mutablility&lt;/td&gt;
&lt;td&gt;Mutable&lt;/td&gt;
&lt;td&gt;Immutable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Update trigger&lt;/td&gt;
&lt;td&gt;Function call&lt;/td&gt;
&lt;td&gt;Direct mutation of current&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Usage in rendering&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async updates&lt;/td&gt;
&lt;td&gt;Batched&lt;/td&gt;
&lt;td&gt;Immediate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hook type&lt;/td&gt;
&lt;td&gt;State hook&lt;/td&gt;
&lt;td&gt;Ref hook&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Common use case&lt;/td&gt;
&lt;td&gt;Managing component state&lt;/td&gt;
&lt;td&gt;Accessing/manipulating DOM elements&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  When to Use useState
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Managing dynamic values that impact the UI.&lt;/li&gt;
&lt;li&gt;  Triggering re-renders to reflect state changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to Use useRef
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Accessing or modifying DOM elements directly.&lt;/li&gt;
&lt;li&gt;  Storing mutable values that persist across renders without causing re-renders.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Understanding the differences between &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useRef&lt;/code&gt; is crucial for effective state management and DOM manipulation in React. Use &lt;code&gt;useState&lt;/code&gt; when you need stateful logic that affects rendering. Use &lt;code&gt;useRef&lt;/code&gt; for accessing DOM elements and persisting values without causing re-renders.&lt;/p&gt;

</description>
      <category>react</category>
    </item>
    <item>
      <title>What is React Hydration</title>
      <dc:creator>Francisco Moretti</dc:creator>
      <pubDate>Tue, 30 Apr 2024 08:51:07 +0000</pubDate>
      <link>https://dev.to/franciscomoretti/what-is-react-hydration-27cm</link>
      <guid>https://dev.to/franciscomoretti/what-is-react-hydration-27cm</guid>
      <description>&lt;h2&gt;
  
  
  Understanding React Hydration
&lt;/h2&gt;

&lt;p&gt;React hydration is the process of taking a server-rendered HTML page and connecting the client-side JavaScript to it, adding interactivity and application state. Here's a step-by-step explanation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Server-side Rendering:&lt;/strong&gt; Server-side rendering involves generating the initial HTML content for the web page using React. This HTML is static and lacks interactivity.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sending HTML to the Client:&lt;/strong&gt; The server sends the pre-rendered HTML to the client's web browser.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hydration on the Client:&lt;/strong&gt; When the client receives the HTML, React's hydration process kicks in. The &lt;code&gt;hydrateRoot()&lt;/code&gt; function is used to "hydrate" the static HTML by attaching event listeners, state, and other interactivity to the existing DOM elements.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reusing the Existing DOM:&lt;/strong&gt; During hydration, React tries to reuse the existing DOM nodes instead of creating new ones. It compares the server-rendered HTML with the virtual DOM and updates only the necessary parts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Interactivity:&lt;/strong&gt; After hydration, the web page becomes interactive. Users can now trigger events, update the UI, and interact with the application just like a traditional client-side rendered React app.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2Fjhh6yoeklztahs84jcqa.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%2Fjhh6yoeklztahs84jcqa.png" alt="React Hydration" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Improved Performance
&lt;/h2&gt;

&lt;p&gt;Hydration allows for faster initial load times compared to a traditional client-side rendered app, as the server has already generated the initial HTML. The client-side JavaScript then enhances the experience by adding interactivity.&lt;/p&gt;

&lt;p&gt;In summary, React hydration is the process of transforming a static, server-rendered HTML page into an interactive, client-side rendered React application, seamlessly connecting the two environments for a better user experience.&lt;/p&gt;

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