<?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: Ashish Patel</title>
    <description>The latest articles on DEV Community by Ashish Patel (@ashishxcode).</description>
    <link>https://dev.to/ashishxcode</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%2F264358%2F90f3f58f-687d-48a9-8ca2-bd8c54ff4127.jpeg</url>
      <title>DEV Community: Ashish Patel</title>
      <link>https://dev.to/ashishxcode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ashishxcode"/>
    <language>en</language>
    <item>
      <title>Turborepo + Vite with React Monorepo (Part 2)</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Tue, 17 Feb 2026 06:51:42 +0000</pubDate>
      <link>https://dev.to/ashishxcode/turborepo-vite-with-react-monorepo-part-2-29ej</link>
      <guid>https://dev.to/ashishxcode/turborepo-vite-with-react-monorepo-part-2-29ej</guid>
      <description>&lt;p&gt;&lt;em&gt;This is Part 2 of a two-part series. Part 1 covered the migration itself — moving 500+ files to a pnpm monorepo, fixing CSS resolution, and debugging a Firebase singleton bug that only appears under pnpm's strict module isolation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post covers what happens after migration: integrating feature branches that don't know the monorepo exists, adding a second app under a sub-path, and the git merge strategy that turned 45 conflicts into zero.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Three Branches, One Migration
&lt;/h2&gt;

&lt;p&gt;After the migration landed on &lt;code&gt;chore/monorepo-migration&lt;/code&gt;, we had two feature branches still built against the old flat &lt;code&gt;src/&lt;/code&gt; structure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;feat/marketplace-integration&lt;/code&gt;&lt;/strong&gt; — A new marketplace app with 14 commits, including its own (broken) monorepo migration attempt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;feat/automated-outreach&lt;/code&gt;&lt;/strong&gt; — A 110-file, 18-commit feature built entirely against &lt;code&gt;src/&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both needed to land on the migrated branch. Both had files that overlapped with each other and with the base. This is where things got interesting.&lt;/p&gt;




&lt;h2&gt;
  
  
  Adding a Second App to the Monorepo
&lt;/h2&gt;

&lt;p&gt;The marketplace was a separate React app that needed to live under a sub-path: &lt;code&gt;example.com/marketplace&lt;/code&gt;. Same domain as the dashboard, different app.&lt;/p&gt;

&lt;h3&gt;
  
  
  The workspace setup
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# pnpm-workspace.yaml&lt;/span&gt;
&lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apps/*"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;packages/*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;apps/
├── dashboard/     # localhost:3002
└── marketplace/   # localhost:3003, hosted at /marketplace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;apps/marketplace/package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@acme/marketplace"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"vite --port 3003"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"turbo dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev:dashboard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm --filter @acme/dashboard dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev:marketplace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm --filter @acme/marketplace dev"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;turbo dev&lt;/code&gt; starts both apps in parallel. Simple enough. The complexity is in sub-path hosting.&lt;/p&gt;

&lt;h3&gt;
  
  
  The five layers of sub-path configuration
&lt;/h3&gt;

&lt;p&gt;Sub-path hosting means the marketplace is served at &lt;code&gt;/marketplace&lt;/code&gt; instead of &lt;code&gt;/&lt;/code&gt;. This requires &lt;strong&gt;five things to agree&lt;/strong&gt;, and missing any one produces a blank white screen with no error message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1: Vite base&lt;/strong&gt;&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;// apps/marketplace/vite.config.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/marketplace/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3003&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Vite to prefix all asset URLs with &lt;code&gt;/marketplace/&lt;/code&gt;. Without it, the app tries to load &lt;code&gt;main.js&lt;/code&gt; from &lt;code&gt;/&lt;/code&gt; instead of &lt;code&gt;/marketplace/main.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2: React Router basename&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// apps/marketplace/src/index.jsx&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BrowserRouter&lt;/span&gt; &lt;span class="na"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/marketplace"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;BrowserRouter&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;Without this, the router doesn't recognize &lt;code&gt;/marketplace/some-page&lt;/code&gt; as a valid route. All navigation fails silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 3: Dev proxy&lt;/strong&gt;&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;// apps/dashboard/vite.config.js&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;proxy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/marketplace&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;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3003&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;changeOrigin&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During development, the dashboard runs on port 3002. When a request comes in for &lt;code&gt;/marketplace/*&lt;/code&gt;, the proxy forwards it to the marketplace dev server on port 3003.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 4: The trailing slash&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This one cost us 2 hours.&lt;/p&gt;

&lt;p&gt;The browser requests &lt;code&gt;/marketplace&lt;/code&gt; (no trailing slash). The proxy matches and forwards to port 3003. But the marketplace's Vite server expects &lt;code&gt;/marketplace/&lt;/code&gt; (with trailing slash) because that's its &lt;code&gt;base&lt;/code&gt;. The HTML never loads. Blank screen. No error.&lt;/p&gt;

&lt;p&gt;The fix — a 12-line Vite plugin:&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;function&lt;/span&gt; &lt;span class="nf"&gt;marketplaceRedirect&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="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;marketplace-redirect&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;configureServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;middlewares&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/marketplace&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;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/marketplace/&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="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="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;One character — &lt;code&gt;/&lt;/code&gt; — was the difference between a working app and a blank screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 5: Asset references in &lt;code&gt;index.html&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- apps/marketplace/index.html --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"icon"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/marketplace/favicon.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not &lt;code&gt;/favicon.png&lt;/code&gt;. Not &lt;code&gt;favicon.png&lt;/code&gt;. Must match the base path exactly. The favicon is the easy one to spot. The harder ones are Open Graph images, manifest files, and any hardcoded asset paths in your HTML.&lt;/p&gt;

&lt;h3&gt;
  
  
  The debugging principle
&lt;/h3&gt;

&lt;p&gt;Sub-path apps fail silently. When something is misconfigured:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No 404 page (the proxy catches everything)&lt;/li&gt;
&lt;li&gt;No console error (the HTML loads but contains nothing useful)&lt;/li&gt;
&lt;li&gt;No network error (requests succeed, they just return the wrong content)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You get a blank white screen and have to check all five layers manually. We now have a checklist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;□ Vite base matches sub-path (with trailing slash)
□ React Router basename matches sub-path (without trailing slash)
□ Dashboard proxy rule exists for sub-path
□ Trailing slash redirect plugin is active
□ All index.html asset paths use the sub-path prefix
□ Hard refresh works (not just client-side navigation)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Integrating Feature Branches: The Wrong Way
&lt;/h2&gt;

&lt;p&gt;With the marketplace working on the monorepo branch, we needed to bring in the outreach feature from &lt;code&gt;feat/automated-outreach&lt;/code&gt; — 110 files, all referencing &lt;code&gt;src/&lt;/code&gt; paths.&lt;/p&gt;

&lt;h3&gt;
  
  
  What we tried first
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Checked out &lt;code&gt;feat/automated-outreach&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Ran the migration script on it (moved files to &lt;code&gt;apps/dashboard/src/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Committed the migration&lt;/li&gt;
&lt;li&gt;Merged &lt;code&gt;feat/marketplace-integration&lt;/code&gt; into it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Result: &lt;strong&gt;45 merge conflicts.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both branches had independently run the migration script. Git saw every file that was moved from &lt;code&gt;src/&lt;/code&gt; to &lt;code&gt;apps/dashboard/src/&lt;/code&gt; as a conflict — because both branches created the same file at the same destination, even when the content was identical.&lt;/p&gt;

&lt;h3&gt;
  
  
  The resolution that silently broke everything
&lt;/h3&gt;

&lt;p&gt;Under time pressure, we resolved all 45 conflicts by bulk-checking out files from the marketplace branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# "Quick" conflict resolution&lt;/span&gt;
git checkout feat/marketplace-integration &lt;span class="nt"&gt;--&lt;/span&gt; packages/ui/src/components/button.jsx
git checkout feat/marketplace-integration &lt;span class="nt"&gt;--&lt;/span&gt; packages/ui/src/components/card.jsx
git checkout feat/marketplace-integration &lt;span class="nt"&gt;--&lt;/span&gt; apps/dashboard/src/features/campaign/constants/index.js
&lt;span class="c"&gt;# ... 42 more files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app started. No conflict markers. Looked clean.&lt;/p&gt;

&lt;p&gt;Then at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SyntaxError: does not provide an export named 'getOutreachStatus'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What actually happened
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git checkout &amp;lt;branch&amp;gt; -- &amp;lt;file&amp;gt;&lt;/code&gt; replaces the entire file with that branch's version. For shared UI components (&lt;code&gt;button.jsx&lt;/code&gt;, &lt;code&gt;card.jsx&lt;/code&gt;), this was fine — identical in both branches.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;campaign/constants/index.js&lt;/code&gt; was different. The outreach branch had added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OUTREACH_AUTOMATION_STATUS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;NOT_STARTED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not_started&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;ACTIVE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;PAUSED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paused&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;OUTREACH_REACHOUT_STATUS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;yet_to_reachout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yet to Reachout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-slate-500&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;reached_out_no_reply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Reached Out - No Reply&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text-amber-500&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;// ...&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;getOutreachStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;OUTREACH_REACHOUT_STATUS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="nx"&gt;OUTREACH_REACHOUT_STATUS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yet_to_reachout&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The marketplace branch had the older version without these exports. By checking out the marketplace version, we &lt;strong&gt;silently deleted the outreach code&lt;/strong&gt;. No warning. No error at merge time. The imports compiled fine because the file existed — it just didn't export what the outreach components expected.&lt;/p&gt;

&lt;p&gt;We found similar silent deletions in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store file — outreach pagination state gone&lt;/li&gt;
&lt;li&gt;Hooks file — outreach board columns gone&lt;/li&gt;
&lt;li&gt;Creator list component — column management drawer gone&lt;/li&gt;
&lt;li&gt;Workflow labels — outreach-specific labels gone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total: &lt;strong&gt;26 files with silently dropped code.&lt;/strong&gt; All discovered only at runtime.&lt;/p&gt;

&lt;h3&gt;
  
  
  The lesson
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Never bulk-resolve merge conflicts by checking out one side.&lt;/strong&gt; It's the git equivalent of deleting files you haven't read. The correct approach depends on which branch owns each file:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Conflict type&lt;/th&gt;
&lt;th&gt;Resolution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;File exists only in branch A&lt;/td&gt;
&lt;td&gt;Take A's version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File exists only in branch B&lt;/td&gt;
&lt;td&gt;Take B's version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File modified in both branches&lt;/td&gt;
&lt;td&gt;Read both versions, merge manually&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And after resolving, &lt;strong&gt;always diff against both parents&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Did we lose anything from the outreach branch?&lt;/span&gt;
git diff HEAD &lt;span class="nt"&gt;--&lt;/span&gt; apps/dashboard/src/features/campaign/

&lt;span class="c"&gt;# Did we lose anything from the marketplace branch?&lt;/span&gt;
git diff feat/marketplace-integration &lt;span class="nt"&gt;--&lt;/span&gt; packages/ui/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Integrating Feature Branches: The Right Way
&lt;/h2&gt;

&lt;p&gt;We scrapped the broken merge and started over. The approach that works:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Start from the migrated base
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feat/automated-outreach upstream/chore/monorepo-migration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us the clean monorepo structure — &lt;code&gt;apps/dashboard/&lt;/code&gt;, &lt;code&gt;packages/ui/&lt;/code&gt;, all the Vite config — without any feature code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Get the feature PR's final file state
&lt;/h3&gt;

&lt;p&gt;Instead of cherry-picking commits (which carry merge history and conflict potential), we extract the final state of every changed file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Fetch the PR&lt;/span&gt;
git fetch upstream pull/XXXX/head:pr-ref

&lt;span class="c"&gt;# List all files the PR changed&lt;/span&gt;
gh &lt;span class="nb"&gt;pr &lt;/span&gt;diff XXXX &lt;span class="nt"&gt;--name-only&lt;/span&gt;
&lt;span class="c"&gt;# → src/features/campaign/api/mutations/createProposalMutation.js&lt;/span&gt;
&lt;span class="c"&gt;# → src/features/campaign/api/mutations/index.js&lt;/span&gt;
&lt;span class="c"&gt;# → src/features/campaign/constants/index.js&lt;/span&gt;
&lt;span class="c"&gt;# → ... (110 files total)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Copy with path mapping
&lt;/h3&gt;

&lt;p&gt;Every file in the PR uses &lt;code&gt;src/&lt;/code&gt; paths. We need them under &lt;code&gt;apps/dashboard/src/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gh &lt;span class="nb"&gt;pr &lt;/span&gt;diff XXXX &lt;span class="nt"&gt;--name-only&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;f&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"package.json"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;continue
    &lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"apps/dashboard/&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    git show pr-ref:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This copies the final version of each file — all 18 commits squashed into the end result — and places it in the monorepo structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Fix imports
&lt;/h3&gt;

&lt;p&gt;The copied files still have old-style imports. Run the verification script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find apps/dashboard/src &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.jsx"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s1"&gt;'s|from "~/components/ui/\([^"]*\)"|from "@acme/ui/components/\1"|g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;{}&lt;/span&gt; +

&lt;span class="c"&gt;# Revert excluded components&lt;/span&gt;
find apps/dashboard/src &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.jsx"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s|from "@acme/ui/components/async-select"|from "~/components/ui/async-select"|g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s|from "@acme/ui/components/confirm-dialog"|from "~/components/ui/confirm-dialog"|g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;{}&lt;/span&gt; +
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: Add new dependencies
&lt;/h3&gt;

&lt;p&gt;The outreach feature introduced three new packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm add &lt;span class="nt"&gt;--filter&lt;/span&gt; @acme/dashboard react-markdown remove-markdown turndown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 6: Commit and merge
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add apps/dashboard/ pnpm-lock.yaml
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: add automated outreach feature to monorepo"&lt;/span&gt;

&lt;span class="c"&gt;# Now merge the marketplace branch&lt;/span&gt;
git merge feat/marketplace-integration &lt;span class="nt"&gt;--no-edit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result: zero conflicts.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The outreach commit added and modified files in &lt;code&gt;apps/dashboard/src/features/campaign/&lt;/code&gt;. The marketplace branch added &lt;code&gt;apps/marketplace/&lt;/code&gt; and dashboard proxy config. No overlapping files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why this works
&lt;/h3&gt;

&lt;p&gt;The key insight: &lt;strong&gt;don't migrate the feature branch.&lt;/strong&gt; Apply the feature's changes directly onto the already-migrated base.&lt;/p&gt;

&lt;p&gt;When you migrate two branches independently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base → Migration → Branch A (all files moved to apps/dashboard/)
Base → Migration → Branch B (all files moved to apps/dashboard/)
Merge A + B → 45 conflicts (same files created at same paths)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you apply features onto the migrated base:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Base → Migration → Feature A applied → Feature B merged → 0 conflicts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second approach treats migration as infrastructure and features as content. Infrastructure is shared (one copy). Content is separate (no overlap).&lt;/p&gt;




&lt;h2&gt;
  
  
  The Import Safety Net
&lt;/h2&gt;

&lt;p&gt;Throughout this process — cherry-picks, merges, file copies — old-style imports kept reappearing. Every time code from a pre-migration branch enters the monorepo, some files will have:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~/components/ui/button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// broken&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@acme/ui/components/button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// correct&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We ran Step 24 of the migration script &lt;strong&gt;five times&lt;/strong&gt; over the course of the integration. It's designed to be run repeatedly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BROKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find apps/dashboard/src &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.jsx"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="s1"&gt;'from "~/components/ui/'&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; + 2&amp;gt;/dev/null | &lt;span class="se"&gt;\&lt;/span&gt;
  xargs &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'from "~/components/ui/'&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"async-select"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"confirm-dialog"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BROKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# Fix and report&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running it on already-fixed code is a no-op. Running it after a merge catches anything that slipped through. It's the cheapest safety net we have.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Treat migration as a gate, not a one-time event.&lt;/strong&gt; Until every pre-migration branch is merged, this gate needs to exist.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Complete Workflow
&lt;/h2&gt;

&lt;p&gt;Here's the process we now follow for integrating any pre-migration feature branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Start from the migrated base&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feat/my-feature upstream/chore/monorepo-migration

&lt;span class="c"&gt;# 2. Fetch and extract the feature PR&lt;/span&gt;
git fetch upstream pull/XXXX/head:pr-ref
gh &lt;span class="nb"&gt;pr &lt;/span&gt;diff XXXX &lt;span class="nt"&gt;--name-only&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;f&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"apps/dashboard/&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    git show pr-ref:&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$target&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;

&lt;span class="c"&gt;# 3. Fix imports&lt;/span&gt;
&lt;span class="c"&gt;# (run Step 24)&lt;/span&gt;

&lt;span class="c"&gt;# 4. Add new dependencies&lt;/span&gt;
pnpm add &lt;span class="nt"&gt;--filter&lt;/span&gt; @acme/dashboard &amp;lt;new-packages&amp;gt;

&lt;span class="c"&gt;# 5. Commit&lt;/span&gt;
git add apps/dashboard/ pnpm-lock.yaml
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: add &amp;lt;feature&amp;gt; to monorepo"&lt;/span&gt;

&lt;span class="c"&gt;# 6. Merge other branches (zero conflicts expected)&lt;/span&gt;
git merge feat/other-branch &lt;span class="nt"&gt;--no-edit&lt;/span&gt;

&lt;span class="c"&gt;# 7. Run Step 24 again (the merge may have brought old imports)&lt;/span&gt;

&lt;span class="c"&gt;# 8. Verify&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'~/components/ui/'&lt;/span&gt; apps/dashboard/src/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.jsx'&lt;/span&gt; &lt;span class="nt"&gt;--include&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'*.js'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s1"&gt;'async-select\|confirm-dialog'&lt;/span&gt;
&lt;span class="c"&gt;# Should return nothing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What We Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Things that broke silently
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Time to diagnose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CSS imports ignoring &lt;code&gt;exports&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;ENOENT on shared stylesheet&lt;/td&gt;
&lt;td&gt;30 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firebase singleton under pnpm&lt;/td&gt;
&lt;td&gt;"Service database is not available"&lt;/td&gt;
&lt;td&gt;4 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trailing slash in sub-path&lt;/td&gt;
&lt;td&gt;Blank white screen, no errors&lt;/td&gt;
&lt;td&gt;2 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;git checkout --theirs&lt;/code&gt; dropping code&lt;/td&gt;
&lt;td&gt;Missing exports at runtime&lt;/td&gt;
&lt;td&gt;3 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Old imports after merge&lt;/td&gt;
&lt;td&gt;Vite import analysis failure&lt;/td&gt;
&lt;td&gt;10 min (once we had Step 24)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Things that could have been caught earlier
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A pre-migration CI check&lt;/strong&gt;: If we'd added &lt;code&gt;grep -r '~/components/ui/' apps/dashboard/src/&lt;/code&gt; to CI, broken imports would fail the build instead of reaching dev.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Firebase integration test&lt;/strong&gt;: A simple test that calls &lt;code&gt;getDatabase(app)&lt;/code&gt; would have caught the singleton issue immediately instead of us discovering it in the browser.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sub-path smoke test&lt;/strong&gt;: A curl to &lt;code&gt;localhost:3002/marketplace&lt;/code&gt; checking for a 200 with non-empty body would have caught the trailing slash issue in seconds.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The production-level principles
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;pnpm breaks singleton libraries.&lt;/strong&gt; Add &lt;code&gt;resolve.dedupe&lt;/code&gt; for any library that shares state between sub-packages. This is invisible until runtime.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;CSS tooling lives outside the module system.&lt;/strong&gt; Use Vite aliases for shared packages that contain styles. &lt;code&gt;exports&lt;/code&gt; won't help you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sub-path apps fail silently.&lt;/strong&gt; Five layers must agree. Build a checklist and test with hard refresh.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Never bulk-resolve git conflicts.&lt;/strong&gt; Read both sides. Diff against both parents after resolving.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Migration is ongoing.&lt;/strong&gt; Until every old-structure branch is merged, keep the import verification script and run it after every merge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Apply features onto the migrated base, not the other way around.&lt;/strong&gt; This single decision turned 45 conflicts into zero.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Final State
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
├── apps/
│   ├── dashboard/       # @acme/dashboard (port 3002)
│   └── marketplace/     # @acme/marketplace (port 3003, /marketplace)
├── packages/
│   └── ui/              # @acme/ui (shared shadcn/ui components)
├── scripts/
│   └── migrate-to-monorepo.sh
├── pnpm-workspace.yaml
├── turbo.json
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One command starts everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two apps running, shared components, Firebase working, all feature branches integrated. The migration touched 500+ files across five branches. The final commit history is clean. The process is documented. And we have a script that catches the next engineer's old-style imports before they reach production.&lt;/p&gt;

&lt;p&gt;That's the part the tutorials leave out — the migration isn't done when the script finishes. It's done when the last pre-migration branch merges and the safety net can finally be removed.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Turborepo + Vite with React Monorepo (Part 1)</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Tue, 17 Feb 2026 06:51:29 +0000</pubDate>
      <link>https://dev.to/ashishxcode/turborepo-vite-with-react-monorepo-part-1-3f15</link>
      <guid>https://dev.to/ashishxcode/turborepo-vite-with-react-monorepo-part-1-3f15</guid>
      <description>&lt;p&gt;&lt;em&gt;This is Part 1 of a two-part series on migrating a production React app to a monorepo. This post covers the migration itself — the script, the structure decisions, and two invisible runtime bugs that only appear after you switch package managers. &lt;a href="https://dev.to/ashishxcode/turborepo-vite-with-react-monorepo-part-2-29ej"&gt;Part 2&lt;/a&gt; covers integrating feature branches, adding a new sub-path app, and the merge strategy that saved us from 45-file conflict hell.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Codebase
&lt;/h2&gt;

&lt;p&gt;A production SaaS platform — React 18, Vite, Tailwind CSS, shadcn UI, Firebase Realtime Database, React Query, Zustand. Over 500 files, 30+ shadcn UI components, active development by multiple engineers.&lt;/p&gt;

&lt;p&gt;Everything lived under one &lt;code&gt;src/&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;acme-dashboard/
├── src/
│   ├── components/
│   │   ├── ui/           ← 30+ shadcn components
│   │   ├── AppSidebar/
│   │   ├── ProfileView/
│   │   └── ...
│   ├── features/         ← Feature modules
│   ├── pages/
│   ├── hooks/
│   ├── stores/
│   ├── contexts/
│   └── configs/
│       └── firebase-config.js
├── public/
├── vite.config.js
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worked fine until a second app needed the same shadcn components. Copy-pasting 30+ UI components across repos and keeping them in sync wasn't sustainable. We needed a shared package.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why pnpm + Turborepo
&lt;/h2&gt;

&lt;p&gt;We evaluated three setups:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setup&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;npm workspaces&lt;/td&gt;
&lt;td&gt;Zero config, familiar&lt;/td&gt;
&lt;td&gt;Slow installs, phantom dependencies from hoisting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;yarn berry (PnP)&lt;/td&gt;
&lt;td&gt;Fast, strict&lt;/td&gt;
&lt;td&gt;Breaks many packages, IDE support is flaky&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;pnpm workspaces + Turborepo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fast installs, strict isolation, task orchestration&lt;/td&gt;
&lt;td&gt;Singleton issues (we'll get to this)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;pnpm won because of disk efficiency (content-addressable store), strict module isolation (no phantom dependencies), and speed. Turborepo added parallel task execution and caching on top.&lt;/p&gt;

&lt;p&gt;The trade-off we didn't know about yet: pnpm's strict isolation would break Firebase in a way that produces zero warnings at install time and only fails at runtime. More on that later.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Target Structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;acme-dashboard/
├── apps/
│   └── dashboard/          # @acme/dashboard
│       ├── src/
│       │   ├── components/ # Dashboard-specific components
│       │   ├── features/
│       │   ├── pages/
│       │   └── ...
│       ├── vite.config.js
│       └── package.json
├── packages/
│   └── ui/                 # @acme/ui
│       ├── src/
│       │   ├── components/ # Shared shadcn components
│       │   ├── styles/
│       │   └── lib/utils.js
│       └── package.json
├── scripts/
│   └── migrate-to-monorepo.sh
├── pnpm-workspace.yaml
├── turbo.json
└── package.json            # Root workspace config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two decisions were made upfront:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Only shadcn UI components move to the shared package.&lt;/strong&gt; Feature components, pages, stores — everything else stays in &lt;code&gt;apps/dashboard/&lt;/code&gt;. Extracting too much creates coupling between the shared package and app-specific code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Two components stay in dashboard even though they're in &lt;code&gt;components/ui/&lt;/code&gt;.&lt;/strong&gt; &lt;code&gt;async-select&lt;/code&gt; depends on React Query hooks. &lt;code&gt;confirm-dialog&lt;/code&gt; uses the app's toast system. Moving them would pull the entire app into &lt;code&gt;packages/ui&lt;/code&gt; as a dependency.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Migration Script
&lt;/h2&gt;

&lt;p&gt;We wrote a 24-step bash script. Not because bash is the right tool — but because the migration is a one-time operation, and a script makes it reproducible. When three feature branches all need migration, running the same script ensures consistency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: Scaffold (Steps 1–10)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create workspace structure&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; apps/dashboard packages/ui/src/&lt;span class="o"&gt;{&lt;/span&gt;components,styles,lib,hooks&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# Move app files&lt;/span&gt;
&lt;span class="nb"&gt;mv &lt;/span&gt;src/ apps/dashboard/src/
&lt;span class="nb"&gt;mv &lt;/span&gt;public/ apps/dashboard/public/
&lt;span class="nb"&gt;mv &lt;/span&gt;vite.config.js apps/dashboard/
&lt;span class="nb"&gt;mv &lt;/span&gt;index.html apps/dashboard/
&lt;span class="c"&gt;# ... tailwind.config, postcss.config, etc.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate workspace config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# pnpm-workspace.yaml&lt;/span&gt;
&lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;apps/*"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;packages/*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;turbo.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://turbo.build/schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tasks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"persistent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"cache"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"dependsOn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"^build"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"outputs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"build/**"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;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;h3&gt;
  
  
  Phase 2: Extract Shared UI (Steps 11–20)
&lt;/h3&gt;

&lt;p&gt;This is where the decisions matter. We move shadcn components but need to handle their dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Move components&lt;/span&gt;
&lt;span class="nb"&gt;mv &lt;/span&gt;apps/dashboard/src/components/ui/&lt;span class="k"&gt;*&lt;/span&gt; packages/ui/src/components/
&lt;span class="c"&gt;# Keep the two that depend on app code&lt;/span&gt;
&lt;span class="nb"&gt;mv &lt;/span&gt;packages/ui/src/components/async-select.jsx apps/dashboard/src/components/ui/
&lt;span class="nb"&gt;mv &lt;/span&gt;packages/ui/src/components/confirm-dialog.jsx apps/dashboard/src/components/ui/

&lt;span class="c"&gt;# Move shared utilities&lt;/span&gt;
&lt;span class="nb"&gt;mv &lt;/span&gt;apps/dashboard/src/lib/utils.js packages/ui/src/lib/
&lt;span class="nb"&gt;mv &lt;/span&gt;apps/dashboard/src/styles/global.css packages/ui/src/styles/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shared package's &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@acme/ui"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"./components/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src/components/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"./styles/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src/styles/*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"./lib/*"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src/lib/*"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"peerDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^18.0.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We defined &lt;code&gt;exports&lt;/code&gt; for correctness, but — spoiler — they won't be what actually resolves the imports. More on that in the next section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 3: Rewrite Imports (Steps 21–23)
&lt;/h3&gt;

&lt;p&gt;Every file that imported from &lt;code&gt;~/components/ui/&lt;/code&gt; needs to point to &lt;code&gt;@acme/ui/components/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find apps/dashboard/src &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.jsx"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s|from "~/components/ui/\([^"]*\)"|from "@acme/ui/components/\1"|g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"s|from '~/components/ui/&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="s2"&gt;[^']*&lt;/span&gt;&lt;span class="se"&gt;\)&lt;/span&gt;&lt;span class="s2"&gt;'|from '@acme/ui/components/&lt;/span&gt;&lt;span class="se"&gt;\1&lt;/span&gt;&lt;span class="s2"&gt;'|g"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;{}&lt;/span&gt; +

&lt;span class="c"&gt;# Revert the two excluded components&lt;/span&gt;
find apps/dashboard/src &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.jsx"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s|from "@acme/ui/components/async-select"|from "~/components/ui/async-select"|g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'s|from "@acme/ui/components/confirm-dialog"|from "~/components/ui/confirm-dialog"|g'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;{}&lt;/span&gt; +
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Phase 4: Verify (Step 24)
&lt;/h3&gt;

&lt;p&gt;This step exists because imports will break again — every cherry-pick, merge, or branch integration can reintroduce old-style imports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;BROKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;find apps/dashboard/src &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.jsx"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.js"&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="s1"&gt;'from "~/components/ui/'&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; + 2&amp;gt;/dev/null | &lt;span class="se"&gt;\&lt;/span&gt;
    xargs &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'from "~/components/ui/'&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"async-select"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"confirm-dialog"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BROKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;warn &lt;span class="s2"&gt;"Found broken ~/components/ui/ imports — fixing..."&lt;/span&gt;
    &lt;span class="c"&gt;# ... same sed replacement as above&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This step is idempotent. Running it on already-fixed code changes nothing. We ended up running it five more times over the next week as branches merged in.&lt;/p&gt;




&lt;h2&gt;
  
  
  The First Invisible Bug: CSS Imports Don't Resolve Exports
&lt;/h2&gt;

&lt;p&gt;After running the script and &lt;code&gt;pnpm install&lt;/code&gt;, we started the dev server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[postcss] ENOENT: no such file or directory, open '@acme/ui/styles/globals.css'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The import in &lt;code&gt;apps/dashboard/src/styles/global.css&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"@acme/ui/styles/globals.css"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'd set up &lt;code&gt;exports&lt;/code&gt; in &lt;code&gt;packages/ui/package.json&lt;/code&gt; to handle this. But &lt;strong&gt;CSS &lt;code&gt;@import&lt;/code&gt; statements don't resolve through Node.js &lt;code&gt;exports&lt;/code&gt; fields.&lt;/strong&gt; PostCSS and Vite's CSS pipeline bypass the module system entirely. They look for the file on disk, following the package's &lt;code&gt;main&lt;/code&gt; field or the literal path — never &lt;code&gt;exports&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a fundamental gap. The &lt;code&gt;exports&lt;/code&gt; field was designed for JavaScript modules. CSS tooling predates it and has no integration with it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The fix
&lt;/h3&gt;

&lt;p&gt;Skip &lt;code&gt;exports&lt;/code&gt; for resolution and use a Vite alias:&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;// apps/dashboard/vite.config.js&lt;/span&gt;
&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;alias&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;~&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@acme/ui&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;replacement&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../packages/ui/src&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The alias resolves &lt;code&gt;@acme/ui/styles/globals.css&lt;/code&gt; to &lt;code&gt;../../packages/ui/src/styles/globals.css&lt;/code&gt; &lt;strong&gt;before&lt;/strong&gt; the CSS pipeline ever sees it. Works for JS imports, CSS imports, and dynamic imports.&lt;/p&gt;

&lt;p&gt;We kept &lt;code&gt;exports&lt;/code&gt; in the package.json for documentation purposes, but the alias is what actually resolves everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The thing that trips people up&lt;/strong&gt;: this only matters for packages that contain non-JS assets (CSS, images, fonts). Pure JS packages work fine with &lt;code&gt;exports&lt;/code&gt;. The moment you share styles across workspaces, you need the alias.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Second Invisible Bug: pnpm Breaks Firebase
&lt;/h2&gt;

&lt;p&gt;The CSS fix let the dev server start. The app loaded to a white screen with this in the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uncaught Error: Service database is not available
    at firebase-config.js:46:27
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Line 46: &lt;code&gt;export const realTimeDB = getDatabase(app);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Same code, same Firebase version, same config. The only change was the package manager.&lt;/p&gt;

&lt;h3&gt;
  
  
  The debugging journey
&lt;/h3&gt;

&lt;p&gt;We started with the obvious — is &lt;code&gt;firebase/database&lt;/code&gt; installed? Yes. Is &lt;code&gt;app&lt;/code&gt; initialized? Yes. Is &lt;code&gt;getDatabase&lt;/code&gt; imported correctly? Yes.&lt;/p&gt;

&lt;p&gt;We added console logs:&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firebaseConfig&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="s2"&gt;App:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;app&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="c1"&gt;// "[DEFAULT]" ✓&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="s2"&gt;App options:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Full config ✓&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getDatabase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// 💥 Error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app was initialized. The database just couldn't see it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How pnpm isolates modules
&lt;/h3&gt;

&lt;p&gt;npm and yarn use a flat &lt;code&gt;node_modules/&lt;/code&gt;. Every package can see every other package. pnpm uses a &lt;strong&gt;virtual store&lt;/strong&gt; — each package gets its own isolated dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/
├── .pnpm/
│   ├── @firebase+app@0.11.4/
│   │   └── node_modules/
│   │       └── @firebase/app/        ← copy A
│   └── @firebase+database@1.0.20/
│       └── node_modules/
│           └── @firebase/database/   ← can't see copy A
│               └── (no @firebase/app here)
└── @firebase/app/                     ← hoisted symlink
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@firebase/database&lt;/code&gt; needs &lt;code&gt;@firebase/app&lt;/code&gt; but doesn't declare it as a direct dependency (it's a peer dependency expected to be hoisted). In npm/yarn, hoisting makes it available everywhere. In pnpm, the package at &lt;code&gt;.pnpm/@firebase+database@1.0.20/&lt;/code&gt; can't traverse up to find the hoisted copy.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Vite does with this
&lt;/h3&gt;

&lt;p&gt;Vite's dev server resolves imports on the fly. When &lt;code&gt;@firebase/database&lt;/code&gt; internally imports &lt;code&gt;@firebase/app&lt;/code&gt;, Vite starts resolution from the database package's location in &lt;code&gt;.pnpm/&lt;/code&gt;. It doesn't find &lt;code&gt;@firebase/app&lt;/code&gt; there, so it tries... and either finds a different copy or fails silently by creating a new instance.&lt;/p&gt;

&lt;p&gt;The result: two separate &lt;code&gt;@firebase/app&lt;/code&gt; instances. Your code registers the Firebase app in instance A. &lt;code&gt;@firebase/database&lt;/code&gt; looks for it in instance B. The registry is empty. "Service not available."&lt;/p&gt;

&lt;p&gt;We confirmed this by checking the actual paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Where does @firebase/database live?&lt;/span&gt;
&lt;span class="nb"&gt;realpath &lt;/span&gt;node_modules/.pnpm/@firebase+database@1.0.20/node_modules/@firebase/database
&lt;span class="c"&gt;# → /Users/.../node_modules/.pnpm/@firebase+database@1.0.20/.../@firebase/database&lt;/span&gt;

&lt;span class="c"&gt;# Can it see @firebase/app?&lt;/span&gt;
&lt;span class="nb"&gt;ls &lt;/span&gt;node_modules/.pnpm/@firebase+database@1.0.20/node_modules/@firebase/app
&lt;span class="c"&gt;# → No such file or directory&lt;/span&gt;

&lt;span class="c"&gt;# Where is @firebase/app actually?&lt;/span&gt;
&lt;span class="nb"&gt;realpath &lt;/span&gt;node_modules/@firebase/app
&lt;span class="c"&gt;# → /Users/.../node_modules/.pnpm/@firebase+app@0.11.4/.../@firebase/app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two completely isolated paths. No way for one to find the other.&lt;/p&gt;

&lt;h3&gt;
  
  
  The fix
&lt;/h3&gt;

&lt;p&gt;Two Vite config options, &lt;strong&gt;both required&lt;/strong&gt;:&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;// apps/dashboard/vite.config.js&lt;/span&gt;
&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;dedupe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@firebase/app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@firebase/component&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;optimizeDeps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;include&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firebase/database&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;resolve.dedupe&lt;/code&gt;&lt;/strong&gt;: Forces Vite to always resolve &lt;code&gt;@firebase/app&lt;/code&gt; and &lt;code&gt;@firebase/component&lt;/code&gt; from the project root, regardless of where the import originates. One copy, one singleton, one registry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;optimizeDeps.include&lt;/code&gt; with &lt;code&gt;firebase/database&lt;/code&gt;&lt;/strong&gt;: Forces Vite to pre-bundle the database module during dev server startup. Pre-bundling respects the dedupe setting, so the pre-bundled database uses the same &lt;code&gt;@firebase/app&lt;/code&gt; instance as everything else.&lt;/p&gt;

&lt;p&gt;Without both, it doesn't work:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Config&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Only &lt;code&gt;optimizeDeps.include&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Pre-bundles each module separately → separate instances&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Only &lt;code&gt;resolve.dedupe&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Dev server bypasses pre-bundling, resolves from &lt;code&gt;.pnpm&lt;/code&gt; directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;optimizeDeps.exclude: ["firebase"]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No pre-bundling at all → &lt;code&gt;.pnpm&lt;/code&gt; resolution → broken&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Neither&lt;/td&gt;
&lt;td&gt;Same as before migration broke it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  This isn't just a Firebase problem
&lt;/h3&gt;

&lt;p&gt;Any library that uses a global singleton pattern will break under pnpm + Vite if the internal packages don't declare proper peer dependencies. Libraries we've seen this affect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Firebase (all services)&lt;/li&gt;
&lt;li&gt;Some analytics SDKs that use a shared event bus&lt;/li&gt;
&lt;li&gt;Auth libraries with global token stores&lt;/li&gt;
&lt;li&gt;Any library where sub-packages need to share state via a common core&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fix is always the same: &lt;code&gt;resolve.dedupe&lt;/code&gt; the core package + include the sub-packages in &lt;code&gt;optimizeDeps&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running the Migration
&lt;/h2&gt;

&lt;p&gt;The complete migration checklist:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Run the script&lt;/span&gt;
bash scripts/migrate-to-monorepo.sh

&lt;span class="c"&gt;# 2. Install dependencies (creates workspace symlinks)&lt;/span&gt;
pnpm &lt;span class="nb"&gt;install&lt;/span&gt;

&lt;span class="c"&gt;# 3. Verify the Vite config has:&lt;/span&gt;
&lt;span class="c"&gt;#    - @acme/ui alias&lt;/span&gt;
&lt;span class="c"&gt;#    - Firebase dedupe&lt;/span&gt;
&lt;span class="c"&gt;#    - Firebase in optimizeDeps.include&lt;/span&gt;

&lt;span class="c"&gt;# 4. Start dev server&lt;/span&gt;
pnpm dev

&lt;span class="c"&gt;# 5. Verify:&lt;/span&gt;
&lt;span class="c"&gt;#    - App loads without white screen&lt;/span&gt;
&lt;span class="c"&gt;#    - Firebase Realtime DB works (no "Service not available")&lt;/span&gt;
&lt;span class="c"&gt;#    - All UI components render (no broken imports)&lt;/span&gt;
&lt;span class="c"&gt;#    - CSS styles apply (shared globals.css loads)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If step 4 shows errors, the most common causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ENOENT on &lt;code&gt;@acme/ui&lt;/code&gt;&lt;/strong&gt;: Run &lt;code&gt;pnpm install&lt;/code&gt; — the workspace symlink doesn't exist yet&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Service X is not available"&lt;/strong&gt;: Add the Firebase service to &lt;code&gt;resolve.dedupe&lt;/code&gt; and &lt;code&gt;optimizeDeps.include&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing component&lt;/strong&gt;: Run Step 24 again — an old import snuck through&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS not loading&lt;/strong&gt;: Check the Vite alias points to the right relative path&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What We Ended Up With
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Root&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"turbo dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev:dashboard"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pnpm --filter @acme/dashboard dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"turbo build"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"turbo format"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ pnpm dev
# Starts dashboard on localhost:3002
# Turborepo orchestrates all workspace tasks in parallel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dashboard works exactly as before — same features, same routes, same behavior. But now &lt;code&gt;packages/ui&lt;/code&gt; is a shared dependency that any new app can import from. And that's exactly what we needed for &lt;a href="https://dev.to/ashishxcode/turborepo-vite-with-react-monorepo-part-2-29ej"&gt;Part 2&lt;/a&gt;, where we add a second app, integrate a 110-file feature branch, and learn why you should never merge two independently-migrated branches.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next: Part 2 — Feature Branches, Sub-Path Apps, and the Merge Strategy Nobody Tells You About&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Frontend Hiring Feels Broken</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Tue, 16 Dec 2025 12:34:53 +0000</pubDate>
      <link>https://dev.to/ashishxcode/frontend-hiring-feels-broken-2999</link>
      <guid>https://dev.to/ashishxcode/frontend-hiring-feels-broken-2999</guid>
      <description>&lt;p&gt;For years, I was on one side of the table &lt;strong&gt;giving frontend interviews&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Recently, I moved to the other side and started &lt;strong&gt;taking interviews&lt;/strong&gt; as well.&lt;br&gt;
That shift changed how I look at frontend hiring entirely.&lt;/p&gt;

&lt;p&gt;A pattern kept repeating.&lt;/p&gt;

&lt;p&gt;Candidates could confidently explain &lt;strong&gt;closures, promises, and array methods&lt;/strong&gt;.&lt;br&gt;
But when asked to build a &lt;strong&gt;real UI&lt;/strong&gt;, things often started to fall apart.&lt;/p&gt;

&lt;p&gt;Not because they were bad engineers.&lt;br&gt;
But because we were testing the &lt;strong&gt;wrong things&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Frontend Interviews Commonly Get Wrong
&lt;/h2&gt;

&lt;p&gt;Most frontend interviews still heavily focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data Structures &amp;amp; Algorithms (DSA)&lt;/li&gt;
&lt;li&gt;Abstract algorithmic puzzles&lt;/li&gt;
&lt;li&gt;Problems that rarely appear in everyday frontend work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These questions do test logical thinking.&lt;br&gt;
But they fail to measure &lt;strong&gt;UI engineering skills&lt;/strong&gt;, which is where frontend engineers spend most of their time.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Frontend Engineers Actually Do Day-to-Day
&lt;/h2&gt;

&lt;p&gt;In real product teams, frontend engineers typically spend their time on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building clean, accessible, and responsive user interfaces&lt;/li&gt;
&lt;li&gt;Managing state and asynchronous flows&lt;/li&gt;
&lt;li&gt;Integrating APIs and handling imperfect, real-world data&lt;/li&gt;
&lt;li&gt;Debugging tricky production issues&lt;/li&gt;
&lt;li&gt;Thinking deeply about UX, performance, and edge cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the skills that separate an &lt;em&gt;average&lt;/em&gt; frontend developer from a &lt;em&gt;great&lt;/em&gt; one.&lt;/p&gt;

&lt;p&gt;Not how fast someone can reverse a linked list.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why UI / Machine Rounds Are So Effective
&lt;/h2&gt;

&lt;p&gt;This is where &lt;strong&gt;UI-focused or machine coding rounds&lt;/strong&gt; shine.&lt;/p&gt;

&lt;p&gt;Even a small task — such as building a form, a card layout, or an interactive component — reveals a lot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How a candidate structures components&lt;/li&gt;
&lt;li&gt;Their approach to state and data flow&lt;/li&gt;
&lt;li&gt;Code readability and organization&lt;/li&gt;
&lt;li&gt;Debugging mindset&lt;/li&gt;
&lt;li&gt;Attention to UX and small details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my experience, a &lt;strong&gt;45-minute UI task&lt;/strong&gt; often provides more signal than a &lt;strong&gt;60-minute algorithm round&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  DSA vs UI Rounds: Finding the Right Balance
&lt;/h2&gt;

&lt;p&gt;DSA is not useless.&lt;/p&gt;

&lt;p&gt;It helps assess:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logical thinking&lt;/li&gt;
&lt;li&gt;Problem-solving fundamentals&lt;/li&gt;
&lt;li&gt;Backend-heavy or system-oriented roles&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, for &lt;strong&gt;UI-heavy frontend roles&lt;/strong&gt;, DSA should &lt;strong&gt;support the hiring process, not lead it&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;UI and machine coding rounds should be the primary filter.&lt;br&gt;
Real-world frontend scenarios should drive hiring decisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rethinking Frontend Hiring
&lt;/h2&gt;

&lt;p&gt;If we want better frontend engineers, we need better frontend interviews.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Evaluating real UI skills&lt;/li&gt;
&lt;li&gt;Simulating actual frontend work&lt;/li&gt;
&lt;li&gt;Measuring how candidates think about UX, performance, and maintainability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not just how well they perform in algorithm puzzles.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Frontend engineering is not “just JavaScript.”&lt;/p&gt;

&lt;p&gt;It’s UI architecture, state management, user experience, performance, and product thinking — all working together.&lt;/p&gt;

&lt;p&gt;Our interview processes should reflect that reality.&lt;/p&gt;




&lt;h3&gt;
  
  
  How does your team evaluate frontend engineers today?
&lt;/h3&gt;

&lt;p&gt;I’d love to hear different perspectives in the comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Frontend Isn’t “Just UI” Anymore</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Tue, 25 Nov 2025 10:22:00 +0000</pubDate>
      <link>https://dev.to/ashishxcode/frontend-isnt-just-ui-anymore-30a8</link>
      <guid>https://dev.to/ashishxcode/frontend-isnt-just-ui-anymore-30a8</guid>
      <description>&lt;p&gt;For a long time, “frontend” sounded like a thin layer of buttons and CSS on top of the “real” application. Today, that picture is completely broken. Frontend is where product decisions surface, where performance is felt, and where user psychology either works for you or against you. All of that is happening inside a single render cycle. When someone taps a button or loads a page, they are judging not just your design but your product, your engineering, and your understanding of their needs.&lt;/p&gt;

&lt;p&gt;That is why the biggest upgrade for modern frontend developers is not learning yet another framework. The real shift is learning to think in systems. Frameworks come and go, patterns trend and fade, but a systems mindset compounds across teams, codebases, and entire products.&lt;/p&gt;

&lt;p&gt;Think in Systems, Not Screens&lt;/p&gt;

&lt;p&gt;Most frontend pain is not because React or Vue or Svelte are “bad.” It comes from building isolated screens instead of coherent systems. When every feature is a one-off, you end up with duplicated logic, confused state, and fragile flows that are painful to change. Thinking in systems forces you to zoom out and design how pieces connect before you obsess over how they look.&lt;/p&gt;

&lt;p&gt;A systems mindset on the frontend shows up in four habits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Model data clearly, and half your bugs disappear. When your domain is well defined, your UI state stops being a random mix of flags and becomes a predictable reflection of real concepts.
&lt;/li&gt;
&lt;li&gt;Design for performance, and your UI finally feels premium. You naturally reach for techniques like memoization, streaming, pagination, caching, and lazy loading instead of shipping whatever loads first.
&lt;/li&gt;
&lt;li&gt;Align with user intent, and features start feeling “obvious.” The flow matches how users think, decide, and recover from mistakes, so the interface feels intuitive rather than “complex but powerful.”
&lt;/li&gt;
&lt;li&gt;Set clean boundaries, and your codebase stays calm as it grows. Components, hooks, services, and modules each have clear responsibilities, so new features feel like extensions, not surgeries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With these in place, adding a new feature feels like plugging into a stable grid, not rewriting half the app.&lt;/p&gt;

&lt;p&gt;Data Modeling: The Hidden Multiplier&lt;/p&gt;

&lt;p&gt;On the backend, everyone understands that data modeling matters. On the frontend, it is often treated as an afterthought—until the state tree becomes unmanageable. The truth is that a huge amount of UI bugs are actually data modeling problems in disguise. When you do not know exactly what a “campaign,” “user,” or “session” looks like, the UI ends up making random guesses.&lt;/p&gt;

&lt;p&gt;Clear frontend modeling means you use consistent shapes and types for the same concepts across the app. You know which fields are required, which are optional, and how entities relate. Changes in the product then become changes in the model first, and the UI follows. This not only prevents bugs but makes your code more readable, testable, and reusable across features.&lt;/p&gt;

&lt;p&gt;Performance as Part of the Product&lt;/p&gt;

&lt;p&gt;Users never say, “This bundle could use better code splitting,” but they absolutely feel laggy transitions, janky scroll, and spinners that never end. Performance is no longer a “nice to have” optimization for later; it is a core part of how your product is experienced. A fast interface feels trustworthy and thoughtful. A sluggish one feels broken, even if every request technically succeeds.&lt;/p&gt;

&lt;p&gt;When you treat performance as a product concern, you design loading states, error states, and interaction flows deliberately. You choose the right rendering strategy for the use case—whether that is static generation, server-side rendering, streaming, or client-side hydration. You think in terms of perceived performance, not just numbers in a report. The result is a product that feels responsive, even when the underlying work is complex.&lt;/p&gt;

&lt;p&gt;Psychology in Every Interaction&lt;/p&gt;

&lt;p&gt;Modern frontend sits right where human psychology meets software. Every microcopy, color, animation, and layout decision nudges behavior in some direction. Good frontend engineering respects this. It recognizes that a confirmation dialog can reduce anxiety, a progress bar can reduce perceived wait time, and a clear empty state can turn confusion into clarity.&lt;/p&gt;

&lt;p&gt;Aligning with user intent means understanding what someone is trying to do at each step and shaping the UI around that. Are they exploring, committing, editing, or recovering from a mistake? The frontend is the place where those states become real. When you design with intent, your features feel natural—not because they are trivial, but because they were built around how people actually think.&lt;/p&gt;

&lt;p&gt;From Component Coder to Product Engineer&lt;/p&gt;

&lt;p&gt;Anyone can learn how to write components. The real differentiator is understanding why things are done a certain way. Engineers who ask “Why is this flow designed like this?” or “What trade-off are we making here?” end up influencing product direction, not just closing tickets. They see the frontend as a living system that connects design, backend, business goals, and user behavior.&lt;/p&gt;

&lt;p&gt;Frontend is not just evolving fast; it is evolving deeper. The surface area of tools will keep changing, but the depth of thinking is what turns a developer into an engineer and an engineer into a product partner.&lt;/p&gt;

&lt;p&gt;So here is the question to leave you with: What is one mindset shift that actually made you a better frontend engineer—not just a faster one?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>frontend</category>
    </item>
    <item>
      <title>What’s New in React 19.2?</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Fri, 03 Oct 2025 06:09:56 +0000</pubDate>
      <link>https://dev.to/ashishxcode/whats-new-in-react-192-1b52</link>
      <guid>https://dev.to/ashishxcode/whats-new-in-react-192-1b52</guid>
      <description>&lt;p&gt;React 19.2, released in September 2025, is a feature-packed update focused on improving rendering performance, developer experience, and server-side rendering capabilities. For frontend engineers striving to build faster and more responsive applications, this release introduces several valuable additions worth exploring.&lt;/p&gt;

&lt;p&gt;In this article, we'll walk through the highlights of React 19.2—including the new &lt;code&gt;&amp;lt;Activity /&amp;gt;&lt;/code&gt; component, the &lt;code&gt;useEffectEvent&lt;/code&gt; hook, enhanced profiling tools, and partial pre-rendering—and show you how these can help optimize your apps.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;New capabilities in React 19.2:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;&amp;lt;Activity /&amp;gt;&lt;/code&gt; component&lt;/strong&gt; for fine-grained control over rendering and navigation speed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;useEffectEvent&lt;/code&gt; hook&lt;/strong&gt; to elegantly solve the common "stale closure" problem in effects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partial pre-rendering APIs&lt;/strong&gt; to serve static shells from CDNs and defer dynamic content hydration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhanced profiling&lt;/strong&gt; with Performance Tracks integrated into Chrome DevTools&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smarter batching&lt;/strong&gt; of Suspense boundaries in SSR for smoother hydration and transitions&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;&amp;lt;Activity /&amp;gt;&lt;/code&gt; Component: Smarter UI Visibility and Resource Management
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;Activity /&amp;gt;&lt;/code&gt; component introduces a powerful new way to manage UI visibility in your React applications. Unlike traditional conditional rendering that fully unmounts hidden components, &lt;code&gt;&amp;lt;Activity /&amp;gt;&lt;/code&gt; preserves component state while pausing rendering and effects when components aren't visible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;p&gt;This pattern is especially valuable for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tabbed navigation&lt;/strong&gt; where users frequently switch between views&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex SPAs&lt;/strong&gt; with multiple concurrent UI states&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance-critical apps&lt;/strong&gt; where you want to minimize unnecessary re-renders&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When a component is hidden, it maintains its state without consuming rendering resources. When it becomes visible again, it resumes efficiently without needing to reconstruct its state from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Building Efficient Tabs
&lt;/h3&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="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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Activity&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;Tabs&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;activeTab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setActiveTab&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;nav&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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setActiveTab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Home&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;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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setActiveTab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Video&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;nav&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;hr&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Activity&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;activeTab&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home&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;visible&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;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Home&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Activity&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;activeTab&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video&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;visible&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;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Video&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Activity&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this approach, switching between tabs is instant and resource-efficient. The &lt;code&gt;Video&lt;/code&gt; component maintains its playback position and state even when hidden, providing a seamless user experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learn more:&lt;/strong&gt; &lt;a href="https://react.dev/reference/react/Activity" rel="noopener noreferrer"&gt;Activity Component Documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;useEffectEvent&lt;/code&gt;: Solving the Stale Closure Problem
&lt;/h2&gt;

&lt;p&gt;One of the most common pitfalls in React development has been the "stale closure" problem—when effect callbacks reference outdated props or state values. The new &lt;code&gt;useEffectEvent&lt;/code&gt; hook provides an elegant solution by ensuring callbacks always access the latest values without requiring them in the dependency array.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem It Solves
&lt;/h3&gt;

&lt;p&gt;Previously, developers had to choose between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding all dependencies to the effect array (causing unnecessary re-runs)&lt;/li&gt;
&lt;li&gt;Omitting dependencies (risking stale closures and bugs)&lt;/li&gt;
&lt;li&gt;Using workarounds like &lt;code&gt;useRef&lt;/code&gt; (adding complexity)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffectEvent&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="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ChatRoom&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&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;onConnected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useEffectEvent&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;showNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Connected!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wss://server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;connection&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;connected&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;onConnected&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// Always uses the latest theme value&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;disconnect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;  &lt;span class="c1"&gt;// Only roomId in dependencies&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pattern is invaluable for effects that define callbacks or event handlers, particularly for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebSocket event handlers&lt;/li&gt;
&lt;li&gt;Timer callbacks&lt;/li&gt;
&lt;li&gt;Subscription event listeners&lt;/li&gt;
&lt;li&gt;Analytics tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Only call &lt;code&gt;useEffectEvent&lt;/code&gt; callbacks inside an effect. It's not a replacement for effect dependencies but a specialized tool for stable event handlers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Learn more:&lt;/strong&gt; &lt;a href="https://react.dev/reference/react/useEffectEvent" rel="noopener noreferrer"&gt;useEffectEvent Hook Documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Partial Pre-rendering: Static Shells with Dynamic Hydration
&lt;/h2&gt;

&lt;p&gt;React 19.2 introduces powerful APIs for partial pre-rendering, allowing you to pre-render static parts of your application while deferring dynamic content. This technique dramatically improves initial load times and Core Web Vitals metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Architecture
&lt;/h3&gt;

&lt;p&gt;The workflow splits rendering into two phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Build/Server time:&lt;/strong&gt; Pre-render static shells to HTML&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Client time:&lt;/strong&gt; Resume and hydrate only dynamic sections&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This means your static content can be served instantly from a CDN while JavaScript loads and hydrates interactive elements in the background.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation Pattern
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;prerender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resumeAndPrerender&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-dom/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Pre-render app to 'prelude' and 'postponed' parts&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;prelude&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;postponed&lt;/span&gt; &lt;span class="p"&gt;}&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;prerender&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;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;abortController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Serve prelude immediately (static HTML)&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="c1"&gt;// Later, resume hydration for dynamic content&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resumedStream&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;resume&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;App&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;postponed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Or combine both operations for static output&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;prelude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;staticHTML&lt;/span&gt; &lt;span class="p"&gt;}&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;resumeAndPrerender&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;App&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;postponedState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Faster first paint&lt;/strong&gt; by serving static HTML immediately&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improved Core Web Vitals&lt;/strong&gt; (LCP, FID, CLS)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better perceived performance&lt;/strong&gt; as users see content instantly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reduced JavaScript overhead&lt;/strong&gt; by deferring non-critical hydration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach is particularly effective for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Marketing pages with dynamic widgets&lt;/li&gt;
&lt;li&gt;E-commerce sites with personalized sections&lt;/li&gt;
&lt;li&gt;Content-heavy applications with interactive features&lt;/li&gt;
&lt;li&gt;Large enterprise applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Learn more:&lt;/strong&gt; &lt;a href="https://react.dev/blog/2025/10/01/react-19-2" rel="noopener noreferrer"&gt;Partial Pre-rendering API Documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Enhanced Performance Profiling with Chrome DevTools
&lt;/h2&gt;

&lt;p&gt;React 19.2 integrates seamlessly with Chrome DevTools through new Performance Tracks that provide unprecedented visibility into your application's rendering behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  What You Can Track
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scheduler Track&lt;/strong&gt; shows task priorities and scheduling:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blocking work (high priority)&lt;/li&gt;
&lt;li&gt;Transition work (lower priority)&lt;/li&gt;
&lt;li&gt;Task scheduling and execution timing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Component Track&lt;/strong&gt; displays detailed rendering information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Component render timings&lt;/li&gt;
&lt;li&gt;Effect mount and unmount operations&lt;/li&gt;
&lt;li&gt;Re-render causes and patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Using the Profiler
&lt;/h3&gt;

&lt;p&gt;These tools help you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identify performance bottlenecks in specific components&lt;/li&gt;
&lt;li&gt;Understand when and why components re-render&lt;/li&gt;
&lt;li&gt;Optimize effect timing and dependencies&lt;/li&gt;
&lt;li&gt;Diagnose slow transitions and interactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The integration with Chrome DevTools means you can use familiar debugging workflows while getting React-specific insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with React 19.2
&lt;/h2&gt;

&lt;p&gt;Upgrading to React 19.2 is straightforward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react@19.2 react-dom@19.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Migration Considerations
&lt;/h3&gt;

&lt;p&gt;React 19.2 is designed to be backward compatible with most React 19.x applications. However, consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Testing new features in development before production deployment&lt;/li&gt;
&lt;li&gt;Reviewing the official migration guide for any breaking changes&lt;/li&gt;
&lt;li&gt;Gradually adopting new APIs like &lt;code&gt;&amp;lt;Activity /&amp;gt;&lt;/code&gt; and &lt;code&gt;useEffectEvent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Evaluating partial pre-rendering for performance-critical pages&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;React 19.2 represents a significant step forward in the evolution of React, providing developers with powerful tools to build faster, more responsive, and more maintainable applications. The combination of smarter rendering control through &lt;code&gt;&amp;lt;Activity /&amp;gt;&lt;/code&gt;, cleaner effect management with &lt;code&gt;useEffectEvent&lt;/code&gt;, and advanced SSR capabilities through partial pre-rendering creates new possibilities for application architecture.&lt;/p&gt;

&lt;p&gt;Whether you're building a complex SPA, optimizing an e-commerce platform, or creating a content-rich application, these features offer concrete solutions to common performance and developer experience challenges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ready to explore?&lt;/strong&gt; Check out the official documentation and start experimenting with these features in your next React project. The community is excited to see what you build!&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://react.dev/reference/react/Activity" rel="noopener noreferrer"&gt;Activity Component Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/reference/react/useEffectEvent" rel="noopener noreferrer"&gt;useEffectEvent Hook Reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/blog/2025/10/01/react-19-2" rel="noopener noreferrer"&gt;React 19.2 Release Announcement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.dev/reference/react" rel="noopener noreferrer"&gt;React Official Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>webdev</category>
      <category>programming</category>
      <category>react</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>The Ultimate React Hook Form + Zod Pattern for Reusable Create and Edit Forms</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Thu, 25 Sep 2025 05:20:45 +0000</pubDate>
      <link>https://dev.to/ashishxcode/the-ultimate-react-hook-form-zod-pattern-for-reusable-create-and-edit-forms-38l</link>
      <guid>https://dev.to/ashishxcode/the-ultimate-react-hook-form-zod-pattern-for-reusable-create-and-edit-forms-38l</guid>
      <description>&lt;p&gt;Forms in React have always given me a headache. Every time I started a new project, setting up validation and default values felt like juggling several balls at once. And don't even get me started on updating forms later—if I changed one thing, I had to hunt through multiple files just to make sure everything still worked.&lt;/p&gt;

&lt;p&gt;Then I started building some complex forms for multi-platform score configurations (think: Instagram and YouTube scoring systems), and the complexity got out of hand pretty fast. I knew I had to fix how I managed form validation and defaults, or I'd lose my mind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR: Bundle your Zod schema and default values in a single custom hook to handle both create and edit forms without duplication. Skip to the code examples below if you're impatient like me!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Form Duplication Headaches
&lt;/h2&gt;

&lt;p&gt;If you've worked with React forms for any length of time, this probably sounds familiar:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You create a form with validation&lt;/li&gt;
&lt;li&gt;Later, you need an "edit" version of the same form&lt;/li&gt;
&lt;li&gt;You copy most of your code but need to handle default values differently&lt;/li&gt;
&lt;li&gt;A requirement changes, and you have to update both forms&lt;/li&gt;
&lt;li&gt;You forget to update one of them, and bugs appear&lt;/li&gt;
&lt;li&gt;Repeat until you question your career choices&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I was tired of maintaining nearly identical form configurations across different components. Especially when using libraries like React Hook Form and Zod, it felt like I was duplicating effort for no good reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Custom Schema Hooks
&lt;/h2&gt;

&lt;p&gt;Here's a stripped-down version of what my solution looks like:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useProfileSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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;// Define validation schema once&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Username is required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Must be at least 18&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;default&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="c1"&gt;// Create defaults based on existing data or empty values&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt; &lt;span class="o"&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic happens with that &lt;code&gt;data&lt;/code&gt; parameter. When creating a new form, you don't pass anything. When editing an existing record, you pass in the current values. The hook handles the rest!&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting It To Work
&lt;/h2&gt;

&lt;p&gt;Once I had this pattern down, setting up forms became a breeze. I just call this hook, plug its schema into React Hook Form via the resolver, and use the default values for initializing the form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useForm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-hook-form&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;zodResolver&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hookform/resolvers/zod&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;useProfileSchema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./useProfileSchema&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;ProfileForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;initialData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;onSubmit&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useProfileSchema&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="nx"&gt;initialData&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;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useForm&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;zodResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;defaultValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&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;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="nx"&gt;htmlFor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Username&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; 
          &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; 
          &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enter username&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
          &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-control&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="nx"&gt;htmlFor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Age&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; 
          &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
          &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;age&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;valueAsNumber&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="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enter age&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
          &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-control&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;formState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-check&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; 
          &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subscribe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
          &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkbox&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; 
          &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subscribe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; 
          &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-check-input&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="nx"&gt;htmlFor&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subscribe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;form-check-label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Subscribe&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;newsletter&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/label&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;btn btn-primary mt-3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialData&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Update Profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create Profile&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The beautiful part? This same component works for both creating and editing profiles. All you need to change is whether you pass initialData or not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create new profile&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProfileForm&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCreate&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// Edit existing profile&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProfileForm&lt;/span&gt; &lt;span class="na"&gt;initialData&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;existingProfile&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleUpdate&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Benefits
&lt;/h2&gt;

&lt;p&gt;After implementing this pattern across several projects, here's what I've gained:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Single source of truth&lt;/strong&gt;: Validation rules and defaults live together, so they never get out of sync&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DRY code&lt;/strong&gt;: No more copy-pasting form logic between create and edit pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easier maintenance&lt;/strong&gt;: Need to change a validation rule? Do it once in the hook, not in multiple places&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better code organization&lt;/strong&gt;: All form-related logic has a clear home&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smoother handoff&lt;/strong&gt;: New team members can quickly understand how forms work in our codebase&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Taking It Further: Advanced Patterns
&lt;/h2&gt;

&lt;p&gt;For larger projects, I've extended this pattern in a few ways that have proven invaluable:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Adding TypeScript for Better Safety
&lt;/h3&gt;

&lt;p&gt;Adding TypeScript definitions makes this pattern even more powerful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Define your schema type&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ProfileFormData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="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;useProfileSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProfileFormData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Username is required&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Must be at least 18&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;default&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="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ProfileFormData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt; &lt;span class="o"&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Adding Transformation Functions
&lt;/h3&gt;

&lt;p&gt;Sometimes the data from your API doesn't match exactly what your form needs. Adding transform functions to your hook can solve this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useProfileSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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;// Schema definition...&lt;/span&gt;

    &lt;span class="c1"&gt;// Transform API data to form format&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiToForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiData&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="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newsletter_opt_in&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Transform form data back to API format&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formToApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;user_age&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;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;newsletter_opt_in&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;subscribe&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;apiToForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;18&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;subscribe&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiToForm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formToApi&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Supporting Conditional Validation
&lt;/h3&gt;

&lt;p&gt;For more complex forms where validation rules change based on other fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usePaymentSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;paymentMethod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;credit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
      &lt;span class="na"&gt;creditCardNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;bankAccountNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;optional&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;refine&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentMethod&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;credit&lt;/span&gt;&lt;span class="dl"&gt;"&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;creditCardNumber&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;false&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paymentMethod&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bank&lt;/span&gt;&lt;span class="dl"&gt;"&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;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bankAccountNumber&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;false&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;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="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Please fill in the required payment details&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;paymentMethod&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;// Rest of the hook...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Form Reset and Initial Values
&lt;/h3&gt;

&lt;p&gt;One challenge I encountered was handling form resets properly. Here's a pattern that works well:&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;ProfileForm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;initialData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;onSubmit&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useProfileSchema&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="nx"&gt;initialData&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;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useForm&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;zodResolver&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;defaultValues&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Reset form when initialData changes (useful in edit scenarios)&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;initialData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaults&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;handleReset&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;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;form&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;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;handleSubmit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSubmit&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="cm"&gt;/* Form fields... */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button-group&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;submit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;btn btn-primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialData&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Update&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleReset&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;btn btn-secondary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nx"&gt;Reset&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/form&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Considerations
&lt;/h2&gt;

&lt;p&gt;For larger forms, you might want to optimize your hook further:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useProfileSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="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;// Memoize the data to avoid unnecessary recalculations&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;memoizedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;// Only include keys you care about&lt;/span&gt;
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;
  &lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useMemo&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;// Schema and defaults logic&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaults&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;memoizedData&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// Use the memoized data&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Honestly, this small change made my workflow so much smoother. If you've ever felt frustrated juggling form state and validation across different files, give this pattern a shot. It saved me a lot of time and stress, and I bet it'll help you too.&lt;/p&gt;

&lt;p&gt;The best part? This pattern works with any form library in React - not just React Hook Form. The principle of bundling schema and defaults in a custom hook is universally applicable.&lt;/p&gt;

&lt;p&gt;What form patterns have you found useful in your React projects? Let me know in the comments!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Follow me for more React patterns and tips that make development smoother and more enjoyable. If this article helped you, consider sharing it with your team!&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How useSyncExternalStore Transformed My React State Management</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Wed, 24 Sep 2025 06:21:31 +0000</pubDate>
      <link>https://dev.to/ashishxcode/how-usesyncexternalstore-transformed-my-react-state-management-i31</link>
      <guid>https://dev.to/ashishxcode/how-usesyncexternalstore-transformed-my-react-state-management-i31</guid>
      <description>

&lt;p&gt;As a React developer, I’ve always been conscious of how I manage state, especially when integrating with external data sources or global stores. Over the years, I’ve used different patterns—&lt;code&gt;useEffect&lt;/code&gt; hooks, custom subscriptions, and third-party libraries like Redux or Zustand. But recently, while working on a React 18+ project, I discovered &lt;code&gt;useSyncExternalStore&lt;/code&gt;, and it genuinely changed my perspective on syncing external state with React components.&lt;/p&gt;




&lt;h3&gt;
  
  
  My Initial Pain Points with External Store Subscriptions
&lt;/h3&gt;

&lt;p&gt;In one project, I needed to build a real-time dashboard listening to data from a custom in-memory store. Initially, I wrote the subscription logic using &lt;code&gt;useEffect&lt;/code&gt; and &lt;code&gt;useState&lt;/code&gt;. However, I ran into tricky bugs—sometimes the UI wouldn’t reflect the latest state correctly during fast updates, and in SSR scenarios, the server-rendered content didn’t always match the hydrated client UI.&lt;/p&gt;

&lt;p&gt;The problem was subtle but frustrating. React’s concurrent rendering means components can render multiple times before committing, and the old subscription patterns weren’t designed for that.&lt;/p&gt;




&lt;h3&gt;
  
  
  Discovering &lt;code&gt;useSyncExternalStore&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When I came across React’s &lt;code&gt;useSyncExternalStore&lt;/code&gt; hook, it was a lightbulb moment. This hook is created specifically to handle subscriptions to external “stores” safely with React 18’s concurrent features and supports server-side rendering.&lt;/p&gt;

&lt;p&gt;It allows React to safely read a snapshot of external state and subscribe to updates, so UI stays consistent without race conditions or hydration mismatches.&lt;/p&gt;




&lt;h3&gt;
  
  
  How I Used It in My Project
&lt;/h3&gt;

&lt;p&gt;I refactored my custom store and React component to use &lt;code&gt;useSyncExternalStore&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// My custom store&lt;/span&gt;
&lt;span class="kd"&gt;let&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;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;listeners&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&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;counterStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getSnapshot&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;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&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="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;listener&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;And in React:&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;useSyncExternalStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;counterStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./counterStore&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="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSyncExternalStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;counterStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;counterStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSnapshot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;counterStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSnapshot&lt;/span&gt; &lt;span class="c1"&gt;// SSR fallback&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;counterStore&lt;/span&gt;&lt;span class="p"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This switch made UI updates flawless—no more stale renders or mismatches. Updates were consistent and aligned perfectly with React 18’s rendering lifecycle.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why This Matters for React Developers
&lt;/h3&gt;

&lt;p&gt;Before this, I was tempted to keep using familiar patterns with &lt;code&gt;useEffect&lt;/code&gt; for subscriptions, but these do not future-proof apps for the latest React features. SSR was especially complicated without snapshot fallbacks.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useSyncExternalStore&lt;/code&gt; solves these by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Providing React the current snapshot during render.&lt;/li&gt;
&lt;li&gt;Registering subscriptions safe for concurrent renders.&lt;/li&gt;
&lt;li&gt;Offering a server snapshot for consistent SSR.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Other Use Cases I Tried
&lt;/h3&gt;

&lt;p&gt;I also used &lt;code&gt;useSyncExternalStore&lt;/code&gt; to track window width via a custom hook. It replaced my old &lt;code&gt;useEffect&lt;/code&gt; based event listener pattern and handled SSR with a sensible default width fallback.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useWindowWidth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useSyncExternalStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&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="mi"&gt;1024&lt;/span&gt; &lt;span class="c1"&gt;// default width for SSR&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;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Switching to &lt;code&gt;useSyncExternalStore&lt;/code&gt; was a small change with a big impact on the reliability and maintainability of my React apps, especially with concurrent features and SSR becoming the norm.&lt;/p&gt;

&lt;p&gt;If managing external subscriptions or custom global state today, I highly recommend exploring this hook. It’s already powering popular state libraries under the hood, so take advantage of it directly!&lt;/p&gt;

&lt;p&gt;Hope my experience helps others avoid the pitfalls I ran into and build more resilient React apps.&lt;/p&gt;




&lt;p&gt;Would love to hear how others are using this hook or challenges faced!&lt;/p&gt;




</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>How to Debug Node.js Applications Like a Pro</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Wed, 17 Jul 2024 05:01:26 +0000</pubDate>
      <link>https://dev.to/ashishxcode/how-to-debug-nodejs-applications-like-a-pro-4aon</link>
      <guid>https://dev.to/ashishxcode/how-to-debug-nodejs-applications-like-a-pro-4aon</guid>
      <description>&lt;p&gt;After working with Node.js for several years, I've encountered and overcome numerous debugging challenges. This guide shares practical insights and techniques I've found effective. Whether you're new to Node.js or looking to refine your debugging skills, I hope these experiences prove useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Console Logging: A Starting Point
&lt;/h2&gt;

&lt;p&gt;Most developers start with console logging, and it's still a useful tool in many situations:&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;function&lt;/span&gt; &lt;span class="nf"&gt;processUser&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="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;Processing user:&lt;/span&gt;&lt;span class="dl"&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="k"&gt;if &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;age&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;18&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;User is under 18&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Too young&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;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;User is adult, continuing...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// More processing...&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Processed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While effective for quick checks, this method can clutter your code. For more complex debugging, consider using the built-in Node.js debugger or IDE integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leveraging the Node.js Debugger
&lt;/h2&gt;

&lt;p&gt;The Node.js debugger is a powerful tool that's often underutilized. Here's how to get started:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--inspect-brk&lt;/span&gt; my-script.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open Chrome and navigate to &lt;code&gt;chrome://inspect&lt;/code&gt;. This allows you to use Chrome DevTools to debug your Node.js application, which is particularly useful for inspecting variables and stepping through code.&lt;/p&gt;

&lt;h2&gt;
  
  
  IDE Integration: Streamlining the Process
&lt;/h2&gt;

&lt;p&gt;Visual Studio Code offers excellent debugging capabilities for Node.js. A basic &lt;code&gt;launch.json&lt;/code&gt; configuration that I've found useful is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.2.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"configurations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"request"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"launch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Debug Current File"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"program"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${file}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"skipFiles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;node_internals&amp;gt;/**"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup allows you to debug the currently open file by pressing F5, which can significantly speed up the debugging process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Asynchronous Code
&lt;/h2&gt;

&lt;p&gt;Debugging asynchronous code can be challenging. Using async/await has made this process 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="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="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.example.com/users/&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="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;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 data:&lt;/span&gt;&lt;span class="dl"&gt;'&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="k"&gt;throw&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When debugging async functions, setting breakpoints inside both the try block and the catch block can provide valuable insights into the execution flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory Profiling
&lt;/h2&gt;

&lt;p&gt;For performance issues, particularly memory leaks, heap snapshots can be invaluable:&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;heapdump&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;heapdump&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;takeHeapSnapshot&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;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`heap-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;.heapsnapshot`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;heapdump&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filename&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="k"&gt;if &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;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 generate heap snapshot:&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="k"&gt;else&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="s2"&gt;`Heap snapshot written to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;filename&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="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;Analyzing these snapshots in Chrome DevTools can help identify memory issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Quality with ESLint
&lt;/h2&gt;

&lt;p&gt;ESLint can catch many potential issues before they become runtime errors. A basic configuration that I've found helpful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;node&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;es2021&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="na"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eslint:recommended&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;rules&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;no-unused-vars&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="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="na"&gt;argsIgnorePattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;^_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;no-console&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warn&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;allow&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;warn&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;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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;eqeqeq&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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;always&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running ESLint as part of your development workflow can prevent many common mistakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Debugging Techniques
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conditional Breakpoints&lt;/strong&gt;: Useful for debugging specific conditions within loops or frequently called functions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Logpoints&lt;/strong&gt;: Allow adding temporary logging without modifying code, which is particularly useful in production environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Remote Debugging&lt;/strong&gt;: Essential for debugging deployed applications:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   node &lt;span class="nt"&gt;--inspect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.0.0.0:9229 app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use SSH tunneling to connect securely from a local machine.&lt;/p&gt;

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

&lt;p&gt;From my experience, these practices have proven most effective:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Structured Logging&lt;/strong&gt;: Tools like Winston or Pino provide more detailed and easily searchable logs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Type Checking&lt;/strong&gt;: TypeScript or JSDoc can catch many errors at compile-time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Comprehensive Testing&lt;/strong&gt;: Well-written tests often reveal bugs before they reach production.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modular Code&lt;/strong&gt;: Smaller, focused modules are generally easier to debug and maintain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Continuous Integration&lt;/strong&gt;: Automated testing and linting on every code push helps catch issues early.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Debugging is an ongoing learning process. Each project brings new challenges and opportunities to refine these skills. I hope these insights prove helpful in your Node.js development journey.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>node</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Creating an Empty Orphan Branch in Git</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Mon, 18 Mar 2024 05:30:03 +0000</pubDate>
      <link>https://dev.to/ashishxcode/creating-an-empty-orphan-branch-in-git-3ng5</link>
      <guid>https://dev.to/ashishxcode/creating-an-empty-orphan-branch-in-git-3ng5</guid>
      <description>&lt;p&gt;In the world of software development, starting a new project often means creating a fresh branch from the main codebase. However, sometimes, you might want to start from scratch without carrying over any history or code from the main branch. This is where Git's "orphan" branches come into play. In this article, we'll explore how to create an empty orphan branch in Git, providing you with a clean slate for your new projects or ideas.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is an Orphan Branch?
&lt;/h3&gt;

&lt;p&gt;An orphan branch in Git is a branch that has no parent branch, meaning it doesn't contain any of the history from the main branch. This feature is particularly useful when you want to start a new project or test out new ideas without the baggage of the existing codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Create an Orphan Branch
&lt;/h3&gt;

&lt;p&gt;Creating an orphan branch is straightforward. Here's a step-by-step guide:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create the Orphan Branch&lt;/strong&gt;: Use the command
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git checkout --orphan &amp;lt;branch_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to create a new orphan branch. Replace &lt;code&gt;&amp;lt;branch_name&amp;gt;&lt;/code&gt; with the name of your new branch.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clean the Working Directory&lt;/strong&gt;: Execute
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git rm -rf .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to remove all files from the current working directory. This ensures that the new branch starts completely empty.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add New Files&lt;/strong&gt;: Now, you can add any new files you want to include in your project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Commit the Changes&lt;/strong&gt;: Commit the new files to the repository with &lt;code&gt;git commit -m "Initial commit"&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Push the New Branch&lt;/strong&gt;: Make the new branch available on the remote repository by using &lt;code&gt;git push -u origin &amp;lt;branch_name&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In conclusion, creating an empty orphan branch in Git is a powerful tool for developers looking to start a new project from scratch. By leveraging the "orphan" feature, you can ensure that your new project is completely independent of the main branch, allowing for a fresh start without the baggage of previous code or history. Whether you're embarking on a new project or testing out a new idea, an orphan branch offers a clean, isolated environment to work in.&lt;/p&gt;

&lt;p&gt;So, the next time you're starting a new project, consider using Git's orphan branches to give yourself a fresh start. Happy coding!&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>The God Object Dilemma: Simplifying Software Complexity</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Sat, 02 Mar 2024 07:13:10 +0000</pubDate>
      <link>https://dev.to/ashishxcode/the-god-object-dilemma-simplifying-software-complexity-c61</link>
      <guid>https://dev.to/ashishxcode/the-god-object-dilemma-simplifying-software-complexity-c61</guid>
      <description>&lt;p&gt;Hey there! Today, let's talk about something that's a bit like finding a messy closet in your house – the "God object." Now, this isn't some divine entity; it's just a fancy name for a messy chunk of code in software development.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's a God Object?
&lt;/h3&gt;

&lt;p&gt;So, imagine you have this piece of code that seems to know and do everything in your program. It's like your all-knowing friend who tries to solve every problem, whether it's managing data or handling user interactions. But here's the catch – it's a nightmare to understand and work with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spotting the Signs:
&lt;/h3&gt;

&lt;p&gt;How do you know if you've got a God object on your hands?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Does Everything&lt;/strong&gt;: It tries to handle way too many things, like a superhero juggling too many tasks at once.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Everyone Depends on It&lt;/strong&gt;: Other parts of your code rely too much on this one object, making it hard to change anything without causing chaos.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Messy Inside Out&lt;/strong&gt;: Instead of neatly organizing its stuff, it exposes too much information and does too much, like a messy room with stuff strewn everywhere.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Testing Trouble&lt;/strong&gt;: Testing it feels like trying to untangle a knot – it's complicated and often leads to more mess.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why It Matters:
&lt;/h3&gt;

&lt;p&gt;Having a God object in your code can cause some headaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Harder to Change&lt;/strong&gt;: As your code grows, this big guy becomes even harder to understand and modify.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limits Growth&lt;/strong&gt;: It's like a roadblock to making your code better and adding new features because everything depends on it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leads to Debt&lt;/strong&gt;: Ignoring it means you're racking up technical debt – like borrowing trouble from the future.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How to Fix It:
&lt;/h3&gt;

&lt;p&gt;So, how do we deal with this code mess?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Break It Down&lt;/strong&gt;: Split it into smaller, simpler parts that each do one thing well.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Loosen Dependencies&lt;/strong&gt;: Make sure other parts of your code don't rely too heavily on it – they should be more independent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep It Neat&lt;/strong&gt;: Encapsulate its functions neatly so it's easier to understand and work with.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test It Out&lt;/strong&gt;: Make sure everything still works as it should by testing each part separately.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Wrapping Up:
&lt;/h3&gt;

&lt;p&gt;The God object might seem like a big problem, but with a bit of patience and some cleanup, you can tame it. Just like tidying up a messy room, breaking down your code into smaller, simpler pieces can make your programming life a whole lot easier.&lt;/p&gt;

&lt;p&gt;So, next time you're faced with a tangled mess of code, don't panic. Take a deep breath, roll up your sleeves, and start untangling – you've got this!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>discuss</category>
    </item>
    <item>
      <title>5 Powerful HTML Attributes That Don't Require JavaScript</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Wed, 28 Feb 2024 04:14:43 +0000</pubDate>
      <link>https://dev.to/ashishxcode/5-powerful-html-attributes-that-dont-require-javascript-lfb</link>
      <guid>https://dev.to/ashishxcode/5-powerful-html-attributes-that-dont-require-javascript-lfb</guid>
      <description>&lt;p&gt;Certainly! Here's a refined version with code examples for each attribute:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1. &lt;strong&gt;Spellcheck:&lt;/strong&gt;
This attribute ensures that the browser checks spelling within specific elements, such as input fields or text areas, without requiring JavaScript. It's particularly useful for forms or content where accuracy matters.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;textarea&lt;/span&gt; &lt;span class="na"&gt;spellcheck=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;2. &lt;strong&gt;Download:&lt;/strong&gt;
The &lt;code&gt;download&lt;/code&gt; attribute allows users to download resources directly when clicking on a link. It's handy for providing downloadable content like images, documents, or media files without JavaScript.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"path/to/file.pdf"&lt;/span&gt; &lt;span class="na"&gt;download&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Download PDF&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;3. &lt;strong&gt;Video Thumbnail (Poster):&lt;/strong&gt;
By using the &lt;code&gt;poster&lt;/code&gt; attribute in the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; element, you can specify an image to display as a placeholder before the video starts playing. This mimics the functionality of a video thumbnail, enhancing user experience.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;video&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt; &lt;span class="na"&gt;poster=&lt;/span&gt;&lt;span class="s"&gt;"path/to/thumbnail.jpg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;source&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"path/to/video.mp4"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"video/mp4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       Your browser does not support the video tag.
   &lt;span class="nt"&gt;&amp;lt;/video&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;4. &lt;strong&gt;Translate:&lt;/strong&gt;
Employing the &lt;code&gt;translate&lt;/code&gt; attribute, you can specify whether an element's content should be translated when a webpage is translated into another language. Setting it to "no" preserves the original content, useful for brand names or specific terms.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;translate=&lt;/span&gt;&lt;span class="s"&gt;"no"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Your Company Name&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;5. &lt;strong&gt;Autocomplete:&lt;/strong&gt;
The &lt;code&gt;autocomplete&lt;/code&gt; attribute enables or disables autocomplete functionality for input fields, providing control over whether the browser should suggest previously entered values. This can enhance user convenience and streamline form submission.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"on"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;If you'd like to show your support for my work, you can buy me a coffee through the following link: &lt;a href="https://www.buymeacoffee.com/ashishxcode" rel="noopener noreferrer"&gt;Buy Me a Coffee&lt;/a&gt;. Your generosity helps fuel my creativity and allows me to continue sharing valuable content. Thank you for your support!&lt;/p&gt;

&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spellcheck: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/spellcheck" rel="noopener noreferrer"&gt;MDN Web Docs - Spellcheck Attribute&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Download: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-download" rel="noopener noreferrer"&gt;MDN Web Docs - Download Attribute&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Video Thumbnail (Poster): &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video#attr-poster" rel="noopener noreferrer"&gt;MDN Web Docs - Poster Attribute&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Translate: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/translate" rel="noopener noreferrer"&gt;MDN Web Docs - Translate Attribute&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Autocomplete: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete" rel="noopener noreferrer"&gt;MDN Web Docs - Autocomplete Attribute&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>html</category>
      <category>learning</category>
      <category>programming</category>
    </item>
    <item>
      <title>React 19: A Deep Dive into the Upcoming Major Release</title>
      <dc:creator>Ashish Patel</dc:creator>
      <pubDate>Thu, 22 Feb 2024 05:14:44 +0000</pubDate>
      <link>https://dev.to/ashishxcode/react-19-a-deep-dive-into-the-upcoming-major-release-1pa7</link>
      <guid>https://dev.to/ashishxcode/react-19-a-deep-dive-into-the-upcoming-major-release-1pa7</guid>
      <description>&lt;p&gt;The highly anticipated release of React 19 is drawing closer, promising a plethora of exciting new features and performance enhancements. In this article, we'll take a comprehensive look at what React 19 has in store for developers, exploring not only the headline features but also the intricate aspects that empower developers to create more performant, dynamic, and engaging user interfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  React Compiler
&lt;/h2&gt;

&lt;p&gt;Originally a research project, the React compiler has evolved into a vital component of production at &lt;code&gt;Instagram.com.&lt;/code&gt; This innovative tool addresses the issue of excessive re-rendering in React applications, a common pain point for developers. While traditional approaches like manual memoization techniques such as &lt;code&gt;useMemo&lt;/code&gt; and &lt;code&gt;useCallback&lt;/code&gt; have been effective, they can be cumbersome and error-prone.&lt;/p&gt;

&lt;p&gt;The React Compiler takes a different approach, automatically optimizing code without compromising the core mental model of React. It leverages its understanding of both JavaScript and React's rules to safely rewrite code for efficiency. This can significantly improve rendering performance, resulting in smoother user experiences, especially in complex data visualization components that frequently re-render.&lt;/p&gt;

&lt;h2&gt;
  
  
  Action
&lt;/h2&gt;

&lt;p&gt;A groundbreaking addition in React 19 is the introduction of Actions, enabling seamless integration of functions with DOM elements such as &lt;code&gt;&amp;lt;form/&amp;gt;&lt;/code&gt;. With Actions, developers can effortlessly execute both synchronous and asynchronous operations, streamlining data submission management and state updates. This paradigm shift towards unified client and server data handling promises a more cohesive programming model across diverse environments, simplifying form interactions and data submissions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;form action={search}&amp;gt;
  &amp;lt;input name="query" /&amp;gt;
  &amp;lt;button type="submit"&amp;gt;Search&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Server Components: Unleashing the Power of Server-Side Rendering
&lt;/h2&gt;

&lt;p&gt;Slow initial page loads and SEO woes can be addressed with Server Components, a game-changer for React development. By rendering components directly on the server using Server Components, developers can deliver lightning-fast first impressions and boost search engine ranking. This is particularly beneficial for content-heavy applications or those that require high SEO performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Asset Loading: Ensuring a Seamless User Experience
&lt;/h2&gt;

&lt;p&gt;Ever encountered flickering text or layout shifts while waiting for stylesheets or fonts to load? Asset Loading integrates Suspense with resource loading, ensuring a smooth and seamless user experience. For instance, consider a product page with high-resolution images. Asset Loading ensures the image is ready before displaying it, preventing jarring disruptions and creating a polished UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Document Metadata: Taking Control of Head Elements
&lt;/h2&gt;

&lt;p&gt;Managing &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;,&lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tags across different environments can be challenging. Document Metadata offers a solution. For example, consider a blog post with a specific title and description. This built-in support works seamlessly in all scenarios, providing unified control over your document's head section, simplifying SEO management, and branding consistency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web Components: Bridging Frameworks
&lt;/h2&gt;

&lt;p&gt;A long-awaited feature arrives! React 19 embraces Web Components, opening up a world of possibilities and facilitating collaboration across different frameworks. For instance, imagine using a popular date picker component built as a Web Component within your React app. This integration fosters a more unified development ecosystem and allows developers to leverage the best of both worlds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks: Enhancing the Toolkit
&lt;/h2&gt;

&lt;p&gt;While no new core hooks are introduced in React 19, improvements have been made to existing ones. useMemo and useCallback now offer enhanced capabilities for fine-grained memoization, potentially reducing unnecessary re-renders. Additionally, useEffect provides more control over when effects run, allowing for cleaner and more efficient side effects management. Lastly, useImperativeHandle offers streamlined usage for creating ref-like objects within functional components.&lt;/p&gt;

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

&lt;p&gt;React 19 isn't just an upgrade; it's a gateway to a more performant, dynamic, and engaging development experience. From the groundbreaking React Compiler to the seamless integration of Web Components, each feature empowers developers to create user interfaces that shine.&lt;/p&gt;

&lt;p&gt;By actively participating in the development process and learning about these new features, developers can ensure their applications are future-proof and leverage the full potential of React 19. Stay tuned for further updates and dive deeper into the official blog post at React Blog for comprehensive documentation and code examples.&lt;/p&gt;

&lt;p&gt;If you found this article insightful, please consider supporting it by giving it a clap or sharing it with your peers. Stay tuned for more enlightening articles on web development by following me.&lt;/p&gt;

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