<?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: Yogesh Raj Kabilan</title>
    <description>The latest articles on DEV Community by Yogesh Raj Kabilan (@yogeshraj).</description>
    <link>https://dev.to/yogeshraj</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%2F224986%2Fc6b35872-cb79-4958-9c2a-aed820d4afa5.jpg</url>
      <title>DEV Community: Yogesh Raj Kabilan</title>
      <link>https://dev.to/yogeshraj</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yogeshraj"/>
    <language>en</language>
    <item>
      <title>Building a Local-First Task Manager with Next.js and React — Lessons from Planow</title>
      <dc:creator>Yogesh Raj Kabilan</dc:creator>
      <pubDate>Wed, 29 Apr 2026 17:05:48 +0000</pubDate>
      <link>https://dev.to/yogeshraj/building-a-local-first-task-manager-with-nextjs-and-react-lessons-from-planow-bkm</link>
      <guid>https://dev.to/yogeshraj/building-a-local-first-task-manager-with-nextjs-and-react-lessons-from-planow-bkm</guid>
      <description>&lt;p&gt;&lt;em&gt;An honest engineering breakdown of building Planow — the decisions, tradeoffs, and lessons that mattered.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.syedshahab.in/" rel="noopener noreferrer"&gt;Syed&lt;/a&gt; and I had been circling around the idea of building &lt;a href="https://planow.app/" rel="noopener noreferrer"&gt;Planow&lt;/a&gt; for roughly four years. Not continuously — it surfaced the way most side projects do: in moments of frustration, when existing tools felt unnecessarily complex or slow.&lt;/p&gt;

&lt;p&gt;We didn’t want another bloated productivity tool.&lt;/p&gt;

&lt;p&gt;We wanted something that answers one question instantly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“What should I do right now?”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That naturally led us to the Eisenhower Matrix.&lt;/p&gt;

&lt;p&gt;The idea was simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Categorize tasks by urgency and importance&lt;/li&gt;
&lt;li&gt;Make prioritization visual and immediate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But building something that feels instant and intuitive turned out to be the real challenge.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="https://planow.app/" rel="noopener noreferrer"&gt;&lt;strong&gt;Planow&lt;/strong&gt;&lt;/a&gt; is built on a simple idea: your tasks should be fast, private, and available the moment you think of them — not after a network request.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h4&gt;
  
  
  The Stack — Choosing the Future (With Tradeoffs)
&lt;/h4&gt;

&lt;p&gt;I made a deliberate decision early: Build on where the ecosystem is going, not where it is.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 16 (Canary) + React 19&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Tailwind CSS v4&lt;/li&gt;
&lt;li&gt;Zustand&lt;/li&gt;
&lt;li&gt;Supabase&lt;/li&gt;
&lt;li&gt;@hello-pangea/dnd&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Next.js 16 (Canary) + React 19:&lt;/strong&gt; Both pre-stable when I started. I made this call deliberately. My reasoning: we were building something we intended to use long-term, and building on the current stable release meant I’d be doing a framework migration in six months regardless. React 19’s improvements to concurrent rendering and transitions were directly relevant to the drag-and-drop experience I had in mind. I accepted the tradeoff — occasional canary instability, fewer Stack Overflow answers — in exchange for building on where the ecosystem was actually heading.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TypeScript throughout:&lt;/strong&gt; Not a stylistic preference. The core Task interface flows through the UI layer, the Zustand store, the Supabase integration, and the drag-and-drop logic. Without TypeScript keeping the shape of that data honest across all four, you’re just hoping nothing breaks at runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tailwind CSS v4&lt;/strong&gt; — also pre-stable. The removal of the tailwind.config.js requirement and improved CSS variable integration in v4 were what pushed me toward it. I use Tailwind for layout utilities, spacing, and responsive breakpoints only. All color and typography tokens live in theme.css as CSS custom properties. That architecture decision mattered more than the Tailwind version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zustand&lt;/strong&gt; for state. Redux felt excessive for the scope. Context didn’t scale cleanly. Zustand hit the right balance. Zustand was minimal and powerful. Zustand worked because of its simple API, centralized state, easy async handling and built-in persistence support. It became the backbone for state, persistence, and optimistic updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supabase&lt;/strong&gt; for backend, auth, and real-time. PostgreSQL, row-level security, Google OAuth, and WebSocket subscriptions in one managed service. I’m not in the business of running database infrastructure. Supabase let me stay focused on the product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@hello-pangea/dnd&lt;/strong&gt; for drag-and-drop. The community-maintained fork of &lt;strong&gt;react-beautiful-dnd&lt;/strong&gt;, updated for React 18+ compatibility. react-beautiful-dnd is no longer actively maintained by Atlassian — using the fork was the safer long-term call.&lt;/p&gt;




&lt;h4&gt;
  
  
  The Data Architecture — Getting Flat Right
&lt;/h4&gt;

&lt;p&gt;Early in the build I had to make a foundational decision about the data shape. The obvious approach for a Kanban-style board is a map keyed by column name:&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;// The tempting approach&lt;/span&gt;
&lt;span class="nx"&gt;mainData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="nx"&gt;Schedule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="nx"&gt;Delegate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="nx"&gt;Limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="nx"&gt;Later&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes rendering easy. It makes drag-and-drop operations locally easy. It makes syncing to a relational database a nightmare. Every operation that touches the database requires you to reconstruct which array a task lives in, and your data shape in the client diverges from your data shape in the DB.&lt;/p&gt;

&lt;p&gt;I went with a flat array instead, with each task carrying its own boardName (and later board_id). The rendering layer groups by quadrant. Every database operation is a straightforward row operation.&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;// Flat array - what I actually used&lt;/span&gt;
&lt;span class="nx"&gt;mainData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="c1"&gt;// each task has boardName: 'Do' | 'Schedule' | etc.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This decision paid off when I connected to Supabase. No transformation layer needed. Fetch from DB, set in store, render.&lt;/p&gt;




&lt;h4&gt;
  
  
  The Drag-and-Drop Reorder Problem
&lt;/h4&gt;

&lt;p&gt;This is where most Kanban implementations quietly take shortcuts.&lt;/p&gt;

&lt;p&gt;The naive approach is to sort by createdAt timestamp and treat position as implicit. This works until someone drags a card, at which point your timestamp is semantically wrong. You need an explicit position integer column.&lt;/p&gt;

&lt;p&gt;My drag handler:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleOnDragEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&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;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;destination&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sourceItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mainData&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boardName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;droppableId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&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;destItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mainData&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boardName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;droppableId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&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;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;position&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;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;droppableId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;droppableId&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;reordered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;sourceItems&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;removed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reordered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&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="nx"&gt;reordered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&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="nx"&gt;removed&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;updated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reordered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="nf"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;sourceItems&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;removed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newDest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;destItems&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="nx"&gt;newDest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;splice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&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="nx"&gt;removed&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;updatedSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newSource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&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;updatedDest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newDest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;boardName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;droppableId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&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;untouched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mainData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boardName&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;droppableId&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
      &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;boardName&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;droppableId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;updateData&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;untouched&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;updatedSource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;updatedDest&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;On the database side, I needed atomic bulk updates for position changes. A single UPDATE per task would mean N round trips per drag operation. I wrote a Supabase RPC function instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; 
&lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="k"&gt;replace&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;reorder_tasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="n"&gt;jsonb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="k"&gt;setof&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt; &lt;span class="n"&gt;perform&lt;/span&gt; &lt;span class="n"&gt;pg_advisory_xact_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;123456&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; 
&lt;span class="k"&gt;with&lt;/span&gt; 
  &lt;span class="k"&gt;rows&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; 
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'position'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="k"&gt;nullif&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'board_id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;board_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;elem&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'updated_at'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt; 
    &lt;span class="k"&gt;from&lt;/span&gt; 
      &lt;span class="n"&gt;jsonb_array_elements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;elem&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;update&lt;/span&gt; 
  &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; 
&lt;span class="k"&gt;set&lt;/span&gt; 
  &lt;span class="k"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;board_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;coalesce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;board_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;board_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
  &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt; 
&lt;span class="k"&gt;from&lt;/span&gt; 
  &lt;span class="k"&gt;rows&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; 
&lt;span class="k"&gt;where&lt;/span&gt; 
  &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; 
&lt;span class="n"&gt;returning&lt;/span&gt; 
  &lt;span class="n"&gt;t&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="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;language&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pg_advisory_xact_lock is important. Without serializing reorder operations at the transaction level, two concurrent drags from two devices produce a race condition where both writes succeed but the resulting order is wrong. The advisory lock is session-scoped and cheap — it doesn’t block reads, only concurrent writes to the same logical lock ID.&lt;/p&gt;




&lt;h4&gt;
  
  
  The Local-First Architecture
&lt;/h4&gt;

&lt;p&gt;This is the part I’m most satisfied with, and also the part that took the most iteration to get right.&lt;/p&gt;

&lt;p&gt;Planow is designed as a local-first system — tasks are stored locally and work instantly. Cloud sync is optional and enabled only when users choose to log in.&lt;/p&gt;

&lt;p&gt;If a user signs in, their locally-created tasks sync to the cloud. If they’re already a cloud user, we fetch from the DB on login. Conflict resolution happens automatically.&lt;/p&gt;

&lt;p&gt;The Zustand pattern I landed on:&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;addTask&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&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;previousData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;mainData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// Optimistic update - UI responds immediately&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tempTask&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="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;completed&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="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userData&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remote&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;local&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mainData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mainData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;tempTask&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;userData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="nx"&gt;supabase&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tasks&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;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;single&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;error&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Rollback on failure&lt;/span&gt;
      &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;mainData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;previousData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;snackbar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;show&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&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="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// Replace temp record with real DB record (correct ID)&lt;/span&gt;
    &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;mainData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mainData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;tempTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// If no userData: stays in local state with source: 'local'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The source: ‘local’ marker is how the sync layer knows what to push on login. We only sync tasks carrying that marker — not everything. This prevents unnecessary writes and handles the common case of a user editing tasks while logged out and then logging back in.&lt;/p&gt;

&lt;p&gt;The sync RPC handles conflict resolution via updated_at comparison:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; 
&lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="k"&gt;replace&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;sync_tasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="n"&gt;jsonb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;returns&lt;/span&gt; &lt;span class="k"&gt;setof&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;begin&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;board_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;select&lt;/span&gt; 
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'board_id'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'completed'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt; &lt;span class="nb"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'position'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'updated_at'&lt;/span&gt;&lt;span class="p"&gt;)::&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt; 
&lt;span class="k"&gt;from&lt;/span&gt; 
  &lt;span class="n"&gt;jsonb_array_elements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;conflict&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; 
&lt;span class="k"&gt;update&lt;/span&gt; 
&lt;span class="k"&gt;set&lt;/span&gt; 
  &lt;span class="n"&gt;board_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;board_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;completed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="k"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt; 
&lt;span class="k"&gt;where&lt;/span&gt; 
  &lt;span class="n"&gt;excluded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;updated_at&lt;/span&gt; 
&lt;span class="n"&gt;returning&lt;/span&gt; 
  &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;language&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Last-write-wins by timestamp. Simple, predictable, and correct for a single-user task manager.&lt;/p&gt;




&lt;h4&gt;
  
  
  The Privacy Philosophy — Your Data Stays Yours
&lt;/h4&gt;

&lt;p&gt;One principle guided many of our decisions:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your data belongs to you — not the platform.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Planow is built to work without requiring an account. Tasks are stored locally by default, meaning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No forced sign-ups&lt;/li&gt;
&lt;li&gt;No automatic cloud sync&lt;/li&gt;
&lt;li&gt;No hidden data collection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a user chooses to log in, only then is data synced to the cloud for cross-device access.&lt;/p&gt;

&lt;p&gt;This flips the typical model:&lt;br&gt;
Most apps start with the cloud and store data locally as a cache.&lt;br&gt;
Planow starts local — and uses the cloud only when needed.&lt;/p&gt;


&lt;h4&gt;
  
  
  The Design System — Syed’s Domain, My Implementation
&lt;/h4&gt;

&lt;p&gt;I should be honest here: the design system architecture was a collaborative decision, but Syed drove the visual language. What I implemented was a full CSS custom property token system where every single color in the application is a variable reference. Not one hardcoded hex value in any component.&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="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"light"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="py"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#5A67FA&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#F7F7FF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="py"&gt;quadrant-do-bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;srgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#CDFDDD&lt;/span&gt; &lt;span class="m"&gt;40%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="py"&gt;quadrant-do-header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0D834C&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="py"&gt;quadrant-do-border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#0D834C&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="py"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#5A67FA&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#06060F&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="err"&gt;-&lt;/span&gt; &lt;span class="py"&gt;quadrant-do-bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;color-mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;srgb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#0D834C&lt;/span&gt; &lt;span class="m"&gt;20%&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;transparent&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 practical consequence: dark mode isn’t a pile of dark: Tailwind overrides. It’s a complete, independently tuned set of token values. Switching themes means flipping the data-theme attribute on . Everything responds. We built out a full opacity ladder for each quadrant color (4%, 8%, 10%, 16%, 32%, 48%, 64%, 80%, 92%) for state layers. Over 400 variables total across both themes. It sounds like overkill for a side project, and maybe it is — but we never had a “this doesn’t look right in dark mode” bug.&lt;/p&gt;




&lt;h4&gt;
  
  
  What I Got Right
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Flat data model → simplified everything&lt;/li&gt;
&lt;li&gt;RPC-based bulk updates → performance win&lt;/li&gt;
&lt;li&gt;Optimistic UI → better UX&lt;/li&gt;
&lt;li&gt;CSS token system → scalable theming&lt;/li&gt;
&lt;li&gt;Zustand → minimal and effective&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  What Was Harder Than Expected
&lt;/h4&gt;

&lt;p&gt;Drag reorder logic&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same column&lt;/li&gt;
&lt;li&gt;Cross column&lt;/li&gt;
&lt;li&gt;Position recalculation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Database sync consistency&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order remains correct&lt;/li&gt;
&lt;li&gt;No race conditions&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  What Planow Is Today
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A fast, visual task manager&lt;/li&gt;
&lt;li&gt;Built around the Eisenhower Matrix&lt;/li&gt;
&lt;li&gt;Real-time synced (when logged in)&lt;/li&gt;
&lt;li&gt;Optimistic and responsive&lt;/li&gt;
&lt;li&gt;Clean, minimal UI&lt;/li&gt;
&lt;li&gt;Dark/light theme supported&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  What’s Next
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Collaboration features (data model ready)&lt;/li&gt;
&lt;li&gt;Reports/analytics UI&lt;/li&gt;
&lt;li&gt;Improved sync strategies&lt;/li&gt;
&lt;li&gt;Better onboarding &amp;amp; landing experience&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Privacy-focused sync alternatives:
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Import / Export tasks between devices&lt;/li&gt;
&lt;li&gt;Manual data transfer without requiring login&lt;/li&gt;
&lt;li&gt;Full user control over where data lives&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is simple:&lt;br&gt;
Users should be able to move their data across devices without being forced into an account system.&lt;/p&gt;




&lt;h4&gt;
  
  
  The Real Lesson
&lt;/h4&gt;

&lt;p&gt;None of this was “hard” individually.&lt;/p&gt;

&lt;p&gt;But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every small decision compounded&lt;/li&gt;
&lt;li&gt;Every edge case mattered&lt;/li&gt;
&lt;li&gt;Every shortcut showed up later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest wins came from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;aligning product decisions with technical architecture early&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h4&gt;
  
  
  The Honest Part
&lt;/h4&gt;

&lt;p&gt;Four years from “we should build this” to shipped MVP. The idea was simple. The engineering was not hard exactly — but it was full of small decisions that compounded, and getting each one right took longer than the individual decision seemed to warrant.&lt;/p&gt;

&lt;p&gt;The local-first sync architecture, the drag-and-drop position management, the conflict resolution — each of these problems is well-understood in the abstract. Actually implementing them in a way that handles real edge cases (concurrent drags, multi-device sync, auth state changes mid-session) requires you to think through every failure mode and decide how to handle it.&lt;/p&gt;

&lt;p&gt;Syed handled product and design. I handled engineering. The best decisions came from the gap between those two functions — the conversations about what the right tradeoff was between implementation complexity and correct behavior.&lt;/p&gt;

&lt;p&gt;That’s worth saying plainly:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the most important engineering decisions on this project weren’t purely technical. They were answers to product questions that happened to have technical consequences.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Not a developer? Want to understand what Planow is actually for and why the Eisenhower Matrix might change the way you work? Read the story here: &lt;a href="https://medium.com/@syed.shahab/i-stopped-drowning-in-to-do-lists-the-day-i-asked-myself-two-questions-1877c921503d" rel="noopener noreferrer"&gt;I Stopped Drowning in To-Do Lists the Day I Asked Myself Two Questions&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Written by &lt;a href="https://www.yogeshraj.com/" rel="noopener noreferrer"&gt;Yogesh Raj&lt;/a&gt; · Built with &lt;a href="https://www.syedshahab.in/" rel="noopener noreferrer"&gt;Syed Shahab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try 👉 &lt;a href="https://planow.app/" rel="noopener noreferrer"&gt;https://planow.app/&lt;/a&gt; and let me know your honest feedback&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>todoapp</category>
      <category>nextjs</category>
      <category>react</category>
    </item>
    <item>
      <title>From Beginner to Pro: The Ultimate Guide to map(), filter(), reduce(), and forEach()</title>
      <dc:creator>Yogesh Raj Kabilan</dc:creator>
      <pubDate>Sat, 29 Mar 2025 18:15:48 +0000</pubDate>
      <link>https://dev.to/yogeshraj/from-beginner-to-pro-the-ultimate-guide-to-map-filter-reduce-and-foreach-3f0j</link>
      <guid>https://dev.to/yogeshraj/from-beginner-to-pro-the-ultimate-guide-to-map-filter-reduce-and-foreach-3f0j</guid>
      <description>&lt;p&gt;&lt;code&gt;map()&lt;/code&gt;, &lt;code&gt;filter()&lt;/code&gt;, &lt;code&gt;reduce()&lt;/code&gt; and &lt;code&gt;forEach()&lt;/code&gt; are the most commonly used Array methods in JavaScript. Let’s dive deeper and learn about these methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;map()&lt;/code&gt; Method: Transforming Arrays
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;map()&lt;/code&gt; is a higher-order function used to iterate over each element in the array and return a new array of the same length, with each element transformed by the provided callback.&lt;/p&gt;

&lt;p&gt;Syntax:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcc64oeksuwdpx58lqteb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcc64oeksuwdpx58lqteb.png" alt="JS Map() syntax" width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;callback&lt;/code&gt; is the function to be executed on each element of the array.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;currentValue&lt;/code&gt; - The current element being processed in the array.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;index&lt;/code&gt; - index of the current element. (optional)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;array&lt;/code&gt; - The original array on which &lt;code&gt;map()&lt;/code&gt; was called. The index of the current element being processed in the array. (optional)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;thisArg&lt;/code&gt; is an optional parameter, which allows you to specify the value of &lt;code&gt;this&lt;/code&gt; inside the callback function.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Let see a simple example how &lt;code&gt;map()&lt;/code&gt; will work with each params&lt;/p&gt;

&lt;p&gt;Basic Example: Multiply each element by 2&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpbk31cyfqng7j42sxjp4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpbk31cyfqng7j42sxjp4.png" alt="how  raw `map()` endraw  will work with each params" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;index&lt;/code&gt;: The index of the current element in the array&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubos5cxms2nlgtoc37oh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubos5cxms2nlgtoc37oh.png" alt="how  raw `map()` endraw  will work with each params" width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;array&lt;/code&gt;: The original array on which map() is called&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxu77mvebdgdsbpqrbbf1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxu77mvebdgdsbpqrbbf1.png" alt="how  raw `map()` endraw  will work with each params" width="800" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;thisArg&lt;/code&gt;: A value to use as this when executing the callback function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyh6nxqftypmd334scrc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyh6nxqftypmd334scrc.png" alt="how  raw `map()` endraw  will work with each params" width="800" height="313"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  2. &lt;code&gt;filter()&lt;/code&gt; Method: Selecting Elements Based on a Condition
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;filter()&lt;/code&gt; method allows you to create a new array containing only those elements that pass a certain test. Unlike &lt;code&gt;map()&lt;/code&gt;, which transforms each element, &lt;code&gt;filter()&lt;/code&gt; filters out elements that don't meet the criteria.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;callback&lt;/code&gt;- The function to test each element.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;currentValue&lt;/code&gt; - The current element being processed in the array.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;index&lt;/code&gt; - index of the current element. (optional)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;array&lt;/code&gt; - The original array on which &lt;code&gt;map()&lt;/code&gt; was called. The index of the current element being processed in the array. (optional)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;thisArg&lt;/code&gt; is an optional parameter, which allows you to specify the value of &lt;code&gt;this&lt;/code&gt; inside the callback function.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Basic Example: Filtering Even Numbers&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplr3nmalj15spysc3dqd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fplr3nmalj15spysc3dqd.png" alt="how  raw `filter()` endraw  will work with each params" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the &lt;code&gt;index&lt;/code&gt; and &lt;code&gt;array&lt;/code&gt; Parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjj28cdz08dzya6o01rzj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjj28cdz08dzya6o01rzj.png" alt="how  raw `filter()` endraw  will work with each params" width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;thisArg&lt;/code&gt; for Contextual Filtering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd10gnj8e1jsjwk03gq3f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd10gnj8e1jsjwk03gq3f.png" alt="how  raw `filter()` endraw  will work with each params" width="800" height="398"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  3. &lt;code&gt;reduce()&lt;/code&gt; Method: Accumulating Values
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;reduce()&lt;/code&gt; method allows you to accumulate a single result from all elements of an array by applying a function that you provide. It's typically used for summing values, but you can also use it for other purposes like flattening an array, combining objects, etc.&lt;/p&gt;

&lt;p&gt;Syntax:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqr2n0p955c55kork70rt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqr2n0p955c55kork70rt.png" alt=" raw `reduce()` endraw  Method: Accumulating Values" width="800" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;callback(accumulator, currentValue, index, array)&lt;/code&gt;: The callback function is executed on each element of the array. It takes four parameters:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;accumulator&lt;/code&gt;:  The value that keeps track of the result as we go through each item in the array. It gets updated during each iteration based on the operation you want to perform.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;currentValue&lt;/code&gt;: The current element being processed in the array.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;index&lt;/code&gt;: The index of the current element (optional).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;array&lt;/code&gt;: The original array that reduce() was called on (optional).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;initialValue&lt;/code&gt;: The initial value to start accumulating (optional). If omitted, the first element of the array is used, and the iteration starts from the second element.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Basic Example: Summing Numbers&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewmsvddzqkwqor5cl2ys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewmsvddzqkwqor5cl2ys.png" alt="how  raw `reduce()` endraw  will work with each params" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Basic Example: Flattening an Array&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ly7sa2srshdy3zqlb4k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5ly7sa2srshdy3zqlb4k.png" alt="how  raw `reduce()` endraw  will work with each params" width="800" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the &lt;code&gt;index&lt;/code&gt; and &lt;code&gt;array&lt;/code&gt; Parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdnpabdba1qqzmn6lycy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdnpabdba1qqzmn6lycy.png" alt="how  raw `reduce()` endraw  will work with each params" width="800" height="217"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  4. &lt;code&gt;forEach()&lt;/code&gt; Method: Iterating Over Arrays
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;forEach()&lt;/code&gt; method is used to execute a provided function on every element in the array. It’s the most basic way to loop through arrays in JavaScript but differs from &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;filter()&lt;/code&gt; in that it doesn’t return a new array. Instead, it simply performs the operation for each item without accumulating or transforming the array.&lt;/p&gt;

&lt;p&gt;Syntax:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1hxnyjvj28amogeh3ri.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1hxnyjvj28amogeh3ri.png" alt=" raw `forEach()` endraw  Method: Iterating Over Arrays" width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;callback&lt;/code&gt; - The function to execute on each element in the array.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;currentValue&lt;/code&gt; - The current element being processed in the array.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;index&lt;/code&gt; - The index of the current element being processed in the array.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;array&lt;/code&gt; - The original array on which &lt;code&gt;forEach()&lt;/code&gt; was called.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;thisArg&lt;/code&gt; - An optional argument that specifies the value of this inside the callback function.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Basic example&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwzhmt3vibtegn22yyh8a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwzhmt3vibtegn22yyh8a.png" alt="how  raw `foreach()` endraw  will work with each params" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the &lt;code&gt;index&lt;/code&gt; and &lt;code&gt;array&lt;/code&gt; Parameters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc0cpib0yqyzsl859hnj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnc0cpib0yqyzsl859hnj.png" alt="how  raw `foreach()` endraw  will work with each params" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;thisArg&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8tsw8zyo2pzxdnrz1no3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8tsw8zyo2pzxdnrz1no3.png" alt="how  raw `foreach()` endraw  will work with each params" width="800" height="694"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Bonus Tip&lt;/strong&gt;: Using Normal Functions and Calling External Functions&lt;/p&gt;

&lt;p&gt;Although we've used arrow functions extensively, you can also use normal functions and call external functions inside methods like &lt;code&gt;map()&lt;/code&gt;, &lt;code&gt;filter()&lt;/code&gt;, &lt;code&gt;reduce()&lt;/code&gt;, and &lt;code&gt;forEach()&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Using a Normal Function Inside &lt;code&gt;map()&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jtbb45yp33regvv8h30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3jtbb45yp33regvv8h30.png" alt="Using a Normal Function Inside  raw `map()` endraw " width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Calling an External Function Inside &lt;code&gt;map()&lt;/code&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqv550d8daf9ag98nejrz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqv550d8daf9ag98nejrz.png" alt="Calling a Normal Function Inside  raw `map()` endraw " width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These techniques are not limited to map(); they can be applied similarly to &lt;code&gt;filter()&lt;/code&gt;, &lt;code&gt;reduce()&lt;/code&gt;, and &lt;code&gt;forEach()&lt;/code&gt;. Using normal functions and external functions can make your code more modular and easier to read, especially when dealing with complex operations or reusable logic.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Conclusion:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this article, we've explored four of the most commonly used array methods in JavaScript: &lt;code&gt;map()&lt;/code&gt;, &lt;code&gt;filter()&lt;/code&gt;, &lt;code&gt;reduce()&lt;/code&gt;, and &lt;code&gt;forEach()&lt;/code&gt;. These methods allow for clean, functional-style iterations over arrays, making your code more readable and expressive.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;map()&lt;/code&gt; allows you to transform each element in an array and return a new array with the same length but modified values.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;filter()&lt;/code&gt; helps you select elements from an array based on a condition, creating a new array of only the matching elements.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reduce()&lt;/code&gt; gives you the ability to accumulate values across the array, making it perfect for summing, averaging, or combining data in more complex ways.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;forEach()&lt;/code&gt; simply iterates over an array and executes a function for each element, with optional access to the index and the entire array.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By understanding and effectively using these methods, you'll be able to handle array manipulations more efficiently and write more declarative and concise code. While these methods are powerful individually, they can also be combined to achieve more sophisticated operations on arrays.&lt;/p&gt;

&lt;p&gt;So next time you're faced with an array operation, consider using one of these methods. With practice, you'll find that your JavaScript code becomes cleaner, more readable, and easier to maintain.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is my first blog, and I appreciate your understanding. Feel free to share any feedback or suggestions—I’m always eager to learn and improve!&lt;/em&gt;&lt;/p&gt;

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