<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>Igor Tosic</title>
		<link>https://www.igortosic.com</link>
		<description>Igor Tosic builds interactive canvas, annotation and map interfaces over very large datasets, and writes about frontend architecture and state management.</description>
		<language>en</language>
		<atom:link href="https://www.igortosic.com/rss.xml" rel="self" type="application/rss+xml" />
		<image>
			<url>https://www.igortosic.com/images/og-default.png</url>
			<title>Igor Tosic</title>
			<link>https://www.igortosic.com</link>
		</image>
		<lastBuildDate>Wed, 25 Feb 2026 00:00:00 GMT</lastBuildDate>
		<item>
			<title>OOP as State Management: What Svelte 5 Runes Made Obvious</title>
			<link>https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious</link>
			<guid isPermaLink="true">https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious</guid>
			<pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate>
			<dc:creator>Igor Tosic</dc:creator>
			<description>How Svelte 5 runes turn plain TypeScript classes into reactive stores, replacing Redux ceremony with object-oriented simplicity.</description>
			<category>svelte</category>
			<category>state-management</category>
			<category>typescript</category>
			<category>oop</category>
			<content:encoded><![CDATA[<h2 id="when-the-scaffold-disappears">When the Scaffold Disappears<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#when-the-scaffold-disappears">#</a></h2>
<p>There's a moment in woodworking — or so I've been told — when a craftsman stops thinking about the chisel and starts thinking only about the wood. The tool becomes invisible. It's no longer an intermediary between intention and creation; it's simply gone.</p>
<p>I've been chasing that feeling in software for years.</p>
<p>With React and Redux, I never quite got there. The tool was always present. Every feature began with the same liturgy: define the action type, write the action creator, build the reducer case, wire up the selector, connect the component. It was structured. It was predictable. And it was <em>loud</em>. The architecture was always announcing itself, always demanding attention before you could think about the actual problem you were solving.</p>
<p>Then I built an <a href="https://github.com/igortosic-easymind/CRM-demo-SvelteKit5-Classbased-Store">CRM demo</a> system with SvelteKit 5, and something shifted. Not a dramatic revelation — more like realizing you've been holding your breath, and you finally exhale.</p>
<p>I want to show you what that exhale looks like — by tracing one feature, end to end. A client list page. Filtering, pagination, search, contacts, modals. The full thing. From the server to the store to the component and back.</p>
<h2 id="the-store-a-class-not-a-configuration">The Store: A Class, Not a Configuration<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#the-store-a-class-not-a-configuration">#</a></h2>
<p>In my <a href="https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5">previous articles</a>, I explored the migration from React/Redux to SvelteKit and the philosophical implications of Svelte 5 runes. But I want to go deeper now. Not into what changed, but <em>why it matters</em> — following the client list flow through every layer.</p>
<p>Let's start where most state management stories start: the store itself.</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// src/lib/store/clients.svelte.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  Client,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  LeadStatus,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  PaginationState,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  ContactResponse,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">} </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '$lib/types'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> class</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ClientStore</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // State as fields</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  clients</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Client</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[]>([])</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  pagination</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">PaginationState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    currentPage: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    totalPages: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    totalItems: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">0</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    itemsPerPage: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">10</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  })</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  filters</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;{</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    search</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    lead</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }>({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    search: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">''</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    lead: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  })</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  loading</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  error</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">string</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Contact sub-domain</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  selectedClientContacts</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ContactResponse</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[]>([])</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  contactModalOpen</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  contactModalMode</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'create'</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'edit'</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'view'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'create'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  selectedContact</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ContactResponse</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Methods as actions</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  setClientsData</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">data</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    clients</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Client</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[]</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    pagination</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PaginationState</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    filters</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#E36209;--shiki-dark:#FFAB70">search</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">; </span><span style="color:#E36209;--shiki-dark:#FFAB70">lead</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  })</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.clients </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> data.clients</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.pagination </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> data.pagination</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (data.filters) {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">      this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.filters </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        ...</span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.filters,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        search: data.filters.search </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ''</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        lead: data.filters.lead </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  updateFilters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">newFilters</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    search</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    lead</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  })</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.filters </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.filters, </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">newFilters }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">isLoading</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.loading </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> isLoading</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Contact modal orchestration</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  viewContact</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">contact</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ContactResponse</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.selectedContact </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> contact</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.contactModalMode </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'view'</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.contactModalOpen </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> true</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  createContact</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.selectedContact </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.contactModalMode </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'create'</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.contactModalOpen </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> true</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  closeContactModal</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.contactModalOpen </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> false</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.selectedContact </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Nested updates</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  addContactToClient</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">clientId</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">contact</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ContactResponse</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.clients </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.clients.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">map</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">client</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      client.id </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> clientId</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        ?</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">            ...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">client,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">            contacts: [</span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">client.contacts, contact],</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">            contact_count: client.contacts.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">length</span><span style="color:#D73A49;--shiki-dark:#F97583"> +</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">          }</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        :</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> client,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    )</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.selectedClientContacts </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.selectedClientContacts, contact]</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clientStore</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ClientStore</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span></code></pre></figure>
<p>Stop and read this carefully. This is not Redux. This is not Vuex. This is <strong>object-oriented programming applied to state management</strong>.</p>
<p>The store is a class. It has fields (state) and methods (actions). When you call <code>clientStore.setClientsData(...)</code>, you're calling a method on an instance. The state properties use the <code>$state</code> rune, which means Svelte automatically tracks their changes and updates any component that reads them.</p>
<p>There are no action creators. No reducers. No switch statements. No middleware. No <code>connect()</code>. No <code>useSelector</code>. No <code>dispatch</code>. The mental model is: <em>an instance of a class that holds data and knows how to modify itself</em>.</p>
<p>The <code>$state</code> rune is not a function call — it's a compiler instruction. Svelte sees it and generates exactly the reactive plumbing your code needs. At runtime, this is just a JavaScript class. Your IDE understands it. TypeScript validates it. A junior developer reads it and knows what it does.</p>
<p>This is simpler than Redux. But more importantly, it's <em>honest</em> — it doesn't pretend to be functional programming if you're writing imperative code anyway.</p>
<h2 id="the-river-and-the-pipes-how-data-reaches-the-client-list">The River and the Pipes: How Data Reaches the Client List<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#the-river-and-the-pipes-how-data-reaches-the-client-list">#</a></h2>
<p>Now that we have the store, how does data reach it? This is where SvelteKit distinguishes itself not just from React, but from the entire mental model of client-side state management.</p>
<p>In Redux-world, data typically flows like this: the component mounts, dispatches an action, the action triggers an API call (through thunks or sagas or whatever middleware is fashionable this quarter), the response hits the reducer, the state updates, and the component re-renders through a selector.</p>
<p>That's a lot of pipes. Carefully assembled, meticulously maintained pipes.</p>
<p>SvelteKit proposes something different: <em>What if the data just... arrived?</em></p>
<h3 id="act-i-the-server-speaks-first-pageserverts">Act I: The Server Speaks First (+page.server.ts)<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#act-i-the-server-speaks-first-pageserverts">#</a></h3>
<p>Every page in SvelteKit can have a <code>+page.server.ts</code> file. This runs exclusively on the server — it has access to cookies, databases, internal APIs. For our client list, it fetches clients from the backend with whatever filters the URL carries:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// src/routes/clients/+page.server.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { listClients } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '$lib/server/clients'</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { PageServerLoad } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> './$types'</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { LeadStatus } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '$lib/types'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> load</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PageServerLoad</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> async</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="color:#E36209;--shiki-dark:#FFAB70">cookies</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">url</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> page</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Number</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">get</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'page'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '1'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> search</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">get</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'search'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ''</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> lead</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">get</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'lead'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">as</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clientsResponse</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> listClients</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(cookies, {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    page,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    itemsPerPage: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">10</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    search,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    lead: lead </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> undefined</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  })</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    clients: clientsResponse.data,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    pagination: clientsResponse.pagination,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    filters: { search, lead },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>No client-side fetch. No loading spinner on first render. No flash of empty content. The data is there when the HTML arrives. The server did its job, quietly, before the browser ever opened its mouth to ask.</p>
<p>This is fundamentally different from Redux or any client-side state library. In those patterns, the client constructs API calls, handles loading states, manages error states. Here, <strong>the server is the authority</strong>. The server decides what data the client needs. The server handles authentication through cookies — no tokens in JavaScript, no auth headers in client code.</p>
<h3 id="act-ii-the-bridge-pagets">Act II: The Bridge (+page.ts)<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#act-ii-the-bridge-pagets">#</a></h3>
<p>But here's the question: we have our <code>ClientStore</code> class on the client. How does server data reach it?</p>
<p>This is where <code>+page.ts</code> enters — the universal load function that runs on <em>both</em> server and client. It receives the data from <code>+page.server.ts</code> and acts as the bridge:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// src/routes/clients/+page.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { PageLoad } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> './$types'</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { clientStore } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '$lib/store/clients.svelte'</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { browser } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '$app/environment'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> load</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PageLoad</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> async</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="color:#E36209;--shiki-dark:#FFAB70">data</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">depends</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  depends</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'app:clients'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (browser) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    clientStore.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setClientsData</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      clients: data.clients,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      pagination: data.pagination,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      filters: data.filters,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    })</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    clientStore.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> data</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>Study this for a moment. The <code>data</code> parameter contains everything <code>+page.server.ts</code> returned. The <code>depends("app:clients")</code> call registers a dependency key — when we later call <code>invalidate("app:clients")</code>, SvelteKit knows to re-run this entire chain.</p>
<p>The <code>if (browser)</code> guard is the key insight: during SSR, this code runs on the server where we don't need to hydrate a client-side store. But when the page loads in the browser — or when the user navigates client-side — we pour the server data into our <code>ClientStore</code> instance. One method call: <code>clientStore.setClientsData(...)</code>. The class does the rest.</p>
<p>This is the pattern that Redux could never offer, because Redux wasn't designed with a full-stack framework in mind. Here: <strong>server loads the data, the bridge hydrates the store, components read from the store.</strong></p>
<p>No prop drilling through seventeen layers. No context providers wrapping your tree. No <code>useEffect</code> that fires after the first render to "sync" state. The store is ready when the component mounts.</p>
<h3 id="act-iii-the-component-breathes-pagesvelte--client-listsvelte">Act III: The Component Breathes (+page.svelte → client-list.svelte)<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#act-iii-the-component-breathes-pagesvelte--client-listsvelte">#</a></h3>
<p>And now the page component itself:</p>
<figure class="code-block" data-lang="svelte"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- src/routes/clients/+page.svelte --></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ClientList </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/components/clients/client-list.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">ClientList</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> /></span></span></code></pre></figure>
<p>That's the entire file. No props. No data destructuring. No store subscriptions. The component delegates to <code>ClientList</code>, which reads directly from the store:</p>
<figure class="code-block" data-lang="svelte"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- src/lib/components/clients/client-list.svelte --></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> SearchBar </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "./search-bar.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> Filters </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "./filters.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ClientTable </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "./client-table.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> RefreshButton </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "./refresh-button.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { Button } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/components/ui/button/index.js"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> Plus </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "@lucide/svelte/icons/plus"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { goto } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$app/navigation"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { clientStore } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/store/clients.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clientsList</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(clientStore.clients);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> paginationData</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(clientStore.pagination);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> currentFilters</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(clientStore.filters);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clientsFound</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF">    `${</span><span style="color:#24292E;--shiki-dark:#E1E4E8">paginationData</span><span style="color:#032F62;--shiki-dark:#9ECBFF">.</span><span style="color:#24292E;--shiki-dark:#E1E4E8">totalItems</span><span style="color:#032F62;--shiki-dark:#9ECBFF">} ${</span><span style="color:#24292E;--shiki-dark:#E1E4E8">paginationData</span><span style="color:#032F62;--shiki-dark:#9ECBFF">.</span><span style="color:#24292E;--shiki-dark:#E1E4E8">totalItems</span><span style="color:#D73A49;--shiki-dark:#F97583"> ===</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="color:#D73A49;--shiki-dark:#F97583"> ?</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "client"</span><span style="color:#D73A49;--shiki-dark:#F97583"> :</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "clients"} found`</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  );</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> handlePageChange</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">pageNum</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> number</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (pageNum </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> paginationData.currentPage) </span><span style="color:#D73A49;--shiki-dark:#F97583">return</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> url</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> URL</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(window.location.href);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">set</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"page"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#6F42C1;--shiki-dark:#B392F0">String</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(pageNum));</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    clientStore.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    goto</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(url.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toString</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(), { replaceState: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">div</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"space-y-4"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">div</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"flex items-center justify-between"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">h1</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"text-2xl font-bold"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>Clients&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">h1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">div</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"flex items-center gap-2"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">RefreshButton</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">Button</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> href</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"/clients/new"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">Plus</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"mr-2 h-4 w-4"</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        Add Client</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="color:#005CC5;--shiki-dark:#79B8FF">Button</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">div</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">div</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">div</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">SearchBar</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">Filters</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> activeFilter</span><span style="color:#24292E;--shiki-dark:#E1E4E8">={currentFilters.lead </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "all"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">} /></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">div</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">div</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> class</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"text-muted-foreground text-sm"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>{clientsFound}&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">div</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">ClientTable</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> clients</span><span style="color:#24292E;--shiki-dark:#E1E4E8">={clientsList} /></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">div</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre></figure>
<p>Notice what's happening. <code>$derived</code> creates reactive references to the store. When <code>clientStore.clients</code> changes, <code>clientsList</code> updates. When <code>clientStore.pagination</code> changes, the pagination buttons re-render. The compiler generates code that does this tracking for you.</p>
<p>No selectors. No memoization. No <code>useCallback</code> worries. No virtual DOM diffing. No <code>React.memo</code>. No <code>shouldComponentUpdate</code>. Just: "This value depends on that value. Update it when that value changes."</p>
<p>And <code>SearchBar</code>, <code>Filters</code>, <code>ClientTable</code> — each of these child components imports <code>clientStore</code> independently. They don't receive it as a prop. They don't need a <code>Provider</code> ancestor. They just reach for the store and use it. The class instance <em>is</em> the shared state.</p>
<h2 id="the-feedback-loop-filtering-in-action">The Feedback Loop: Filtering in Action<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#the-feedback-loop-filtering-in-action">#</a></h2>
<p>Here's where you see the full cycle come alive. The Filters component:</p>
<figure class="code-block" data-lang="svelte"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- src/lib/components/clients/filters.svelte --></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { clientStore } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/store/clients.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { Button } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/components/ui/button/index.js"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { goto } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$app/navigation"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { LeadStatus } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/types"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#005CC5;--shiki-dark:#79B8FF">activeFilter</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "all"</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">props</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;{ </span><span style="color:#E36209;--shiki-dark:#FFAB70">activeFilter</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "all"</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }>();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> isLoading</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(clientStore.loading);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> handleFilterChange</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">filter</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "all"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (filter </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "all"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      clientStore.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">updateFilters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ lead: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="color:#D73A49;--shiki-dark:#F97583">else</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      clientStore.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">updateFilters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ lead: filter });</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    clientStore.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> url</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> URL</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(window.location.href);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (filter </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "all"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"lead"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="color:#D73A49;--shiki-dark:#F97583">else</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">set</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"lead"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, filter);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"page"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    goto</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(url.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toString</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(), { replaceState: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre></figure>
<p>And the SearchBar:</p>
<figure class="code-block" data-lang="svelte"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- src/lib/components/clients/search-bar.svelte --></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { Input } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/components/ui/input/index.js"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { Button } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/components/ui/button/index.js"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> Search </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "@lucide/svelte/icons/search"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> RefreshCcw </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "@lucide/svelte/icons/refresh-ccw"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { clientStore } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/store/clients.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { goto } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$app/navigation"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Track search query in local state, initialize from store</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> searchQuery </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(clientStore.filters.search </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ""</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> searchTimeout</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ReturnType</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#D73A49;--shiki-dark:#F97583">typeof</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> setTimeout>;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Handle search as user types with debounce</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> handleSearchInput</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Update the store directly for immediate UI feedback</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    clientStore.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">updateFilters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ search: searchQuery });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Clear existing timeout</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    clearTimeout</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(searchTimeout);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Debounce the actual navigation</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    searchTimeout </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> setTimeout</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      performSearch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">500</span><span style="color:#24292E;--shiki-dark:#E1E4E8">); </span><span style="color:#6A737D;--shiki-dark:#6A737D">// 500ms debounce</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> performSearch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    clientStore.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Build URL with search term</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> url</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> URL</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(window.location.href);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (searchQuery) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">set</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"search"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, searchQuery);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="color:#D73A49;--shiki-dark:#F97583">else</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"search"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"page"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">); </span><span style="color:#6A737D;--shiki-dark:#6A737D">// Reset to first page when searching</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Use goto to trigger server-side navigation</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    goto</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(url.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toString</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(), { replaceState: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre></figure>
<p>Watch the cycle:</p>
<ol>
<li><strong>User clicks "Hot" filter</strong> → <code>clientStore.updateFilters({ lead: "hot" })</code> — instant UI feedback</li>
<li><strong><code>goto()</code> changes the URL</strong> → SvelteKit sees URL change</li>
<li><strong><code>+page.server.ts</code> re-runs</strong> → server fetches only hot leads from backend API</li>
<li><strong><code>+page.ts</code> receives new data</strong> → <code>clientStore.setClientsData(...)</code> hydrates the store</li>
<li><strong>Components re-render</strong> → <code>$derived(clientStore.clients)</code> picks up the new filtered list</li>
</ol>
<p>The same cycle for search. The same cycle for pagination. The URL is the source of truth for what the server fetches; the store is the source of truth for what the UI shows. They're not competing — they're collaborating.</p>
<p>In React, this pattern would require TanStack Query or SWR alongside Redux, creating two parallel state systems fighting for authority. Or you'd wire together <code>useEffect</code>, <code>useState</code>, <code>useCallback</code>, router listeners, and cache invalidation logic across half a dozen files. In SvelteKit, the framework <em>is</em> the data layer.</p>
<h2 id="the-redux-mental-model-honestly">The Redux Mental Model, Honestly<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#the-redux-mental-model-honestly">#</a></h2>
<p>Let's be concrete about what this replaces. Imagine the same filtering feature in Redux:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Redux: actions.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> SET_CLIENTS_DATA</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'SET_CLIENTS_DATA'</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> UPDATE_FILTERS</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'UPDATE_FILTERS'</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> SET_LOADING</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'SET_LOADING'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Redux: reducer.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> clientsReducer</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> initialState, </span><span style="color:#E36209;--shiki-dark:#FFAB70">action</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  switch</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (action.type) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    case</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'SET_CLIENTS_DATA'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        ...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        clients: action.payload.clients,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        pagination: action.payload.pagination,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      }</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    case</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'UPDATE_FILTERS'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        ...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        filters: { </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state.filters, </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">action.payload },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      }</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    case</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'SET_LOADING'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state, loading: action.payload }</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    default</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Redux: component.tsx</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clients</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useSelector</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state.clients.clients)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> filters</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useSelector</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state.clients.filters)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> dispatch</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useDispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> handleFilter</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">lead</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ type: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'UPDATE_FILTERS'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, payload: { lead } })</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ type: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'SET_LOADING'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, payload: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // ...then navigate, and hope the useEffect catches the new data</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>Now the same thing in Svelte 5:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Svelte 5: in the component</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clients</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(clientStore.clients)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> filters</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(clientStore.filters)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> handleFilter</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">lead</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  clientStore.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">updateFilters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ lead })</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  clientStore.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  goto</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(url, { replaceState: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> })</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<table>
<thead>
<tr>
<th>Aspect</th>
<th>Redux</th>
<th>Svelte 5 Class Store</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>State definition</strong></td>
<td>Initial state object + reducer</td>
<td>Class fields with <code>$state</code></td>
</tr>
<tr>
<td><strong>Updates</strong></td>
<td>Dispatch actions → reducer switch</td>
<td>Call methods directly</td>
</tr>
<tr>
<td><strong>Computed values</strong></td>
<td><code>createSelector</code> + <code>useSelector</code></td>
<td><code>get</code> accessors or <code>$derived</code></td>
</tr>
<tr>
<td><strong>Async operations</strong></td>
<td>Thunk/Saga middleware</td>
<td>Direct async methods</td>
</tr>
<tr>
<td><strong>Boilerplate</strong></td>
<td>Actions + reducers + selectors + Provider + connect</td>
<td>Class + export instance</td>
</tr>
<tr>
<td><strong>TypeScript</strong></td>
<td>Good (with toolkit)</td>
<td>Excellent (class members are typed)</td>
</tr>
<tr>
<td><strong>Bundle size impact</strong></td>
<td>~2KB+ (Redux + React-Redux)</td>
<td>0KB (compiler feature)</td>
</tr>
<tr>
<td><strong>Component access</strong></td>
<td><code>useSelector</code> + <code>useDispatch</code></td>
<td>Import and use</td>
</tr>
<tr>
<td><strong>Multiple stores</strong></td>
<td>One global store (convention)</td>
<td>Multiple class instances (natural)</td>
</tr>
<tr>
<td><strong>Learning curve</strong></td>
<td>Actions, reducers, selectors, middleware, Provider</td>
<td>Classes + three runes</td>
</tr>
</tbody>
</table>
<p>The Svelte version isn't a little simpler. It's a different order of magnitude of simplicity. And not because it's doing less — the same features are there. Filtering. Loading states. Pagination. Shared state across sibling components. It's simpler because the framework eliminated the ceremony.</p>
<h2 id="computed-properties-when-getters-are-enough">Computed Properties: When Getters Are Enough<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#computed-properties-when-getters-are-enough">#</a></h2>
<p>The same pattern extends beyond the client list. Consider the <code>UserStore</code>, which demonstrates how class getters become reactive computed properties:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// src/lib/store/user.svelte.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> class</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> UserStore</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  user</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">User</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  get</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> isAuthenticated</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user </span><span style="color:#D73A49;--shiki-dark:#F97583">!==</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  get</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> permissions</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> UserPermissions</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#D73A49;--shiki-dark:#F97583">!</span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user) </span><span style="color:#D73A49;--shiki-dark:#F97583">return</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      canCreateClients: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user.can_manage_clients,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      canDeleteClients: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user.can_delete_clients,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      isAdmin: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user.is_superuser </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user.is_staff,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  get</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> displayName</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#D73A49;--shiki-dark:#F97583">!</span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user) </span><span style="color:#D73A49;--shiki-dark:#F97583">return</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user.first_name </span><span style="color:#D73A49;--shiki-dark:#F97583">&#x26;&#x26;</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user.last_name</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      ?</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> `${</span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#032F62;--shiki-dark:#9ECBFF">.</span><span style="color:#24292E;--shiki-dark:#E1E4E8">user</span><span style="color:#032F62;--shiki-dark:#9ECBFF">.</span><span style="color:#24292E;--shiki-dark:#E1E4E8">first_name</span><span style="color:#032F62;--shiki-dark:#9ECBFF">} ${</span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#032F62;--shiki-dark:#9ECBFF">.</span><span style="color:#24292E;--shiki-dark:#E1E4E8">user</span><span style="color:#032F62;--shiki-dark:#9ECBFF">.</span><span style="color:#24292E;--shiki-dark:#E1E4E8">last_name</span><span style="color:#032F62;--shiki-dark:#9ECBFF">}`</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      :</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user.username</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> userStore</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> UserStore</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span></code></pre></figure>
<p>Standard JavaScript getters. But because they reference <code>$state</code> properties, Svelte automatically tracks them as derived values. They recompute when their dependencies change. No <code>useMemo</code>. No <code>createSelector</code>. No dependency arrays to get wrong.</p>
<p>In a component: <code>userStore.displayName</code>. One expression. Type-safe. Automatically reactive when <code>userStore.user</code> changes.</p>
<p>In Redux: <code>useSelector(selectDisplayName)</code> — where <code>selectDisplayName</code> is a separate function, in a separate file, that you hope stays in sync with the state shape.</p>
<h2 id="when-not-to-use-a-store-the-wisdom-of-constraint">When Not to Use a Store: The Wisdom of Constraint<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#when-not-to-use-a-store-the-wisdom-of-constraint">#</a></h2>
<p>Here's something often unmentioned in state management articles: not everything should be in a store.</p>
<p>In this demo system, the dashboard takes a different approach:</p>
<figure class="code-block" data-lang="svelte"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- src/routes/dashboard/+page.svelte --></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> StatsCard </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/components/dashboard/stats-card.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> RecentClients </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/components/dashboard/recent-clients.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { PageData } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "./$types"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { data } </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">props</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;{ </span><span style="color:#E36209;--shiki-dark:#FFAB70">data</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PageData</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }>();</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">StatsCard</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> title</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Total Clients"</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> value</span><span style="color:#24292E;--shiki-dark:#E1E4E8">={data.statistics.totalClients} </span><span style="color:#6F42C1;--shiki-dark:#B392F0">description</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Demo"</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">RecentClients</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> clients</span><span style="color:#24292E;--shiki-dark:#E1E4E8">={data.recentClients} /></span></span></code></pre></figure>
<p>No store. <code>data</code> props directly from the server. And that's intentional. And wise.</p>
<p>The dashboard is a read-only summary view. It aggregates statistics, shows recent items, displays charts. There's no filtering. No pagination. No modals. No user interaction that modifies this data. The data flows in one direction — from server to template — and stays there.</p>
<p><strong>Stores should be used for:</strong></p>
<ul>
<li>Shared state across multiple components/routes</li>
<li>State that persists across navigation</li>
<li>Complex interactions (filtering, pagination, modals, CRUD)</li>
</ul>
<p><strong>Props should be used for:</strong></p>
<ul>
<li>Data that flows parent → child</li>
<li>Read-only displays, dashboards, summaries</li>
<li>Data that doesn't need to be shared with siblings</li>
</ul>
<p><strong>Component-local <code>$state</code> should be used for:</strong></p>
<ul>
<li>Form inputs, toggle states, temporary values</li>
<li>UI state not needed by other components</li>
</ul>
<p>A well-designed application demonstrates this wisdom. The clients page uses a store because <code>SearchBar</code>, <code>Filters</code>, <code>ClientTable</code>, and <code>RefreshButton</code> all need shared access. The dashboard passes props because data flows in one direction. <strong>The framework doesn't impose a single pattern. It asks: <em>does this state need to be shared?</em></strong></p>
<p>This is the philosophical shift Redux missed. Redux assumes all state must be managed. Svelte 5 asks: <em>does it need to be?</em></p>
<h2 id="no-library-thats-the-point">No Library. That's the Point.<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#no-library-thats-the-point">#</a></h2>
<p>Let me state something that should be obvious but somehow isn't: <strong>we use no state management library in this entire application.</strong></p>
<p>No Redux. No Zustand. No Jotai. No Recoil. No MobX. No Pinia. No NgRx.</p>
<p>The entire store layer — user authentication with permission getters, client management with nested contacts and modal orchestration, task tracking with filtered pagination, calendar events with drag-to-create — is built with nothing but Svelte 5 runes and plain TypeScript classes.</p>
<p>In the React ecosystem, choosing a state management library is itself a project. Evaluating options, learning APIs, configuring middleware, debugging integration issues. Teams spend days on this decision. Blog posts rank them. Conference talks compare them.</p>
<p>Svelte 5 sidesteps the question entirely. <code>$state()</code> makes a property reactive. <code>$derived()</code> computes from reactive properties. <code>$effect()</code> runs side effects. Classes organize your domain. Export an instance. Import it where you need it.</p>
<p>That's the whole API. There's nothing else to learn.</p>
<h2 id="what-this-means-for-the-craft">What This Means for the Craft<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-as-state-management-what-svelte-5-runes-made-obvious#what-this-means-for-the-craft">#</a></h2>
<p>I've been thinking about why this matters beyond the technical arguments. Beyond bundle sizes and benchmark numbers and developer experience surveys.</p>
<p>When I write Redux, I spend mental energy on action naming conventions, reducer organization, selector memoization, middleware configuration, DevTools setup, type definitions that mirror structure three times over. When I write Svelte 5 stores, I think about <em>my domain</em>. Clients. Contacts. Filters. Pagination. That's it.</p>
<p>For a developer building a complex application, this compounds. Every feature is 30-40% faster to implement. Every bug is easier to trace — no action logs to decode. Every refactor is simpler — no reducer restructuring. Over months, this adds up to weeks of saved time.</p>
<p>But it's more than productivity. It's about the relationship between the developer and the code. <strong>Software should be shaped by the problem, not by the framework's expectations.</strong> When I design the <code>ClientStore</code>, I'm thinking about clients and contacts and how a sales team filters their pipeline. I'm not thinking about which middleware handles the async flow. I'm not wondering if my selector is properly memoized.</p>
<p>The framework becomes invisible. And when the framework is invisible, all that's left is the thing you're actually making.</p>
<p>The chisel disappears. You see only the wood.</p>]]></content:encoded>
		</item>
		<item>
			<title>The Death of Frameworks and Why I Stopped Worrying</title>
			<link>https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying</link>
			<guid isPermaLink="true">https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying</guid>
			<pubDate>Thu, 04 Dec 2025 00:00:00 GMT</pubDate>
			<dc:creator>Igor Tosic</dc:creator>
			<description>Why &quot;what if Svelte dies?&quot; is the wrong question: your domain logic outlives any framework, and Svelte 5 runes remove the state management ceremony.</description>
			<category>svelte</category>
			<category>react</category>
			<category>state-management</category>
			<category>architecture</category>
			<content:encoded><![CDATA[<h2 id="how-svelte-5-runes-made-me-realize-the-wrong-question">How Svelte 5 Runes Made Me Realize the Wrong Question<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#how-svelte-5-runes-made-me-realize-the-wrong-question">#</a></h2>
<p>When I first started considering Svelte for my enterprise canvas application, I kept asking myself the wrong question: "What if Svelte dies?"</p>
<p>I spent days researching market share. Stack Overflow answer counts. I compared npm downloads like they were stock charts. React has a million developers. Svelte has fifty thousand. The math was obvious, right?</p>
<p>Then something clicked.</p>
<h3 id="the-question-nobody-asks">The Question Nobody Asks<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#the-question-nobody-asks">#</a></h3>
<p>Here's what I realized: Nobody asks "What if React dies?" But they should.</p>
<p>Meta can change direction. They've done it before — remember the class components to hooks migration? Remember when everyone had to relearn everything?
The React ecosystem churns constantly: Redux, then Context, then Zustand, then Jotai, then Recoil, then... what's next?</p>
<p>No framework is truly "safe." The difference is that React developers have normalized the chaos. We call it "ecosystem maturity."</p>
<p>But here's the uncomfortable truth I discovered while building my drawing application: the framework is maybe 10% of your codebase.
The other 90%? That's your domain logic. Your canvas operations. Your PDF rendering. Your business rules. Usually, it's like that in a big enterprise application.</p>
<p>And all of that is framework-agnostic.</p>
<h2 id="what-actually-matters">What Actually Matters<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#what-actually-matters">#</a></h2>
<p>I've been working with React and Redux for years. Enterprise applications. Complex state. The whole ceremony.</p>
<p>You know the ceremony I'm talking about:</p>
<figure class="code-block" data-lang="jsx"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// The Redux dance: actions, reducers, selectors, dispatch</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// actions.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> ADD_ANNOTATION</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'ADD_ANNOTATION'</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> UNDO</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'UNDO'</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> REDO</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'REDO'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> addAnnotation</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">annotation</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  type: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">ADD_ANNOTATION</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  payload: annotation,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// reducer.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> initialState</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  past: [],</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  present: [],</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  future: [],</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> historyReducer</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> initialState, </span><span style="color:#E36209;--shiki-dark:#FFAB70">action</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  switch</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (action.type) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    case</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> ADD_ANNOTATION</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        past: [</span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state.past, state.present],</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        present: [</span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state.present, action.payload],</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        future: [],</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      }</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    case</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> UNDO</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (state.past.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">length</span><span style="color:#D73A49;--shiki-dark:#F97583"> ===</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // ... another 20 lines</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // ... REDO case, another 15 lines</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Component.tsx</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { useDispatch, useSelector } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'react-redux'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> AnnotationPanel</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> dispatch</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useDispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> annotations</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useSelector</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state.history.present)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> canUndo</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useSelector</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state.history.past.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">length</span><span style="color:#D73A49;--shiki-dark:#F97583"> ></span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> handleAdd</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      addAnnotation</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">        /* ... */</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      }),</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    )</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> &#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">button</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> onClick</span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8">{handleAdd}>Add&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">button</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>That's roughly 80 lines of code. To add an item to a list. With undo capability.
We've normalized this. We call it "predictable state management." We've convinced ourselves the boilerplate is a feature, not a bug.</p>
<h2 id="enter-svelte-5-when-less-becomes-more">Enter Svelte 5: When Less Becomes More<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#enter-svelte-5-when-less-becomes-more">#</a></h2>
<p>Now look at Svelte 5 with class-based stores using runes:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// stores/projects.svelte.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { Project } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "../types/projects"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { getProjects } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "../services/projects"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> class</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ProjectsStore</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    projects</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Project</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[]>([]);</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    selectedProject</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Project</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    loading</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">boolean</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    error</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">string</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    get</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> hasProjects</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        return</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.projects.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">length</span><span style="color:#D73A49;--shiki-dark:#F97583"> ></span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    get</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> selectedProjectId</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        return</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.selectedProject?.id </span><span style="color:#D73A49;--shiki-dark:#F97583">??</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setProjects</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">projects</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Project</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[])</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.projects </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> projects;</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.error </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    addProject</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">project</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Project</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.projects </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.projects, project];</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    async</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> fetchProjects</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Promise</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">void</span><span style="color:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.loading </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.error </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        try</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">            const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> projects</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> getProjects</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">            this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setProjects</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(projects);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="color:#D73A49;--shiki-dark:#F97583">catch</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (err) {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">            this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.error </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> err </span><span style="color:#D73A49;--shiki-dark:#F97583">instanceof</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Error</span><span style="color:#D73A49;--shiki-dark:#F97583"> ?</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> err.message </span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Failed to fetch projects"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        } </span><span style="color:#D73A49;--shiki-dark:#F97583">finally</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">            this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.loading </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> projectsStore</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ProjectsStore</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span></code></pre></figure>
<p>That's it. No actions. No reducers. No dispatch. No selectors. No <code>connect()</code>. No <code>useSelector</code>. No middleware.</p>
<p>Just a class. With properties. That are reactive.</p>
<p>In the component:</p>
<figure class="code-block" data-lang="svelte"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { projectsStore } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "../stores/projects.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">button</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> onclick</span><span style="color:#24292E;--shiki-dark:#E1E4E8">={() </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> projectsStore.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">addProject</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(newProject)}></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  Add Project</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">button</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">{#</span><span style="color:#D73A49;--shiki-dark:#F97583">if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> projectsStore.loading}</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">p</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>Loading...&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">p</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">{:</span><span style="color:#D73A49;--shiki-dark:#F97583">else</span><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">ul</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    {#</span><span style="color:#D73A49;--shiki-dark:#F97583">each</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> projectsStore.projects </span><span style="color:#D73A49;--shiki-dark:#F97583">as</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> project}</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">li</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>{project.name}&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">li</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    {/</span><span style="color:#D73A49;--shiki-dark:#F97583">each</span><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">ul</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">{/</span><span style="color:#D73A49;--shiki-dark:#F97583">if</span><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>No ceremony. No dance. You just... use it.</p>
<h2 id="the-philosophy-behind-runes">The Philosophy Behind Runes<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#the-philosophy-behind-runes">#</a></h2>
<p>Svelte 5's runes (<code>$state</code>, <code>$derived</code>, <code>$effect</code>) represent a philosophical shift. They're not just syntax sugar — they're a statement about what reactivity should feel like.</p>
<p>In React, reactivity is opt-in through hooks, wrapped in rules you must memorize. Don't call hooks conditionally. Always include dependencies in useEffect. Memoize everything or watch your app crawl.</p>
<p>In Svelte 5, you say what you mean:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> count </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">0</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#6A737D;--shiki-dark:#6A737D">// This is state</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> doubled </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(count </span><span style="color:#D73A49;--shiki-dark:#F97583">*</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 2</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#6A737D;--shiki-dark:#6A737D">// This derives from state</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">$effect</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(() </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'Count changed:'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, count)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre></figure>
<p>The <code>$</code> prefix is a rune — a compiler instruction. Not a runtime hook. Not a function that runs on every render. A compile-time transformation that generates exactly the reactive code you need.</p>
<p>The class-based approach takes this further. Instead of scattered stores and derived values, you encapsulate your domain:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> class</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> AuthStore</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    user</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">User</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    loading</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">boolean</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    error</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> $state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">string</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    get</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> isAuthenticated</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        return</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user </span><span style="color:#D73A49;--shiki-dark:#F97583">!==</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    clearAuth</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> void</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.user </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.error </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">        this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.loading </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> authStore</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> AuthStore</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span></code></pre></figure>
<p>This feels like writing JavaScript. Not React-flavored JavaScript. Not Redux-flavored JavaScript. Just... <a href="https://www.igortosic.com/blog/javascript-oop">JavaScript</a> with superpowers.</p>
<h2 id="the-data-flow-difference">The Data Flow Difference<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#the-data-flow-difference">#</a></h2>
<p>Let me show you what data flow looks like in practice.</p>
<h3 id="reactredux-mental-model">React/Redux mental model:<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#reactredux-mental-model">#</a></h3>
<figure class="code-block" data-lang="text"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span>JavaScriptComponent → dispatch(action) → middleware → reducer → new state → selector → component re-render</span></span></code></pre></figure>
<p>Every step is explicit. Every step is traceable. Every step is boilerplate.</p>
<h3 id="svelte-5-mental-model">Svelte 5 mental model:<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#svelte-5-mental-model">#</a></h3>
<figure class="code-block" data-lang="text"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span>Component → store.property = value → component re-renders</span></span></code></pre></figure>
<p>That's not an oversimplification. That's the actual model.</p>
<p>When you write <code>projectsStore.projects = newProjects</code>, Svelte knows. The compiler saw your <code>$state</code> declaration.
It generated the reactive bindings. Your components using <code>projectsStore.projects</code> will update.</p>
<p>No action to dispatch. No reducer to write. No selector to optimize. No <code>React.memo</code> to consider.</p>
<h2 id="the-performance-argument-and-why-it-doesnt-matter">The Performance Argument (And Why It Doesn't Matter)<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#the-performance-argument-and-why-it-doesnt-matter">#</a></h2>
<p>I'll be honest: when I looked at raw performance numbers, the difference was... not what I expected.</p>
<p>For most operations in my canvas application, the differences were barely noticeable. Tool switching? Imperceptible in both frameworks.
Drawing strokes? Konva is the bottleneck, not the framework — both hit 60fps without breaking a sweat.</p>
<p>The real performance bottlenecks? PDF rendering with pdf.js. Canvas operations with Konva. Network latency. None of these care about your framework choice.</p>
<p>So why switch?</p>
<h2 id="developer-experience-the-real-win">Developer Experience: The Real Win<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#developer-experience-the-real-win">#</a></h2>
<p>Here's the uncomfortable truth about framework choices: <strong>performance gains are marginal, but developer experience compounds.</strong>
When I write Redux, I spend mental energy on:</p>
<ul>
<li>Action naming conventions</li>
<li>Reducer organization</li>
<li>Selector memoization</li>
<li>Middleware configuration</li>
<li>DevTools setup</li>
<li>Type definitions that mirror structure three times over</li>
</ul>
<p>When I write Svelte 5 stores, I think about:</p>
<ul>
<li>My domain</li>
</ul>
<p>That's it. I think about projects and annotations and tools. Not about the ceremony of making them reactive.</p>
<p>For a solo developer building a complex application, this compounds. Every feature is 30-40% faster to implement. Every bug is easier to trace (no action logs to decode).
Every refactor is simpler (no reducer restructuring).</p>
<p>Over months, this adds up to weeks of saved time.</p>
<h2 id="the-hiring-question">The Hiring Question<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#the-hiring-question">#</a></h2>
<p>"But what about hiring? React developers are everywhere!"</p>
<p>Let me share what I've seen in enterprise applications: finding a developer who truly understands React deeply, knows Redux architecture properly,
can work with Canvas APIs, understands PDF manipulation — or any complex domain logic, whether that's financial systems,
healthcare workflows, real-time collaboration, or other specialized business domains — AND writes clean maintainable code — is already hard.</p>
<p>A good developer will pick up Svelte 5 in few weeks. The runes syntax is simpler than hooks. The class-based stores are just... classes. There's less to learn, not more.</p>
<p>When I eventually hire, I'll look for:</p>
<ul>
<li>Strong JavaScript fundamentals</li>
<li>Experience in the relevant domain</li>
<li>Learning ability</li>
</ul>
<p>Framework-specific experience? Nice to have. But I'd rather hire someone who can learn than someone who only knows one tool.</p>
<h2 id="what-i-actually-built">What I Actually Built<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#what-i-actually-built">#</a></h2>
<p>I didn't switch to Svelte for performance metrics. I switched because I wanted to write less code that does more.</p>
<p>My canvas application now has:</p>
<ul>
<li>Class-based stores for each domain (projects, annotations, canvas, auth)</li>
<li>Derived properties that just work (no useMemo, no selectors)</li>
<li>Async operations that feel natural (no thunks, no sagas)</li>
<li>TypeScript that helps instead of fighting me</li>
</ul>
<p>The migration could take about two to three weeks for a medium-sized application. Most of that time goes to UI components, not state management.
The state management is actually faster to implement in Svelte than it is to maintain in Redux.</p>
<h2 id="the-philosophical-conclusion">The Philosophical Conclusion<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-death-of-frameworks-and-why-I-stopped-worrying#the-philosophical-conclusion">#</a></h2>
<p>Every few years, the web development world declares a new winner. jQuery. Backbone. Angular. React. And someday, something else.</p>
<p>The developers who thrive aren't the ones who bet on the winning horse. They're the ones who understand that the horse doesn't matter as much as knowing how to ride.</p>
<p>Your domain logic — the actual business value you create — is framework-agnostic. Your understanding of state management patterns transcends any specific implementation.
Your ability to learn new tools is more valuable than mastery of current ones.</p>
<p>I chose Svelte 5 not because I think it will "win."
I chose it because it lets me focus on what I'm actually building — the product, the features, the problems I'm solving for users.</p>
<p>🎨 The framework is just the canvas. What you paint on it is what matters. 🖌️</p>]]></content:encoded>
		</item>
		<item>
			<title>The Svelte Shift: From React/Redux to SvelteKit5</title>
			<link>https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5</link>
			<guid isPermaLink="true">https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5</guid>
			<pubDate>Sat, 31 May 2025 00:00:00 GMT</pubDate>
			<dc:creator>Igor Tosic</dc:creator>
			<description>Migrating a CRM from Next.js and Redux to SvelteKit 5: file-based layouts, load functions as built-in state, idiomatic stores and no more provider hell.</description>
			<category>svelte</category>
			<category>react</category>
			<category>state-management</category>
			<content:encoded><![CDATA[<p>When we are talking about reactivity on frameworks in the frontend world, I was, or I am still, a big fan of SolidJS reactivity.
It is simpler and better than React. But now I am testing SvelteKit5, which I hadn't done until recently.
I've read about Svelte but never tried it... However, Svelte5 attracted me because of runes and the possibility of better state management than Redux,
along with a more efficient framework. Svelte is a compiler that is much faster than Next.js and other frameworks.
So I decided to migrate my NextJS project to SvelteKit5 to compare pros and cons and how fast we can migrate our frontend.</p>
<p>SolidJS is a good framework, but it is still not recognized in the market, and the community is not big.
However, Svelte is ready to take the next step in the frontend world, and big companies and systems can start thinking about implementing it as part of their tech stack!</p>
<h3 id="a-fundamental-shift-in-architecture">A Fundamental Shift in Architecture<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5#a-fundamental-shift-in-architecture">#</a></h3>
<p>One of the first things I noticed when migrating to <a href="https://github.com/igortosic-easymind/CRM-SvelteKit5">SvelteKit - CRM demo project</a> was the fundamentally different approach to layouts and routing.
In NextJS/React, we're used to explicitly importing layout components and wrapping our pages manually.
With SvelteKit, I discovered that layouts automatically wrap pages in their directories through a file-based system.</p>
<figure class="code-block" data-lang="svelte"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- src/routes/+layout.svelte --></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '../app.css'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ProtectedLayout </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/components/layouts/protected-layout.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { LayoutData } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> './$types'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { data, children } </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">props</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;{</span><span style="color:#E36209;--shiki-dark:#FFAB70">data</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LayoutData</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">children</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> any</span><span style="color:#24292E;--shiki-dark:#E1E4E8">}>();</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">{#</span><span style="color:#D73A49;--shiki-dark:#F97583">if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> data.user}</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">ProtectedLayout</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> user</span><span style="color:#24292E;--shiki-dark:#E1E4E8">={data.user}></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    {@</span><span style="color:#D73A49;--shiki-dark:#F97583">render</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> children</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()}</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="color:#005CC5;--shiki-dark:#79B8FF">ProtectedLayout</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">{:</span><span style="color:#D73A49;--shiki-dark:#F97583">else</span><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  {@</span><span style="color:#D73A49;--shiki-dark:#F97583">render</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> children</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()}</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">{/</span><span style="color:#D73A49;--shiki-dark:#F97583">if</span><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>This simple layout file automatically wraps every page in the application, conditionally applying a protected layout for authenticated users.
No manual imports, no explicit wrapping - just a clean, declarative approach to page structure.</p>
<h2 id="the-data-flow-revolution-built-in-state-management">The Data Flow Revolution: Built-in State Management<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5#the-data-flow-revolution-built-in-state-management">#</a></h2>
<p>Perhaps the most mind-blowing aspect of SvelteKit for me, coming from React/Redux, was discovering how data flows through the application.
In Redux, we spend hours setting up stores, actions, reducers, and connectors.
With SvelteKit, I found myself with what felt like a hidden "global state provider" built right into the framework.</p>
<h3 id="the-sveltekit-data-loading-chain">The SvelteKit Data Loading Chain<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5#the-sveltekit-data-loading-chain">#</a></h3>
<p>When you create a <code>+layout.server.ts</code> file in SvelteKit, it automatically acts as a data provider for:</p>
<ul>
<li>That layout file (<code>+layout.svelte</code>)</li>
<li>Any page within that directory (<code>+page.svelte</code>)</li>
<li>Any child layouts/pages in subdirectories</li>
</ul>
<p>This means data can cascade through your entire application without any explicit prop drilling or context providers. The framework just handles it.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// /routes/+layout.server.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> load</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    user: { id: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, name: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Alice"</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    settings: { theme: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"dark"</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// /routes/dashboard/+layout.server.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> load</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="color:#E36209;--shiki-dark:#FFAB70">parent</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Get parent data first! This connects to the upstream "provider"</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> parentData</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> parent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Add new dashboard-specific data</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    tasks: [</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Task 1"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Task 2"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">],</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // You can also transform parent data if needed</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    userWithRole: {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      ...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">parentData.user,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      role: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"admin"</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre></figure>
<p>Then in ANY component within the hierarchy, you can just access the data:</p>
<figure class="code-block" data-lang="svelte"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // In any +page.svelte or +layout.svelte</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { data } </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">props</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;{</span><span style="color:#E36209;--shiki-dark:#FFAB70">data</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PageData</span><span style="color:#24292E;--shiki-dark:#E1E4E8">}>();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // data now contains:</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  //   user: { id: 1, name: "Alice" },</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  //   settings: { theme: "dark" },</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  //   tasks: ["Task 1", "Task 2"],</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  //   userWithRole: { id: 1, name: "Alice", role: "admin" }</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">h1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>Welcome, {data.user.name}!&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">h1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">p</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>You have {data.tasks.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">length</span><span style="color:#24292E;--shiki-dark:#E1E4E8">} tasks&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">p</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre></figure>
<h2 id="the-redux-mental-model-vs-the-svelte-approach">The Redux Mental Model vs. The Svelte Approach<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5#the-redux-mental-model-vs-the-svelte-approach">#</a></h2>
<p>Coming from Redux, I initially approached Svelte stores with the same mental model. Here's how I first implemented my client store, following a very Redux-like pattern:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Redux-like approach in Svelte</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { writable, derived, </span><span style="color:#D73A49;--shiki-dark:#F97583">type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> Writable } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'svelte/store'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { Client, LeadStatus, PaginationState } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '$lib/types'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Define the state structure</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">interface</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ClientsState</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  clients</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Client</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  pagination</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PaginationState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  filters</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    search</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    lead</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  loading</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  error</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Initial state</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> initialState</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ClientsState</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  clients: [],</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  pagination: {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    currentPage: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    totalPages: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    totalItems: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">0</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    itemsPerPage: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">10</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  filters: {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    search: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">''</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    lead: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  loading: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  error: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">};</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Create the writable store</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createClientsStore</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#005CC5;--shiki-dark:#79B8FF">subscribe</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">set</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">update</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Writable</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ClientsState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">> </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> writable</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(initialState);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    subscribe,</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Set entire state - useful when hydrating from SSR</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setData</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="color:#E36209;--shiki-dark:#FFAB70">data</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">      clients</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Client</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">      pagination</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PaginationState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">      filters</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#E36209;--shiki-dark:#FFAB70">search</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">; </span><span style="color:#E36209;--shiki-dark:#FFAB70">lead</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      update</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#D73A49;--shiki-dark:#F97583"> =></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        ...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        clients: data.clients,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        pagination: data.pagination,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        filters: {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">          search: data.filters?.search </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ''</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">          lead: data.filters?.lead </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        loading: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        error: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      }));</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Set loading state</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="color:#E36209;--shiki-dark:#FFAB70">isLoading</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      update</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#D73A49;--shiki-dark:#F97583"> =></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state, loading: isLoading }));</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Set error state</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setError</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="color:#E36209;--shiki-dark:#FFAB70">error</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      update</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#D73A49;--shiki-dark:#F97583"> =></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state, error, loading: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }));</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Update filters</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    updateFilters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="color:#E36209;--shiki-dark:#FFAB70">filters</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#E36209;--shiki-dark:#FFAB70">search</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">; </span><span style="color:#E36209;--shiki-dark:#FFAB70">lead</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      update</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#D73A49;--shiki-dark:#F97583"> =></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        ...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        filters: { </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state.filters, </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">filters }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      }));</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Reset to initial state</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    reset</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: () </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#6F42C1;--shiki-dark:#B392F0"> set</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(initialState)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Create the store instance</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clientsStore</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createClientsStore</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Derived stores for convenience</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> filteredClients</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(clientsStore, </span><span style="color:#E36209;--shiki-dark:#FFAB70">$store</span><span style="color:#D73A49;--shiki-dark:#F97583"> =></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> filtered </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">$store.clients];</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Apply lead filter if set</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ($store.filters.lead) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    filtered </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> filtered.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">client</span><span style="color:#D73A49;--shiki-dark:#F97583"> =></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> client.lead </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $store.filters.lead);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Apply search filter if set</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ($store.filters.search) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> searchLower</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $store.filters.search.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toLowerCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    filtered </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> filtered.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">client</span><span style="color:#D73A49;--shiki-dark:#F97583"> =></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      client.company_name?.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toLowerCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(searchLower) </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      client.first_name.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toLowerCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(searchLower) </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      client.last_name.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toLowerCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(searchLower) </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      client.email.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toLowerCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(searchLower)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    );</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> filtered;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Loading state</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> isLoading</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(clientsStore, </span><span style="color:#E36209;--shiki-dark:#FFAB70">$store</span><span style="color:#D73A49;--shiki-dark:#F97583"> =></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $store.loading);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Error state</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> error</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(clientsStore, </span><span style="color:#E36209;--shiki-dark:#FFAB70">$store</span><span style="color:#D73A49;--shiki-dark:#F97583"> =></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $store.error);</span></span></code></pre></figure>
<p>This approach works, but it's heavy with Redux concepts, including:</p>
<ul>
<li>A single large state object</li>
<li>Immutable update patterns</li>
<li>Action creator functions</li>
<li>A lot of boilerplate</li>
</ul>
<p>As I became more comfortable with Svelte, I realized there's a much simpler, more idiomatic Svelte approach:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Svelte-native approach</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { writable, derived } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'svelte/store'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { Client, LeadStatus, PaginationState } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> '$lib/types'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Separate stores for different concerns</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clients</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> writable</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Client</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[]>([]);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> pagination</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> writable</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">PaginationState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  currentPage: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  totalPages: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  totalItems: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">0</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  itemsPerPage: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">10</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> filters</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> writable</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;{</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  search</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  lead</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}>({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  search: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">""</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  lead: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Loading and error states</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> loading</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> writable</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> error</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> writable</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">string</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Derived store for filtered clients</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> filteredClients</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">([clients, filters], ([</span><span style="color:#E36209;--shiki-dark:#FFAB70">$clients</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">$filters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">]) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // If no filters are applied, return all clients</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#D73A49;--shiki-dark:#F97583">!</span><span style="color:#24292E;--shiki-dark:#E1E4E8">$filters.search </span><span style="color:#D73A49;--shiki-dark:#F97583">&#x26;&#x26;</span><span style="color:#D73A49;--shiki-dark:#F97583"> !</span><span style="color:#24292E;--shiki-dark:#E1E4E8">$filters.lead) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $clients;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Apply filters</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $clients.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">filter</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">client</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Filter by lead status</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ($filters.lead </span><span style="color:#D73A49;--shiki-dark:#F97583">&#x26;&#x26;</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> client.lead </span><span style="color:#D73A49;--shiki-dark:#F97583">!==</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $filters.lead) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Filter by search term</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ($filters.search) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> term</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $filters.search.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toLowerCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        client.company_name?.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toLowerCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(term) </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        client.first_name.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toLowerCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(term) </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        client.last_name.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toLowerCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(term) </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        client.email.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toLowerCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">includes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(term)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      );</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  });</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Simple action to set clients data</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> setClientsData</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">data</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  clients</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Client</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  pagination</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PaginationState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  filters</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#E36209;--shiki-dark:#FFAB70">search</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">; </span><span style="color:#E36209;--shiki-dark:#FFAB70">lead</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  clients.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">set</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(data.clients);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  pagination.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">set</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(data.pagination);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (data.filters) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    filters.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">update</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">currentFilters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      ...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">currentFilters,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      search: data.filters?.search </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ""</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      lead: data.filters?.lead </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }));</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Action to update filters</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> updateFilters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">newFilters</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#E36209;--shiki-dark:#FFAB70">search</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">; </span><span style="color:#E36209;--shiki-dark:#FFAB70">lead</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  filters.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">update</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">f</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">f, </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">newFilters }));</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Action to reset filters</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> resetFilters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  filters.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">set</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ search: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">""</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, lead: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Action to set loading state</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">isLoading</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  loading.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">set</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(isLoading);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Action to set error state</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> setError</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">errorMessage</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  error.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">set</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(errorMessage);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>This approach is much cleaner, with:</p>
<ul>
<li>Separate, focused stores for specific pieces of state</li>
<li>Simpler update functions</li>
<li>Automatic reactivity through derived stores</li>
<li>Minimal boilerplate</li>
</ul>
<h2 id="integrating-sveltekit-data-flow-with-stores">Integrating SvelteKit Data Flow with Stores<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5#integrating-sveltekit-data-flow-with-stores">#</a></h2>
<p>One of the most elegant patterns I discovered was integrating SvelteKit's server data loading with client-side stores.
In my <a href="https://github.com/igortosic-easymind/CRM-SvelteKit5">CRM application</a>, I implemented a hybrid approach:</p>
<ol>
<li>Server-side data loading in <code>+page.server.ts</code>:</li>
</ol>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// src/routes/clients/+page.server.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> load</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PageServerLoad</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> async</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="color:#E36209;--shiki-dark:#FFAB70">cookies</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">url</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Get query parameters</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> page</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Number</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">get</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"page"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "1"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> search</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">get</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"search"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ""</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> lead</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">get</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"lead"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">as</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Fetch data with filters</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clientsResponse</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> listClients</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(cookies, {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    page,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    itemsPerPage: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">10</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    search,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    lead: lead </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> undefined</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    clients: clientsResponse.data,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    pagination: clientsResponse.pagination,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    filters: {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      search,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      lead,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre></figure>
<ol start="2">
<li>Client-side store initialization in <code>+page.ts</code>:</li>
</ol>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// src/routes/clients/+page.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { PageLoad } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "./$types"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { setClientsData } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/store/clients"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { browser } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$app/environment"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> load</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PageLoad</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> async</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({ </span><span style="color:#E36209;--shiki-dark:#FFAB70">data</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">depends</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Register a dependency to ensure the data is re-fetched when navigating</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  depends</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"app:clients"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // If we're in the browser, update the store with the data from the server</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (browser) {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setClientsData</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      clients: data.clients,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      pagination: data.pagination,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      filters: data.filters,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    });</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Return the data as-is for the page component</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> data;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre></figure>
<ol start="3">
<li>Data access in components via props and/or stores:</li>
</ol>
<figure class="code-block" data-lang="svelte"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- src/lib/components/clients/client-list.svelte --></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { PaginationState, LeadStatus } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/types"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> SearchBar </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "./search-bar.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> Filters </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "./filters.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ClientTable </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "./client-table.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> RefreshButton </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "./refresh-button.svelte"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { Button } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/components/ui/button/index.js"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> Plus </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "@lucide/svelte/icons/plus"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Import stores</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    filteredClients,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    pagination </span><span style="color:#D73A49;--shiki-dark:#F97583">as</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> paginationStore,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    filters </span><span style="color:#D73A49;--shiki-dark:#F97583">as</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> filtersStore</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/store/clients"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // We still accept props for the initial SSR data</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { clients, pagination, filters </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {} } </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">props</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;{</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    clients</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> any</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    pagination</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PaginationState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">    filters</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#E36209;--shiki-dark:#FFAB70">search</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#24292E;--shiki-dark:#E1E4E8">; </span><span style="color:#E36209;--shiki-dark:#FFAB70">lead</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }>();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Use store values directly with $ prefix and $derived for reactivity</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clientsList</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">($filteredClients);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> paginationData</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">($paginationStore);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> currentFilters</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">($filtersStore);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Compute number of clients found</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clientsFound</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF">    `${</span><span style="color:#24292E;--shiki-dark:#E1E4E8">paginationData</span><span style="color:#032F62;--shiki-dark:#9ECBFF">.</span><span style="color:#24292E;--shiki-dark:#E1E4E8">totalItems</span><span style="color:#032F62;--shiki-dark:#9ECBFF">} ${</span><span style="color:#24292E;--shiki-dark:#E1E4E8">paginationData</span><span style="color:#032F62;--shiki-dark:#9ECBFF">.</span><span style="color:#24292E;--shiki-dark:#E1E4E8">totalItems</span><span style="color:#D73A49;--shiki-dark:#F97583"> ===</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 1</span><span style="color:#D73A49;--shiki-dark:#F97583"> ?</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'client'</span><span style="color:#D73A49;--shiki-dark:#F97583"> :</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'clients'} found`</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  );</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">&#x3C;!-- Component markup would go here, using clientsList, paginationData, etc. --></span></span></code></pre></figure>
<p>This approach gives me the best of both worlds:</p>
<ul>
<li>Server-rendered initial data for SEO and performance</li>
<li>Client-side reactivity for instant UI updates</li>
<li>A consistent data pattern throughout my application</li>
</ul>
<h2 id="svelte5-runes-a-new-level-of-reactivity">Svelte5 Runes: A New Level of Reactivity<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5#svelte5-runes-a-new-level-of-reactivity">#</a></h2>
<p>Svelte5 introduces runes - a new syntax for reactivity that makes the code even more elegant.
Runes like <code>$state()</code>, <code>$derived</code>, and the <code>$</code> prefix for store subscriptions create a unified reactive programming model:</p>
<figure class="code-block" data-lang="svelte"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> lang</span><span style="color:#24292E;--shiki-dark:#E1E4E8">=</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"ts"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    updateFilters,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    filters,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    loading</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/store/clients"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { Button } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/components/ui/button/index.js"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { LeadStatus } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "$lib/types"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#005CC5;--shiki-dark:#79B8FF">activeFilter</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "all"</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">props</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;{ </span><span style="color:#E36209;--shiki-dark:#FFAB70">activeFilter</span><span style="color:#D73A49;--shiki-dark:#F97583">?:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "all"</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }>();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Access store directly with $derived</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> isLoading</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">($loading);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> storeFilters</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> $</span><span style="color:#6F42C1;--shiki-dark:#B392F0">derived</span><span style="color:#24292E;--shiki-dark:#E1E4E8">($filters);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> handleFilterChange</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">filter</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LeadStatus</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "all"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Update store directly</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (filter </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "all"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      updateFilters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ lead: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> });</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="color:#D73A49;--shiki-dark:#F97583">else</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      updateFilters</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ lead: filter });</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Update URL without page reload</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> url</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> URL</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(window.location.href);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (filter </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "all"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"lead"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="color:#D73A49;--shiki-dark:#F97583">else</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">set</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"lead"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, filter);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    url.searchParams.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">delete</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"page"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">); </span><span style="color:#6A737D;--shiki-dark:#6A737D">// Reset to first page when filtering</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    history.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">pushState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({}, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">""</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, url.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">toString</span><span style="color:#24292E;--shiki-dark:#E1E4E8">());</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#22863A;--shiki-dark:#85E89D">script</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre></figure>
<p>The <code>$derived</code> rune creates variables that automatically update when their dependencies change, eliminating the need for useEffect-style hooks or complicated state management.</p>
<h2 id="no-more-provider-hell">No More "Provider Hell"<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5#no-more-provider-hell">#</a></h2>
<p>One of the most striking differences I found between React/Redux and Svelte is the simplicity of data flow. In React, we often end up with deeply nested providers:</p>
<figure class="code-block" data-lang="jsx"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// React "Provider Hell"</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">AuthProvider</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">ThemeProvider</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    &#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">DataProvider</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      &#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">LayoutProvider</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        &#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF">App</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> /></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      &#x3C;/</span><span style="color:#005CC5;--shiki-dark:#79B8FF">LayoutProvider</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    &#x3C;/</span><span style="color:#005CC5;--shiki-dark:#79B8FF">DataProvider</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  &#x3C;/</span><span style="color:#005CC5;--shiki-dark:#79B8FF">ThemeProvider</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;/</span><span style="color:#005CC5;--shiki-dark:#79B8FF">AuthProvider</span><span style="color:#24292E;--shiki-dark:#E1E4E8">></span></span></code></pre></figure>
<p>With SvelteKit's built-in data loading and Svelte's stores, this entire hierarchy simply disappears.
Data flows naturally from server to components, and stores are globally accessible without any provider wrappers.</p>
<h2 id="why-companies-should-consider-svelte-for-their-tech-stack">Why Companies Should Consider Svelte for Their Tech Stack<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5#why-companies-should-consider-svelte-for-their-tech-stack">#</a></h2>
<p>After my experience migrating from <a href="https://github.com/igortosic-easymind/CRM-demo">Next.js-CRM-demo</a> to <a href="https://github.com/igortosic-easymind/CRM-SvelteKit5">CRM-SvelteKit5</a>,
I believe companies should seriously consider adopting Svelte for these reasons:</p>
<ol>
<li><strong>Development Speed</strong>: Less boilerplate, fewer concepts, and a more intuitive API mean faster development cycles.</li>
<li><strong>Performance</strong>: Svelte's compile-time approach eliminates the need for a virtual DOM, resulting in smaller bundle sizes and faster runtime performance.</li>
<li><strong>Maintainability</strong>: The code is more readable and expresses intent more clearly than React's often complex component patterns.</li>
<li><strong>Learning Curve</strong>: Svelte builds on standard HTML, CSS, and JavaScript concepts, making it easier for new developers to learn.</li>
<li><strong>Built-in State Management</strong>: No need for Redux, MobX, Recoil, or other external libraries - Svelte's stores are simple, powerful, and built-in.</li>
<li><strong>Progressive Enhancement</strong>: SvelteKit's server-side rendering and progressive enhancement philosophy mean better user experiences across all devices.</li>
</ol>
<p>The question shouldn't be "Why Svelte?" but rather "Why not?" Companies often default to React simply because of its popularity,
but Svelte provides a more efficient, developer-friendly alternative that can lead to better products and happier development teams.</p>
<h3 id="conclusion">Conclusion<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-svelte-shift-from-react-redux-to-sveltekit5#conclusion">#</a></h3>
<p>My journey from React/Redux to SvelteKit5 has been eye-opening.
While React has dominated the frontend landscape for years, Svelte offers a fundamentally different approach that addresses many of React's pain points.
Its compiler-centric design, intuitive reactivity system, and elegant state management make it a compelling alternative.</p>
<p>As for SvelteKit, it takes Svelte's strengths and builds a complete application framework that rivals Next.js in capabilities while maintaining Svelte's simplicity and developer experience.
The integration of server and client rendering, the automatic data flow, and the file-based routing system all contribute to a development experience that feels natural and productive.</p>
<p>SolidJS is another excellent alternative, but Svelte's growing community and mature ecosystem make it a safer bet for companies looking to adopt a React alternative.
I believe we're seeing the beginning of a shift in the frontend landscape, with Svelte positioned to capture a significant portion of the market as developers and companies realize its advantages.</p>
<p>For me, the migration has been well worth the effort, and I look forward to building more applications with Svelte and SvelteKit in the future.</p>]]></content:encoded>
		</item>
		<item>
			<title>Redux Toolkit: The Evolution of State Management in Modern React Applications</title>
			<link>https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications</link>
			<guid isPermaLink="true">https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications</guid>
			<pubDate>Fri, 02 May 2025 00:00:00 GMT</pubDate>
			<dc:creator>Igor Tosic</dc:creator>
			<description>Redux Toolkit in a Next.js app: reducers vs extraReducers, createSlice and createAsyncThunk, RTK vs Context, and pairing Redux with server actions.</description>
			<category>react</category>
			<category>state-management</category>
			<category>architecture</category>
			<category>typescript</category>
			<content:encoded><![CDATA[<h2 id="introduction">Introduction<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#introduction">#</a></h2>
<p>Frontend state management has evolved significantly over the years, with Redux being one of the most influential libraries in the React ecosystem.
Today, Redux Toolkit (RTK) has emerged as the preferred way to implement Redux, bringing significant improvements to the developer experience.
In this article, I'll explore Redux Toolkit's architecture, its integration with NextJS, and practical patterns for building scalable applications.</p>
<h3 id="the-historical-journey-of-redux">The Historical Journey of Redux<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#the-historical-journey-of-redux">#</a></h3>
<p>Redux was born in 2015, created by Dan Abramov and Andrew Clark as a predictable state container for JavaScript applications.
It drew inspiration from Facebook's Flux architecture and the Elm programming language's approach to state management.
What began as a mere 99 lines of code quickly became the dominant state management solution for React applications.
The Redux journey includes several key milestones:</p>
<ul>
<li>2015: Initial release at React Europe conference</li>
<li>2019: Redux Toolkit was introduced to address the "boilerplate" complaints</li>
<li>2020: Redux Toolkit 1.0 launched with full TypeScript support</li>
<li>2021: RTK Query was introduced for data fetching and caching</li>
</ul>
<p>While Redux provided powerful capabilities like time-travel debugging, it also gained a reputation for verbosity and complexity.
Redux Toolkit emerged as a solution to these challenges, streamlining the Redux experience while maintaining its core principles.</p>
<h2 id="redux-toolkit-simplifying-state-management">Redux Toolkit: Simplifying State Management<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#redux-toolkit-simplifying-state-management">#</a></h2>
<h3 id="reducers-vs-extrareducers">Reducers vs ExtraReducers<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#reducers-vs-extrareducers">#</a></h3>
<p>One of Redux Toolkit's key improvements is how it handles reducers and their interactions with asynchronous operations.
Let's explore this using examples from my <a href="https://github.com/igortosic-easymind/CRM-demo">CRM demo project</a>.</p>
<h3 id="reducers">Reducers<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#reducers">#</a></h3>
<p>In Redux Toolkit, reducers are defined within <code>createSlice</code> to handle actions that are automatically created for that slice:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// From src/store/clientsSlice.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clientsSlice</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createSlice</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  name: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'clients'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  initialState,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  reducers: {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setClients</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">action</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PayloadAction</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Client</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[]>) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.list </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> action.payload</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.error </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setCurrentClient</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">action</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PayloadAction</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Client</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.currentClient </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> action.payload</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.error </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // More reducers...</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre></figure>
<p>These reducers handle state changes that are "owned" by this slice and are used for direct, synchronous state updates.
They work particularly well with Next.js server actions, allowing for clean interaction between server and client state.</p>
<h3 id="extrareducers">ExtraReducers<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#extrareducers">#</a></h3>
<p>ExtraReducers, on the other hand, react to actions that are defined elsewhere, typically from async thunks:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Example of extraReducers in a typical implementation</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">extraReducers</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="color:#E36209;--shiki-dark:#FFAB70">builder</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  builder</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="color:#6F42C1;--shiki-dark:#B392F0">addCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(fetchClients.pending, (</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.loading.list </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> true</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    })</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="color:#6F42C1;--shiki-dark:#B392F0">addCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(fetchClients.fulfilled, (</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">action</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.list </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> action.payload</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.loading.list </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> false</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    })</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    .</span><span style="color:#6F42C1;--shiki-dark:#B392F0">addCase</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(fetchClients.rejected, (</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">action</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.error </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> action.error.message </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'Failed to fetch clients'</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.loading.list </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> false</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    })</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>ExtraReducers don't generate action creators but instead react to actions from async thunks or other slices.
This builder pattern provides better TypeScript support and is ideal for handling asynchronous operations or cross-slice communication.</p>
<h3 id="createslice-vs-createasyncthunk">createSlice vs createAsyncThunk<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#createslice-vs-createasyncthunk">#</a></h3>
<p>The relationship between <code>createSlice</code> and <code>createAsyncThunk</code> is central to modern Redux applications.</p>
<h3 id="createslice">createSlice<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#createslice">#</a></h3>
<p><code>createSlice</code> generates a reducer function, action creators, and action types in one unified API:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// From src/store/taskSlice.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> taskSlice</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createSlice</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  name: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"tasks"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  initialState,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  reducers: {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setTasks</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">action</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PayloadAction</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Task</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[]>) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.list </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> action.payload;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.error </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // More reducers...</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">});</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#005CC5;--shiki-dark:#79B8FF">setTasks</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> } </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> taskSlice.actions;</span></span></code></pre></figure>
<h3 id="createasyncthunk">createAsyncThunk<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#createasyncthunk">#</a></h3>
<p><code>createAsyncThunk</code> handles asynchronous operations with standardized lifecycle actions:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Example async thunk implementation</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> fetchTasks</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createAsyncThunk</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'tasks/fetchTasks'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#D73A49;--shiki-dark:#F97583">async</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> response</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> fetch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'/api/tasks'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> response.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">json</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span></code></pre></figure>
<p>Each async thunk automatically dispatches:</p>
<ul>
<li>A pending action when it starts</li>
<li>A fulfilled action on success</li>
<li>A rejected action on error</li>
</ul>
<p>In my <a href="https://github.com/igortosic-easymind/CRM-demo">CRM demo project</a>,
I've taken a different approach by using server actions for data fetching, which aligns well with Next.js's server components architecture:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// From src/app/actions/tasks.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> async</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> listTasks</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">params</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ListTasksParams</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {})</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Promise</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ListTasksResponse</span><span style="color:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  try</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> token</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> getAuthToken</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#D73A49;--shiki-dark:#F97583">!</span><span style="color:#24292E;--shiki-dark:#E1E4E8">token) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        success: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        error: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Authentication required"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        data: [],</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        pagination: { </span><span style="color:#6A737D;--shiki-dark:#6A737D">/* pagination data */</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // API call implementation</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // ...</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> response.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">json</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  } </span><span style="color:#D73A49;--shiki-dark:#F97583">catch</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (error) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      success: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      error: error </span><span style="color:#D73A49;--shiki-dark:#F97583">instanceof</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Error</span><span style="color:#D73A49;--shiki-dark:#F97583"> ?</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> error.message </span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Failed to fetch tasks"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      data: [],</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      pagination: { </span><span style="color:#6A737D;--shiki-dark:#6A737D">/* pagination data */</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>These server actions are then called from components, and their results are dispatched to the Redux store:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// From src/components/tasks/list/TaskList.tsx</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> loadTasks</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useCallback</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">async</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  try</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ operation: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'list'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, isLoading: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }))</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setError</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> result</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> listTasks</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      status: filters.status,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      search: filters.searchTerm,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      client_id: filters.clientId,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      page: pagination.currentPage,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      itemsPerPage: pagination.itemsPerPage,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    })</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (result.success </span><span style="color:#D73A49;--shiki-dark:#F97583">&#x26;&#x26;</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> result.data) {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setTasks</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(result.data))</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setPagination</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(result.pagination))</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="color:#D73A49;--shiki-dark:#F97583">else</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setError</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(result.error </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'Failed to load tasks'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">))</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  } </span><span style="color:#D73A49;--shiki-dark:#F97583">catch</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (err) {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      setError</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        err </span><span style="color:#D73A49;--shiki-dark:#F97583">instanceof</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Error</span><span style="color:#D73A49;--shiki-dark:#F97583"> ?</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> err.message </span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'An unexpected error occurred'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      ),</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    )</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  } </span><span style="color:#D73A49;--shiki-dark:#F97583">finally</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ operation: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'list'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, isLoading: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> }))</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setIsInitialMount</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}, [</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  dispatch,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  filters.status,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  filters.searchTerm,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  filters.clientId,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  isInitialMount,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  pagination.currentPage,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  pagination.itemsPerPage,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">])</span></span></code></pre></figure>
<p>This pattern combines Next.js server-side capabilities with client-side state management for a hybrid approach that leverages the strengths of both.</p>
<h2 id="redux-toolkit-vs-context-api--usereducer">Redux Toolkit vs Context API + useReducer<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#redux-toolkit-vs-context-api--usereducer">#</a></h2>
<p>A common question in the React community is whether to use Redux Toolkit or React's built-in Context API with useReducer. Both have their place, but they serve different needs.</p>
<h3 id="when-to-choose-redux-toolkit">When to Choose Redux Toolkit<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#when-to-choose-redux-toolkit">#</a></h3>
<p>Redux Toolkit shines in applications with:</p>
<ol>
<li>Complex Global State: When your application has many interconnected state slices</li>
<li>Team Collaboration: When you need a standardized approach for a team</li>
<li>Performance Optimization: Redux's selective component updates outperform Context for large state trees</li>
<li>Developer Tools: Time-travel debugging through Redux DevTools</li>
<li>Middleware Ecosystem: For logging, persistence, etc.</li>
</ol>
<h3 id="when-to-choose-context--usereducer">When to Choose Context + useReducer<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#when-to-choose-context--usereducer">#</a></h3>
<p>Context and useReducer are better suited for:</p>
<ol>
<li>Simpler Applications: With limited global state needs</li>
<li>Component Tree Scoping: When state is only needed in a specific part of your app</li>
<li>Bundle Size Concerns: No additional dependencies required</li>
<li>Simpler Setup: Less boilerplate for small features</li>
</ol>
<p>In <a href="https://github.com/igortosic-easymind/CRM-demo">CRM demo</a>, Redux Toolkit was the right choice due to the complex state requirements and the need for organized state management across multiple features like clients, tasks, and calendar events.</p>
<h2 id="the-evolution-from-switchcase-to-structured-reducers">The Evolution from Switch/Case to Structured Reducers<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#the-evolution-from-switchcase-to-structured-reducers">#</a></h2>
<p>One of the most significant improvements in Redux Toolkit is how it handles reducers and action types.</p>
<h3 id="traditional-redux-pre-rtk">Traditional Redux (Pre-RTK)<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#traditional-redux-pre-rtk">#</a></h3>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Action types as constants</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> ADD_TASK</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'tasks/ADD_TASK'</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> UPDATE_TASK</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'tasks/UPDATE_TASK'</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Action creators</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> addTask</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">task</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> ({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  type: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">ADD_TASK</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  payload: task,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">})</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Reducer with switch/case</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> tasksReducer</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [], </span><span style="color:#E36209;--shiki-dark:#FFAB70">action</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  switch</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (action.type) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    case</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> ADD_TASK</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">state, action.payload]</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    case</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> UPDATE_TASK</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">map</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">task</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        task.id </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> action.payload.id </span><span style="color:#D73A49;--shiki-dark:#F97583">?</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">task, </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">action.payload } </span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> task,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      )</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    default</span><span style="color:#24292E;--shiki-dark:#E1E4E8">:</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<h3 id="redux-toolkit-way">Redux Toolkit Way<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#redux-toolkit-way">#</a></h3>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// From src/store/taskSlice.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> taskSlice</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createSlice</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  name: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"tasks"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  initialState,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  reducers: {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    addTask</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">action</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PayloadAction</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Task</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.list.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">push</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(action.payload);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.error </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    updateTask</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: (</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">action</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PayloadAction</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Task</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> index</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state.list.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">findIndex</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">task</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> task.id </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> action.payload.id);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (index </span><span style="color:#D73A49;--shiki-dark:#F97583">!==</span><span style="color:#D73A49;--shiki-dark:#F97583"> -</span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        state.list[index] </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> action.payload;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      }</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (state.currentTask?.id </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> action.payload.id) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        state.currentTask </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> action.payload;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      state.error </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">});</span></span></code></pre></figure>
<p>Behind the scenes, Redux Toolkit:</p>
<ol>
<li>Generates action types by combining slice name with reducer function name (<code>tasks/addTask</code>)</li>
<li>Creates action creators that produce actions with the correct type and payload</li>
<li>Builds a reducer function with an internal switch/case that maps action types to your reducer functions</li>
<li>Integrates Immer so you can write "mutating" code that actually produces immutable updates</li>
</ol>
<p>This approach dramatically reduces boilerplate while maintaining the predictability that made Redux popular.</p>
<h2 id="typed-custom-hooks-for-redux">Typed Custom Hooks for Redux<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#typed-custom-hooks-for-redux">#</a></h2>
<p>One improvement that could be made to <a href="https://github.com/igortosic-easymind/CRM-demo">CRM project</a> is adding typed custom hooks for accessing the Redux store.
While the project works without them, adding these hooks would improve type safety and DX:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Suggested improvement</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { TypedUseSelectorHook, useDispatch, useSelector } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "react-redux"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">import</span><span style="color:#D73A49;--shiki-dark:#F97583"> type</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { RootState, AppDispatch } </span><span style="color:#D73A49;--shiki-dark:#F97583">from</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "./store"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Use throughout your app instead of plain `useDispatch` and `useSelector`</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useAppDispatch</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useDispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">AppDispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>();</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> useAppSelector</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> TypedUseSelectorHook</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">RootState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">> </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> useSelector;</span></span></code></pre></figure>
<p>Using these hooks would provide better TypeScript inference throughout the application:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Before</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clients</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useSelector</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> RootState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state.clients.list);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> dispatch</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useDispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// After (with improved type safety)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clients</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useAppSelector</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#D73A49;--shiki-dark:#F97583"> =></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state.clients.list);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> dispatch</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useAppDispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span></code></pre></figure>
<h2 id="integrating-with-nextjs-a-modern-approach">Integrating with Next.js: A Modern Approach<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#integrating-with-nextjs-a-modern-approach">#</a></h2>
<p>Next.js has evolved significantly, especially with the introduction of React Server Components and Server Actions.
This has changed how I approach state management in Next.js applications.</p>
<p>In <a href="https://github.com/igortosic-easymind/CRM-demo">CRM project</a>, I've adopted a hybrid approach:</p>
<ol>
<li>Server Actions for data fetching and mutations</li>
<li>Redux Toolkit for client-side state management</li>
<li>Component-based patterns for connecting the two</li>
</ol>
<p>This approach allows us to leverage the strengths of both paradigms:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// From src/app/actions/clients.ts (Server Action)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> async</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createClient</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">clientData</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> CreateClientData</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Promise</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">ClientResponse</span><span style="color:#24292E;--shiki-dark:#E1E4E8">> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  try</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> token</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> getAuthToken</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#D73A49;--shiki-dark:#F97583">!</span><span style="color:#24292E;--shiki-dark:#E1E4E8">token) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { success: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, error: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Authentication required"</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> response</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> fetch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">`${</span><span style="color:#005CC5;--shiki-dark:#79B8FF">API_URL</span><span style="color:#032F62;--shiki-dark:#9ECBFF">}/clients/`</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      method: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"POST"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      headers: {</span></span>
<span class="line"><span style="color:#032F62;--shiki-dark:#9ECBFF">        "Content-Type"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">"application/json"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        Authorization: </span><span style="color:#032F62;--shiki-dark:#9ECBFF">`Bearer ${</span><span style="color:#24292E;--shiki-dark:#E1E4E8">token</span><span style="color:#032F62;--shiki-dark:#9ECBFF">}`</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      body: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">JSON</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">stringify</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(clientData),</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    });</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#D73A49;--shiki-dark:#F97583">!</span><span style="color:#24292E;--shiki-dark:#E1E4E8">response.ok) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> handleApiError</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(response);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> data</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> response.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">json</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> { success: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, data };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  } </span><span style="color:#D73A49;--shiki-dark:#F97583">catch</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (error) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      success: </span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      error: error </span><span style="color:#D73A49;--shiki-dark:#F97583">instanceof</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Error</span><span style="color:#D73A49;--shiki-dark:#F97583"> ?</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> error.message </span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Failed to create client"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    };</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// From src/components/clients/form/ClientForm.tsx (Client Component)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> handleSubmit</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> async</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">e</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> React</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">FormEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  e.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">preventDefault</span><span style="color:#24292E;--shiki-dark:#E1E4E8">();</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#D73A49;--shiki-dark:#F97583">!</span><span style="color:#6F42C1;--shiki-dark:#B392F0">validateForm</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()) {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setError</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"Please fill in all required fields correctly"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">true</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  setError</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  try</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (mode </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "create"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> result</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createClient</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(formData);</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (result.success </span><span style="color:#D73A49;--shiki-dark:#F97583">&#x26;&#x26;</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> result.data) {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">        dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">addClient</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(result.data));</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        router.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">push</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">"/clients"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      } </span><span style="color:#D73A49;--shiki-dark:#F97583">else</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">        console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(result.error);</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">        setError</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(result.error </span><span style="color:#D73A49;--shiki-dark:#F97583">||</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "Failed to create client"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="color:#D73A49;--shiki-dark:#F97583">else</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">      // Update client logic...</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  } </span><span style="color:#D73A49;--shiki-dark:#F97583">catch</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (err) {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setError</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(err </span><span style="color:#D73A49;--shiki-dark:#F97583">instanceof</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Error</span><span style="color:#D73A49;--shiki-dark:#F97583"> ?</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> err.message </span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> "An unexpected error occurred"</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  } </span><span style="color:#D73A49;--shiki-dark:#F97583">finally</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    setLoading</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">);</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">};</span></span></code></pre></figure>
<h2 id="the-philosophy-of-state-management">The Philosophy of State Management<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#the-philosophy-of-state-management">#</a></h2>
<p>Beyond the technical details, state management is about how we conceptualize our applications. Redux encourages us to think of state as a "single source of truth" that drives our UI, rather than the other way around.</p>
<p>In <a href="https://github.com/igortosic-easymind/CRM-demo">CRM application</a>,
this philosophy manifests in how we structure our state. For example, our client state not only tracks the list of clients but also loading states, errors, filters, pagination, and more:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// From src/types/index.ts</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> interface</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ClientsState</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  list</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Client</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  currentClient</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Client</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  loading</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> LoadingState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  error</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  filters</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> FilterState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  pagination</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> PaginationState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  sorting</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> SortingState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Client</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>This comprehensive approach ensures that our UI is always driven by the state, making our application more predictable and easier to debug.</p>
<h2 id="conclusion">Conclusion<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-evolution-of-state-management-in-modern-react-applications#conclusion">#</a></h2>
<p>Redux Toolkit represents a significant evolution in how we manage state in React applications.
By addressing the pain points of traditional Redux while maintaining its core principles, RTK provides a powerful yet developer-friendly approach to state management.</p>
<p>In this Next.js <a href="https://github.com/igortosic-easymind/CRM-demo">CRM application</a>,
we've seen how RTK integrates with modern Next.js features like Server Actions to create a hybrid architecture that leverages the strengths of both client and server state management.
This approach allows us to build complex, interactive applications while maintaining a clear separation of concerns.</p>
<p>As the React ecosystem continues to evolve, the patterns and practices we've explored will remain valuable, even as the specific technologies change.
The fundamental principles of predictable state changes, clear data flow, and component-driven architecture will continue to guide us in building robust, maintainable applications.</p>
<hr>
<p><em>In the next article in this series, I will explore state management in Svelte and SvelteKit 5, examining how Svelte's unique approach with runes compares to the patterns we've discussed here.</em></p>]]></content:encoded>
		</item>
		<item>
			<title>The Journey to Better Architecture</title>
			<link>https://www.igortosic.com/blog/the-journey-to-better-architecture</link>
			<guid isPermaLink="true">https://www.igortosic.com/blog/the-journey-to-better-architecture</guid>
			<pubDate>Tue, 05 Nov 2024 00:00:00 GMT</pubDate>
			<dc:creator>Igor Tosic</dc:creator>
			<description>Lessons from building a canvas annotation app with React, Redux and Konva: state as the source of truth, event handling as translation, and performance.</description>
			<category>react</category>
			<category>state-management</category>
			<category>architecture</category>
			<category>canvas</category>
			<content:encoded><![CDATA[<h2 id="a-personal-journey-into-state-management">A Personal Journey into State Management<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#a-personal-journey-into-state-management">#</a></h2>
<p>As a developer currently working on a complex canvas-based annotation system,
I find myself exploring and reimagining better architectural patterns.
This ongoing experience has inspired the creation of <a href="https://github.com/igortosic-easymind/startproject-react-redux-konva-annotationApp">EasyNotes Canvas</a>,
which serves as a proving ground for implementing these architectural improvements.
This is a story about that journey, and the insights being gained along the way.</p>
<h3 id="the-birth-of-easynotes-canvas">The Birth of EasyNotes Canvas<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#the-birth-of-easynotes-canvas">#</a></h3>
<p><a href="https://github.com/igortosic-easymind/startproject-react-redux-konva-annotationApp">EasyNotes Canvas</a> emerged from a simple yet profound realization:
the tools we use for digital note-taking and annotation should feel as natural as pen and paper,
yet harness the full power of modern web technologies.
Built with React, Redux, and Konva, it serves as a canvas for thoughts, ideas, and collaborative work.</p>
<p>But beyond its functional purpose, EasyNotes Canvas represents something more:
a quest for architectural elegance in the face of complexity.</p>
<h2 id="the-state-management-epiphany">The State Management Epiphany<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#the-state-management-epiphany">#</a></h2>
<h3 id="wrestling-with-complexity">Wrestling with Complexity<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#wrestling-with-complexity">#</a></h3>
<p>My initial attempts at managing canvas state were, frankly, chaotic.
Local state scattered across components, prop drilling that made code reviews painful,
and event handlers that grew more complex with each new feature.
It was a classic case of organic growth without architectural foresight.</p>
<figure class="code-block" data-lang="jsx"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">// Early attempts at state management - a lesson in what not to do</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> CanvasComponent</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="color:#005CC5;--shiki-dark:#79B8FF">shapes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">setShapes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">] </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">([])</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="color:#005CC5;--shiki-dark:#79B8FF">selectedShape</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">setSelectedShape</span><span style="color:#24292E;--shiki-dark:#E1E4E8">] </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="color:#005CC5;--shiki-dark:#79B8FF">isDrawing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">setIsDrawing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">] </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useState</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">false</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // More state variables...</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>The turning point came when I realized that canvas applications aren't just about drawing
—they're about managing a complex ecosystem of states, events, and user interactions.
This led to a fundamental shift in thinking about state management.</p>
<h2 id="the-redux-revelation">The Redux Revelation<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#the-redux-revelation">#</a></h2>
<p>Moving to Redux wasn't just about choosing a state management library;
it was about adopting a philosophy of predictable state changes and clear data flow.
The resulting architecture became a testament to this philosophy:</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">interface</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ShapeState</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  shapes</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ShapeProps</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  savedShapes</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ShapeProps</span><span style="color:#24292E;--shiki-dark:#E1E4E8">[];</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  isDirty</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> boolean</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  selectedShapeId</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  activeTool</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> ShapeType</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> string</span><span style="color:#D73A49;--shiki-dark:#F97583"> |</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> null</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // A clear representation of our application's state</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>Each property in this interface tells a story about user interaction and application behavior.
The <code>isDirty</code> flag, for instance, represents the tension between user changes and persistence—a
simple boolean that carries significant meaning for the user experience.</p>
<p>While React's Context API and useReducer hook might seem like suitable alternatives—and
they are excellent for simpler applications—the decision to use Redux came from deeper architectural
needs. Context API, while powerful for prop drilling prevention,
lacks the robust middleware ecosystem that Redux provides for handling complex side effects.
The useReducer hook, though similar in concept to Redux,
doesn't offer the same level of state isolation and time-travel debugging capabilities
that become invaluable when debugging complex canvas interactions.
In a canvas application where every mouse movement could trigger state changes
and where undo/redo functionality is crucial,
Redux's DevTools and middleware system prove themselves indispensable for development and debugging.</p>
<h2 id="the-art-of-event-handling">The Art of Event Handling<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#the-art-of-event-handling">#</a></h2>
<h3 id="finding-harmony-between-events-and-state">Finding Harmony Between Events and State<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#finding-harmony-between-events-and-state">#</a></h3>
<p>One of the most enlightening aspects of this journey was discovering the delicate balance
between immediate user feedback and state consistency.
Mouse events in canvas applications aren't just about capturing coordinates;
they're about translating human intent into digital creation.</p>
<figure class="code-block" data-lang="ts"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useCanvasEvents</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">handleShapeAdd</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">handleShapeUpdate</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> handleMouseDown</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> useCallback</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">e</span><span style="color:#D73A49;--shiki-dark:#F97583">:</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Konva</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">KonvaEventObject</span><span style="color:#24292E;--shiki-dark:#E1E4E8">&#x3C;</span><span style="color:#6F42C1;--shiki-dark:#B392F0">MouseEvent</span><span style="color:#24292E;--shiki-dark:#E1E4E8">>) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // This moment of interaction initiates a cascade of state changes</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> clickedOnShape</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> e.target </span><span style="color:#D73A49;--shiki-dark:#F97583">instanceof</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Konva</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">Shape</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (clickedOnShape) {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">      dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">selectShape</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(e.target.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">id</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()));</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8">;</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // The beginning of creation...</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }, [dispatch]);</span></span></code></pre></figure>
<p>This code represents more than just event handling—it's
the bridge between user intention and application state.
Each mouse movement tells a story, and our architecture must be ready to listen and respond.</p>
<h2 id="lessons-in-architecture">Lessons in Architecture<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#lessons-in-architecture">#</a></h2>
<h3 id="the-value-of-clarity">The Value of Clarity<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#the-value-of-clarity">#</a></h3>
<p>Working on my current canvas project has taught me the importance of architectural clarity.
When you're dealing with complex interactions and state changes,
your architecture needs to be more than just functional—it needs to be comprehensible.
This led to some key principles in EasyNotes Canvas:</p>
<p><strong>1. State as a Source of Truth:</strong>
Every shape, every tool selection,
every user interaction is reflected in our Redux store.
This isn't just about state management—it's about creating a clear narrative of application state.</p>
<p><strong>2. Event Handling as Translation:</strong>
Mouse events become a translation layer between user intent
and application state. This perspective changed how I approached event handling:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> handleShapeModification</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> async</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">shapeId</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">newProperties</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">  // Immediate feedback for the user</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">updateShape</span><span style="color:#24292E;--shiki-dark:#E1E4E8">({ id: shapeId, </span><span style="color:#D73A49;--shiki-dark:#F97583">...</span><span style="color:#24292E;--shiki-dark:#E1E4E8">newProperties }))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  try</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Ensuring our changes persist</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    await</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">saveShapes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(currentProjectId)).</span><span style="color:#6F42C1;--shiki-dark:#B392F0">unwrap</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  } </span><span style="color:#D73A49;--shiki-dark:#F97583">catch</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (error) {</span></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">    // Gracefully handling when things go wrong</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    dispatch</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">revertShapeUpdate</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(shapeId))</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<h3 id="performance-as-a-feature">Performance as a Feature<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#performance-as-a-feature">#</a></h3>
<p>One of the most valuable lessons from my project was that performance isn't just about
optimization—it's about user experience.
This led to implementations like batch operations and memoized selectors:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">export</span><span style="color:#D73A49;--shiki-dark:#F97583"> const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> selectSelectedShape</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createSelector</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  [(</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state.shapes.shapes, (</span><span style="color:#E36209;--shiki-dark:#FFAB70">state</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> state.shapes.selectedShapeId],</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  (</span><span style="color:#E36209;--shiki-dark:#FFAB70">shapes</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">selectedId</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> shapes.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">find</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">shape</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> shape.id </span><span style="color:#D73A49;--shiki-dark:#F97583">===</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> selectedId),</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre></figure>
<h2 id="looking-forward">Looking Forward<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#looking-forward">#</a></h2>
<p>The journey of building <a href="https://github.com/igortosic-easymind/startproject-react-redux-konva-annotationApp">EasyNotes Canvas</a> has been about applying lessons learned in real-time
from my current work with canvas applications.
Each architectural decision carries the weight of active experiences and challenges,
and each line of code represents a step toward better software design.</p>
<p>Future enhancements like PDF integration and collaborative editing aren't just features
to be added—they're opportunities to further refine our architectural approach.
The foundation we've built, inspired by real-world experiences and challenges,
stands ready for these additions.</p>
<h2 id="conclusion">Conclusion<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/the-journey-to-better-architecture#conclusion">#</a></h2>
<p>Building canvas applications has taught me that good architecture isn't just about patterns
and practices—it's about understanding the deeper relationships between user interactions,
application state, and system behavior. <a href="https://github.com/igortosic-easymind/startproject-react-redux-konva-annotationApp">EasyNotes Canvas</a> is more than just an application;
it's a reflection of this understanding, and a step forward in my journey as a developer.</p>
<hr>
<p><em>Next in this series: "Beyond Drawing - Implementing Document Integration in Canvas Applications,"
where we'll explore the architectural challenges of adding PDF and document support
to canvas applications.</em></p>]]></content:encoded>
		</item>
		<item>
			<title>OOP vs. FP in JavaScript and generally :)</title>
			<link>https://www.igortosic.com/blog/oop-vs-fp</link>
			<guid isPermaLink="true">https://www.igortosic.com/blog/oop-vs-fp</guid>
			<pubDate>Mon, 27 Nov 2023 00:00:00 GMT</pubDate>
			<dc:creator>Igor Tosic</dc:creator>
			<description>Composition vs inheritance, the pros and cons of object-oriented and functional programming in JavaScript, and how to blend the two paradigms.</description>
			<category>javascript</category>
			<category>oop</category>
			<category>functional-programming</category>
			<content:encoded><![CDATA[<h2 id="object-oriented-programming-vs-functional-programming">Object-oriented programming vs functional programming<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-vs-fp#object-oriented-programming-vs-functional-programming">#</a></h2>
<p>JavaScript is a versatile language that supports multiple programming paradigms, allowing developers to choose the approach that best suits their needs.
Two prominent paradigms in <a href="https://www.igortosic.com/blog/javascript-oop">JavaScript are Object-Oriented Programming (OOP)</a> and <a href="https://www.igortosic.com/blog/functions-javascript">Functional Programming (FP)</a>.</p>
<p>Before I write the pros and cons, I would like to explain a bit about composition vs. inheritance.</p>
<h3 id="composition-vs-inheritance">Composition vs. Inheritance<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-vs-fp#composition-vs-inheritance">#</a></h3>
<p>To understand both concepts, we need to know what they are.
The main difference is inheritance; the child class depends on the parent class, and in composition, both the child and parent class are independent.
In inheritance, we use class extends; in composition, we use smaller pieces to create something more significant.
In the community, there is always a considerable debate about which approach is better, and usually, people prefer composition vs inheritance.
I will write about some issues or problems in inheritance and how composition is solved. Inheritance does not belong to OOP,
and composition does not belong to functional programming. We can do both things in both paradigms.</p>
<p>The first thing we must take care of in inheritance is our code structure.
We're structuring our classes around what things are. They have some data, methods, actions, etc.
On the other hand, with composition, we are structuring code what it has, or actually what it does to data.</p>
<p>Also, we need to think about things such as tight coupling and fragile base class problems.</p>
<p><strong>Tight Coupling:</strong>
This is when classes are highly dependent on each other. A change in one class may require changes in other classes.
Inheritance can lead to tight coupling because a subclass is heavily dependent on the implementation details of its superclass.
If the superclass changes, it can potentially break the functionality of the subclass.</p>
<p><strong>Fragile Base Class Problem:</strong>
This is a specific issue related to inheritance.
It occurs when changes to a base class can break the functionality of derived classes.
Since derived classes rely on the implementation of the base class, any changes to the base class can have unintended consequences in the derived classes.
These problems highlight the importance of designing class hierarchies carefully.
Tight coupling and the fragile base class problem can make code more challenging to maintain and evolve over time.</p>
<h3 id="oop-vs-fp">OOP vs. FP<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-vs-fp#oop-vs-fp">#</a></h3>
<p>Functional programming is all about performing many different operations for which the data is fixed.
OOP is about a few operations on common data.
In FP, the state is immutable versus object-oriented programming, which is very state-full.
In functional programming, functions are pure. There are no side effects.
It means that the functions that we write don't make an impact on the code that is running outside of that function.
In OOP, there are definitely side effects.
Functional programming is more declarative. It's about what we want to be doing vs. an object oriented program,
which is more about how we want it to be done, and it is more imperative.
Functional programming works really well for high-performance processors; you can run it on multiple processors.
If you have a few things that require a lot of operations, then functional programming is usually a good idea.
If, on the other hand, you have objects with not too many operations, then object-oriented programming might be a better solution.</p>
<p>You can use both ideas to write your code. In all programs, there are two primary segments: the data and the behaviours.
OOP brings together the data and the behaviour in a single location
called an object or class. This will make our program easier and more organized.
Functional programming says that data and behaviour are distinctly different things and should be kept separate for clarity.</p>
<h3 id="object-oriented-programming-oop">Object-Oriented Programming (OOP)<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-vs-fp#object-oriented-programming-oop">#</a></h3>
<h3 id="pros">Pros:<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-vs-fp#pros">#</a></h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" checked disabled> Encapsulation: OOP promotes encapsulation, enabling the bundling of data and methods into cohesive units called objects. This organizational structure enhances code clarity and maintainability.</li>
<li class="task-list-item"><input type="checkbox" checked disabled> Inheritance: OOP facilitates the creation of hierarchies through inheritance, allowing objects to inherit properties and behaviours from other objects. This promotes code reuse and extensibility.</li>
<li class="task-list-item"><input type="checkbox" checked disabled> Readability: Object-oriented code often mirrors real-world entities, making it more intuitive and readable for developers. This alignment with familiar concepts enhances the understanding of code.</li>
</ul>
<h3 id="cons">Cons:<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-vs-fp#cons">#</a></h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" checked disabled> Mutability: OOP frequently involves a mutable state, which can lead to issues like unexpected side effects and bugs. Managing state changes becomes crucial, especially in complex applications.</li>
<li class="task-list-item"><input type="checkbox" checked disabled> Verbosity: Object-oriented code can be more verbose compared to functional programming, potentially resulting in longer development times and increased opportunities for errors.</li>
</ul>
<h3 id="functional-programming-fp">Functional Programming (FP)<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-vs-fp#functional-programming-fp">#</a></h3>
<h3 id="pros-1">Pros:<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-vs-fp#pros-1">#</a></h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" checked disabled> Immutability: FP encourages immutability, meaning once a piece of data is created, it cannot be altered. This reduces the risk of bugs related to unintended side effects and makes the code more predictable.</li>
<li class="task-list-item"><input type="checkbox" checked disabled> Pure Functions: FP emphasizes the use of pure functions, which have no side effects and consistently produce the same output for the same input. This predictability enhances testability and maintainability.</li>
<li class="task-list-item"><input type="checkbox" checked disabled> Concurrency: Functional programming is well-suited for concurrent programming. Immutable data and pure functions minimize the chances of race conditions and other concurrency-related issues.</li>
</ul>
<h3 id="cons-1">Cons:<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-vs-fp#cons-1">#</a></h3>
<ul class="contains-task-list">
<li class="task-list-item"><input type="checkbox" checked disabled> Learning Curve: Functional programming concepts, such as higher-order functions and monads, may pose a learning curve for developers new to the paradigm. Mastery of these concepts is essential for leveraging the full power of FP.</li>
<li class="task-list-item"><input type="checkbox" checked disabled> Performance: In certain scenarios, functional programming can have performance implications, particularly when dealing with large datasets or intricate algorithms. Careful consideration is necessary to strike a balance between performance and functional principles.</li>
</ul>
<h2 id="blending-paradigms-in-javascript">Blending Paradigms in JavaScript<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/oop-vs-fp#blending-paradigms-in-javascript">#</a></h2>
<p>JavaScript allows developers to blend OOP and FP seamlessly, providing a flexible and expressive programming environment.
Many modern frameworks and libraries like React and Redux incorporate functional programming principles within an overarching object-oriented structure.
This hybrid approach enables developers to harness the strengths of both paradigms and create robust, scalable, and maintainable code.</p>
<p>In conclusion, the choice between OOP and FP in JavaScript depends on a project's specific requirements and the development team's preferences.
Understanding the strengths and weaknesses of each paradigm empowers developers to make informed decisions,
ultimately leading to the creation of efficient and reliable software solutions.</p>]]></content:encoded>
		</item>
		<item>
			<title>Functional programming in JavaScript</title>
			<link>https://www.igortosic.com/blog/functions-javascript</link>
			<guid isPermaLink="true">https://www.igortosic.com/blog/functions-javascript</guid>
			<pubDate>Fri, 29 Sep 2023 00:00:00 GMT</pubDate>
			<dc:creator>Igor Tosic</dc:creator>
			<description>Pure functions, immutability, higher-order functions, closures, currying, memoization and compose: the core ideas of functional programming in JavaScript.</description>
			<category>javascript</category>
			<category>functional-programming</category>
			<content:encoded><![CDATA[<p>Functional programming relies on functions :). And based on mathematical functions.</p>
<p>The idea of FP is the same as in OOP: to make our code clear and understandable, easy to extend, and easy to maintain.
It will allow us not to repeat ourselves, keeping our code dry and memory efficient.</p>
<p>In computer science, functional programming is a programming paradigm where programs are constructed by applying and composing functions
without mutating state and data.</p>
<p>In functional programming, functions are treated as first-class citizens, meaning they can be bound to names (including local identifiers),
passed as arguments, and returned from other functions, just as any other data type can.
That allows programs to be written in a declarative and composable style, where small functions are combined in a modular manner.
Functional programming is sometimes synonymous with purely functional programming,
a subset of functional programming that treats all functions as deterministic mathematical functions, or pure functions.
When a pure function is called with some given arguments, it will always return the same result and cannot be affected by any mutable state or other side effects.
This is in contrast with impure procedures, common in imperative programming, which can have side effects (such as modifying the program's state or taking input from a user).</p>
<p>If you want to break things down in functional programming, it all comes down to this concept of pure functions.
That is the main difference from OOP concepts such as encapsulation, abstraction, polymorphism and inheritance.</p>
<h3 id="pure-functions">Pure functions<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/functions-javascript#pure-functions">#</a></h3>
<p>Okay, so what are pure functions!? A Pure Function returns the same result if the same arguments are passed. A function cannot modify
anything outside of itself. No side effects.</p>
<p>We are going to see what is that side effect. If we log mutateArr we will get undefined because our function is returning nothing.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> array</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">2</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">3</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">4</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">5</span><span style="color:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> mutateArr</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">arr</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  arr.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">pop</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">mutateArr</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(array))</span></span></code></pre></figure>
<p>But if log <code>array</code> <code>console.log(array)</code>, we will get <code>[ 1, 2, 3, 4 ]</code>. So, it means we have mutated our array. And that is a side effect.
This function has side effects, and it modifies anything outside of itself. This function has side effects, and it modifies anything outside of itself.
We can imagine this array as a global state that can interact with everything in an application and produce many bugs, especially if many functions interact with the same state.</p>
<p>We can fix it like this:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> array</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [</span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">2</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">3</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">4</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">5</span><span style="color:#24292E;--shiki-dark:#E1E4E8">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> mutateArr</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">arr</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> newArray</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> [].</span><span style="color:#6F42C1;--shiki-dark:#B392F0">concat</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(array)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  newArray.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">pop</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> newArray</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">mutateArr</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(array))</span></span></code></pre></figure>
<p>So, we make a new copy of an array without affecting the outside world. But could it be everything pure function?</p>
<p>Having everything pure without side effects is nice, but programming and applications don't work like that. Our program needs to communicate with the outside world :).
To do fetch calls to talk to some other functions, etc. So, our goal is to minimize side effects.</p>
<p>Perfect function should do one task and return some statement. It should be pure :), with no shared state with other functions and an immutable state.
We always return a new copy of the output. Functions should be composable, and I will explain this through examples. And most important, we want to make functions predictable.</p>
<h3 id="imperative-vs-declarative">Imperative vs declarative<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/functions-javascript#imperative-vs-declarative">#</a></h3>
<p>We have those concepts in programming. Imperative code is code that tells the machine what to do and how to do it.
Declarative code tells it what to do and what should happen. It doesn't say to the computer how to do things.
A computer is better at being imperative. That is, it needs to know how to do something. We, on the other hand, as humans, are more declarative.
We usually say to friends, hei give that or that, but we don't say how they will do that. Humans are more declarative :).
For example, machine code is more imperative when we are talking about computers. If we go level up and up forward to some high-level languages, it becomes more declarative.
We declare a variable, and the computer does the rest of the job for us.
A good example in JS for imperative vs declarative is for loop:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">for</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#D73A49;--shiki-dark:#F97583">let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> i </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 0</span><span style="color:#24292E;--shiki-dark:#E1E4E8">; i </span><span style="color:#D73A49;--shiki-dark:#F97583">&#x3C;</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 10</span><span style="color:#24292E;--shiki-dark:#E1E4E8">; i</span><span style="color:#D73A49;--shiki-dark:#F97583">++</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>vs</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">;[</span><span style="color:#005CC5;--shiki-dark:#79B8FF">1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">2</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">3</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">4</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#005CC5;--shiki-dark:#79B8FF">5</span><span style="color:#24292E;--shiki-dark:#E1E4E8">].</span><span style="color:#6F42C1;--shiki-dark:#B392F0">forEach</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">item</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(item))</span></span></code></pre></figure>
<p>In the old traditional for loop, you have too many instructions to say what to do. With <code>forEach</code> you have that more declarative way.
jQuery is also a good example of an imperative approach. But I am writing this because functional programming helps us be more declarative.
Declarative code is easier to read, and we can be more productive.</p>
<h3 id="immutability">Immutability<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/functions-javascript#immutability">#</a></h3>
<p>In functional programming, the idea of immutability is not changing state but instead making copies of the state and returning a new state every time.</p>
<h3 id="hof-and-closure">HOF and Closure<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/functions-javascript#hof-and-closure">#</a></h3>
<p>Functions are first-class citizens, which means we can have high-order functions, and we can also have closures.
HOF means it's a function that does one of two things. It either takes one or more functions as arguments. Or returns a function as a result, often called a callback.
A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).
In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created at function creation time.</p>
<h3 id="curring">Curring<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/functions-javascript#curring">#</a></h3>
<p>Currying in JavaScript transforms a function with multiple arguments into a nested series of functions, each taking a single argument.
Currying helps you avoid passing the same variable multiple times, and it helps you create a higher-order function. An example looks like this:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> sum</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">a</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">b</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> a </span><span style="color:#D73A49;--shiki-dark:#F97583">*</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> b</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">//by now Curring</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> curriedSum</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">a</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">b</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> a </span><span style="color:#D73A49;--shiki-dark:#F97583">*</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> b</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">curriedSum</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">5</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">4</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre></figure>
<p>Because of closures, we have access to the A variable inside the B function. But why is this useful!?</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> curriedSum</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">a</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">b</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> a </span><span style="color:#D73A49;--shiki-dark:#F97583">*</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> b</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> curriedSumBy5</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> curriedSum</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">5</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6A737D;--shiki-dark:#6A737D">//after some time, after 7 years</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">curriedSumBy5</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">4</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre></figure>
<p>And instead of running <code>curriedSum(5)</code> this function forever it will be run only once. So when we run <code>curriedSumBy5(4)</code> we run only this part of function <code>(b) => a*b</code>.</p>
<h3 id="memoization">Memoization<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/functions-javascript#memoization">#</a></h3>
<p>To understand how dynamic programming works, we need to understand what caching means. Caching is a way to store values so you can use them later on.
Memorization is a specific form of caching.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> memoizeAddTo50</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">n</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> cache </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {}</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">n</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    if</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (n </span><span style="color:#D73A49;--shiki-dark:#F97583">in</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> cache) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> cache[n]</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    } </span><span style="color:#D73A49;--shiki-dark:#F97583">else</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'long time'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> answer</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> n </span><span style="color:#D73A49;--shiki-dark:#F97583">+</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 50</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">      cache[n] </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> answer</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> answer</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>If we try to call this function <code>memoizeAddTo50(5)</code> with same value more than once we will see caching in action :).</p>
<h3 id="compose">Compose<a class="heading-anchor" aria-hidden="true" tabindex="-1" href="https://www.igortosic.com/blog/functions-javascript#compose">#</a></h3>
<p>Compose is the most powerful concept. Function composition is an approach where the result of one function is passed on to the next function,
which is passed to another until the final function is executed for the final result. Function compositions can be composed of any number of functions.</p>
<p>In functional programming you can use something called compose. Compose doesn't exist in JavaScript, but we have many libraries that let you use compose.
One of the best is <a href="https://ramdajs.com/docs/#compose">Ramda's compose</a>.</p>
<p>Also we can make our own custom compose function, and here you have one of my examples:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">fn1</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">fn2</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">fn3</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">50</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">compose</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(fn1, fn2, fn3)(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">50</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#6A737D;--shiki-dark:#6A737D">//Right to left</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">pipe</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(fn3, fn2, fn1)(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">50</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#6A737D;--shiki-dark:#6A737D">//left to right</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> compose</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">f</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">g</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">a</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#6F42C1;--shiki-dark:#B392F0"> f</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">g</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(a))</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> pipe</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">f</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">g</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> (</span><span style="color:#E36209;--shiki-dark:#FFAB70">a</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#6F42C1;--shiki-dark:#B392F0"> g</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">f</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(a))</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> multiplyBy2AndAbsolute</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> compose</span><span style="color:#24292E;--shiki-dark:#E1E4E8">((</span><span style="color:#E36209;--shiki-dark:#FFAB70">num</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> num </span><span style="color:#D73A49;--shiki-dark:#F97583">*</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> 2</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, Math.abs)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#6F42C1;--shiki-dark:#B392F0">multiplyBy3AndMakeAbsolute</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#D73A49;--shiki-dark:#F97583">-</span><span style="color:#005CC5;--shiki-dark:#79B8FF">10</span><span style="color:#24292E;--shiki-dark:#E1E4E8">))</span></span></code></pre></figure>
<p>You can also see in this example function with name pipe. Actually, it is the same thing as composing but just going from left to right, see <a href="https://ramdajs.com/docs/#pipe">Ramda's pipe</a>.</p>
<p>Most of those concepts could be explained more in-depth, but this article is about generally functional programming and things around it.
Functional programming is very lovely. The idea in functional programming is to keep functions small, pure, and composable.
This idea of immutability is that a function takes inputs and returns outputs to be used with other functions.
It allows us to have a predictable program that minimizes bugs because everything is simple.</p>
<p>But is functional programming the answer for everything!? Sometimes OOP is a better way for some cases.
That is where we, as programmers, come out on the stage and show our best :)</p>]]></content:encoded>
		</item>
		<item>
			<title>OOP in JavaScript</title>
			<link>https://www.igortosic.com/blog/javascript-oop</link>
			<guid isPermaLink="true">https://www.igortosic.com/blog/javascript-oop</guid>
			<pubDate>Fri, 25 Aug 2023 00:00:00 GMT</pubDate>
			<dc:creator>Igor Tosic</dc:creator>
			<description>From object literals and factory functions to prototypes, constructor functions and ES6 classes: how object-oriented programming works in JavaScript.</description>
			<category>javascript</category>
			<category>oop</category>
			<content:encoded><![CDATA[<p>The beauty of JavaScript is multi-paradigm. We can use both functional and object-oriented approaches to make our code cleaner, more understandable,
easy to extend and more efficient. We can pick the paradigm for the specific problem based on our issues.</p>
<p>When we are talking about OOP in JavaScript, we need to cover things like this keyword, prototype inheritance, the new keyword,
the ES6 classes, object, create, private and public fields and four principles of OOP.</p>
<p>We know in OOP, we have two main types: class-based programming languages and prototype-based programming languages.
In JavaScript, we have prototype inheritance. Object-oriented programming is all about modelling real-world objects and relationships.</p>
<p>We can start a few steps behind OOP in JavaScript and see how things are doing with functions and objects.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> person</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    name: “igor”,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    hobby: “basketball”,</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    Doing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">        return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> name </span><span style="color:#D73A49;--shiki-dark:#F97583">+</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> " is love to play "</span><span style="color:#D73A49;--shiki-dark:#F97583"> +</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>What did we see, and what are the benefits of that!? We grouped functionality. We have states and functions or methods of doing something.
We have something called encapsulation. That is our first step in object-oriented programming.</p>
<p>That is fine, but what is the problem with this!? So, if we want to create person1, we need to copy the previous object, and so on.</p>
<p>What we can do here is to create a factory function. Factory function is the function that creates an object and returns it. It is like a constructor or class function,
but we will see that later. So, we are moving a step forward to OOP.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createPerson</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">name</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">hobby</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    name,</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    hobby,</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">    doing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">      return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> name </span><span style="color:#D73A49;--shiki-dark:#F97583">+</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ' is love to play '</span><span style="color:#D73A49;--shiki-dark:#F97583"> +</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">    },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> igor</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createPerson</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'Igor'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'basketball'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(igor.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">doing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">())</span></span></code></pre></figure>
<p>Here, we avoided repeating code, but still, we have a problem. In case we have thousands of persons, they require space in memory.
Things like name and hobby are different, and that’s okay, but methods of doing them are standard and the same thing.
That is going to copy on memory for each person. And this could be better.</p>
<p>We have something in JavaScript; how can we solve this problem? It is prototype inheritance, but now we will see how to solve it manually.</p>
<p>The easiest way to get a method outside of function and make the new object:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> doingFunc</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  doing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> name </span><span style="color:#D73A49;--shiki-dark:#F97583">+</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ' is love to play '</span><span style="color:#D73A49;--shiki-dark:#F97583"> +</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  },</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>So now, if want to create a new method inside our createPerson we just do:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">igor.doing </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> doingFunc.doing</span></span></code></pre></figure>
<p>Actually, here we can use <code>Object.create()</code> and simplify things. The <code>Object.create()</code> static method creates a new object,
using an existing object as the prototype of the newly created object.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> createPerson</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">name</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">hobby</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  let</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> newPerson </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> Object.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">create</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(doingFunc)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  newPerson.name </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> name</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  newPerson.hobby </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> weapon</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> newPerson</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>So, what does an object create do for us!? It links <code>doingFunc</code> and <code>newPerson</code>. We are doing prototype inheritance here.</p>
<p>If you try to log newPerson inside of createPerson like this console.log(newPerson) you will get the empty object, but not doing method from doingFunc.
You need to write <code>console.log(newPerson.__proto__)</code></p>
<p><code>Object.create()</code> solved our problems. We are using prototype inheritance; everything is working, and we are doing things.</p>
<p>There are not too many code bases with this approach; this is not the standard way of doing things inside the JavaScript community.</p>
<p>Things look closer to OOP, but we still need to fundamental OOP in JavaScript.</p>
<p>Now we will see something we had before <code>Object.create()</code>.</p>
<p>Before we didn't have <code>Object.create()</code>, we used constructor functions. Something like this:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">name</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">hobby</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">  this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.name </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> name</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">  this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>You can notice we don't return anything. We are using the keyword this, and that's it. We are constructing.
But if we try to create an object from this as we did previously, you will get an error, that prop. is undefined.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> igor</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'Igor'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'basketball'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre></figure>
<p>To use constructor function you need to use new keyword in JavaScript.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> igor</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'Igor'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'basketball'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre></figure>
<p>That is just a way JavaScript works :). The new keyword automatically returns the object, creating a Person constructor.
It runs our code and constructs the Person function.
So, we can say that any function invoked by new keyword is called a constructor function.
As a rule, all constructor functions should start with a capital letter to let other programmers know that the function should be called with new keyword.</p>
<p><strong>One note about this and new keyword:</strong> We know that this in JavaScript points to the global object, the window object. But new keyword changes it.
When you use new keyword, we are pointing this to the object we just created. Because of that, we got an error when we tried to create an object without a new keyword.</p>
<p>Every function in the JavaScript script automatically gets a prototype property. As we know, everything is an object in JS; functions are special type of objects.
<code>__proto__</code> is useful for constructor functions. So, we can simply do this to get the method attached to the function.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">prototype</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">doing</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'I like to '</span><span style="color:#D73A49;--shiki-dark:#F97583"> +</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>Also, one more note about arrow functions. If we try to write like this, we will get undefined:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">prototype</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">doing</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'I like to '</span><span style="color:#D73A49;--shiki-dark:#F97583"> +</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>This is because arrow functions are lexically scoped.</p>
<p><strong>One more important thing about prototyping in JS:</strong>
If we have the example below and try to log <code>console.log(igor.prototype)</code>, we will get undefined. Why!?
Because igor is not a function, it is an object, and only functions have access to the prototype.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">name</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">hobby</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">  this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.name </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> name</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">  this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">prototype</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">doing</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'I like to '</span><span style="color:#D73A49;--shiki-dark:#F97583"> +</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> igor</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'Igor'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'basketball'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">igor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">prototype</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre></figure>
<p>If we try to make our method more complex, like in this example, we need to take care about one thing. About this keyword.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">name</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">hobby</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">  this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.name </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> name</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">  this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">prototype</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">doing</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> something</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby </span><span style="color:#D73A49;--shiki-dark:#F97583">+</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ' is the best for me'</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  something</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> igor</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'Igor'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'basketball'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(igor.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">doing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">())</span></span></code></pre></figure>
<p>It will be undefined. Function inside methods, or just functions inside functions, means that this keyword inside function
is not assigned to object itself, but actually to window object. Solution is to bind function.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">prototype</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">doing</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> something</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby </span><span style="color:#D73A49;--shiki-dark:#F97583">+</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ' is the best for me'</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  something</span><span style="color:#24292E;--shiki-dark:#E1E4E8">().</span><span style="color:#6F42C1;--shiki-dark:#B392F0">bind</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#005CC5;--shiki-dark:#79B8FF">this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>Or :)</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">prototype</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">doing</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> function</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> self</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  function</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> something</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> self.hobby </span><span style="color:#D73A49;--shiki-dark:#F97583">+</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> ' is the best for me'</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> something</span><span style="color:#24292E;--shiki-dark:#E1E4E8">()</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>With this and new keyword coding style, the idea is much more object-oriented programming.
But things like <code>Object.create()</code> simplify things in JS, and they come out.
<code>Object.create()</code> is technically less OOP than we have with constructor functions.
This construction approach is not pretty and is sometimes hard to understand and follow.
We are moving to our goal, to add classes :)</p>
<p>Finally, we are here ES6 and classes. ECMAScript 2015 was the second major revision to JavaScript.
ECMAScript 2015 is also known as ES6 and ECMAScript 6. Here, we got let and const keywords, Promises and many more.
But we are talking about classes now :).</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">class</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  constructor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">name</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">hobby</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.name </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> name</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>Constructor is something that gets run every time when we use new keyword when creating new object.</p>
<p>Alo this line of code:</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#005CC5;--shiki-dark:#79B8FF">prototype</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">doing</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> () </span><span style="color:#D73A49;--shiki-dark:#F97583">=></span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  return</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'I like to '</span><span style="color:#D73A49;--shiki-dark:#F97583"> +</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>we are going to move to our class.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">class</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  constructor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">name</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">hobby</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.name </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> name</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  doing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'I like to '</span><span style="color:#D73A49;--shiki-dark:#F97583"> +</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>When we have any updates, we put things inside class. Class becomes some blueprint for what we want to do.
And when we update methods or change something inside a class, all instances will get updated. We are now using the word instance,
which is a common term for object-oriented programming. Instance happens when we call a class and create an object from it.</p>
<p>If you try to log this line of code, you will get <code>true</code>.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> igor</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'Igor'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'basketball'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(igor </span><span style="color:#D73A49;--shiki-dark:#F97583">instanceof</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span></code></pre></figure>
<p>But under the hood, JS is still using prototype inheritance. We are not using classes like classes work in other languages.
That is the closest that JavaScript comes to classes.</p>
<p>The idea of Brendan Eich (creator of JS :)) was to make JS different from Java and make a competing language with Java. He didn't have classes in JavaScript back in 1995 :).
In JavaScript, we have a principle that everything is an object. So, classes are just objects. In other languages, classes are an actual thing.</p>
<p>Now you can say this is similar to <code>Object.create()</code>. Yes, it is. But as we noted at the beginning of this article, JS is a multi-paradigm. It depends on your team,
personal preferences, etc. But most of the code bases that use OOP in JavaScript use this approach with <code>class</code>.</p>
<p>The core aspect of OOP is inheritance, which means passing things down :). So, we are going to see what it looks like in JavaScript.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">class</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  constructor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">name</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">hobby</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.name </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> name</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  doing</span><span style="color:#24292E;--shiki-dark:#E1E4E8">() {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">    return</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'I like to '</span><span style="color:#D73A49;--shiki-dark:#F97583"> +</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.hobby</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">class</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> EuropaPerson</span><span style="color:#D73A49;--shiki-dark:#F97583"> extends</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  constructor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">name</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">hobby</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#E36209;--shiki-dark:#FFAB70">type</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.type </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> type</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> igor</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> EuropaPerson</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'Igor'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'basketball'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'slavic'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(igor)</span></span></code></pre></figure>
<p>If try to log out new object we are going to get <code>ReferenceError</code> - must call super constructor in derived class before accessing <code>this</code>
or returning derived constructor. What this mean!?</p>
<p>We have special keyword called <code>super</code> for super class. It means we need to call super inside of constructor.
What does this mean, call super class inside of <code>EuropaPerson</code> class,
so super class is <code>Person</code>. <code>EuropaPerson</code> extends <code>Person</code>.
If you log it now, everything is working fine. Constructor is working with actual class, and we use <code>super()</code> to extend things.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">class</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> EuropaPerson</span><span style="color:#D73A49;--shiki-dark:#F97583"> extends</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Person</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">  constructor</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">type</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    super</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(name, hobby)</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.type </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> type</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">const</span><span style="color:#005CC5;--shiki-dark:#79B8FF"> igor</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#D73A49;--shiki-dark:#F97583"> new</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> EuropaPerson</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#032F62;--shiki-dark:#9ECBFF">'Igor'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'basketball'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">, </span><span style="color:#032F62;--shiki-dark:#9ECBFF">'slavic'</span><span style="color:#24292E;--shiki-dark:#E1E4E8">)</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">console.</span><span style="color:#6F42C1;--shiki-dark:#B392F0">log</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(igor)</span></span></code></pre></figure>
<p>Inheritance in JavaScript doesn't copy from an extended class; it simply links to the prototype chain. Unlike other languages, JavaScript is just an object
or everything inside JS is called an object. So it is an object inheriting from an object :). There are no technical classes. Other languages, for example, Java or C++,
copy objects when we extend classes. There is a bit of efficiency with memory in JS.</p>
<p>Finally new release in ES2022 brings us essential things inside of OOP: private methods and fields, and class fields declarations.
Previously, we used something like this to make things private inside of class.</p>
<figure class="code-block" data-lang="js"><pre class="shiki shiki-themes github-light github-dark" style="background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#D73A49;--shiki-dark:#F97583">class</span><span style="color:#6F42C1;--shiki-dark:#B392F0"> Employee</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> {</span></span>
<span class="line"><span style="color:#E36209;--shiki-dark:#FFAB70">  #name</span><span style="color:#D73A49;--shiki-dark:#F97583"> =</span><span style="color:#032F62;--shiki-dark:#9ECBFF"> 'Test'</span><span style="color:#6A737D;--shiki-dark:#6A737D"> // private field</span></span>
<span class="line"><span style="color:#6F42C1;--shiki-dark:#B392F0">  setName</span><span style="color:#24292E;--shiki-dark:#E1E4E8">(</span><span style="color:#E36209;--shiki-dark:#FFAB70">name</span><span style="color:#24292E;--shiki-dark:#E1E4E8">) {</span></span>
<span class="line"><span style="color:#005CC5;--shiki-dark:#79B8FF">    this</span><span style="color:#24292E;--shiki-dark:#E1E4E8">.#name </span><span style="color:#D73A49;--shiki-dark:#F97583">=</span><span style="color:#24292E;--shiki-dark:#E1E4E8"> name</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">  }</span></span>
<span class="line"><span style="color:#24292E;--shiki-dark:#E1E4E8">}</span></span></code></pre></figure>
<p>In this article, I mentioned inheritance several times. It is one of the few most important pillars of OOP. In object-oriented programming, we have
concepts of encapsulation, abstraction and polymorphism. Those are also important in OOP. Encapsulation can be defined as “the packing of data and functions into one component”.
Packing, which is also known as bundling, grouping and binding, simply means to bring together data and the methods which operate on data.
The component can be a function, a class or an object. Abstraction is a way of hiding the implementation details and showing only the functionality to the users. In other words,
it ignores the irrelevant details and shows only the required one. And finally Polymorphism is one of the core concepts of object-oriented programming languages where poly means
many and morphism means transforming one form into another. Polymorphism means the same function with different signatures is called many times.
I don't go deeper to those concepts, this article is just about main concepts of OOP in JavaScript and the development path how we got here.</p>
<p>In conclusion, despite those new concepts, JavaScript is not well-suited for most of the work we do on the server side, but on the other hand, it can attract backend developers
to do something on frontend :).</p>]]></content:encoded>
		</item>
	</channel>
</rss>
