<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yasin Demir</title>
    <description>The latest articles on DEV Community by Yasin Demir (@ysndmr).</description>
    <link>https://dev.to/ysndmr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4019087%2F87d9f628-a325-4ea7-afaa-8b5e61a40555.jpg</url>
      <title>DEV Community: Yasin Demir</title>
      <link>https://dev.to/ysndmr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ysndmr"/>
    <language>en</language>
    <item>
      <title>On building systems that survive team turnover</title>
      <dc:creator>Yasin Demir</dc:creator>
      <pubDate>Tue, 07 Jul 2026 12:14:39 +0000</pubDate>
      <link>https://dev.to/ysndmr/on-building-systems-that-survive-team-turnover-ebg</link>
      <guid>https://dev.to/ysndmr/on-building-systems-that-survive-team-turnover-ebg</guid>
      <description>&lt;p&gt;I have taken over codebases and had codebases taken from me. The experience in both directions has converged on one principle: a durable system communicates intent, not just implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What durable looks like
&lt;/h2&gt;

&lt;p&gt;Naming that explains purpose. Architecture that encodes constraints. Tests that document behavior, not implementation. The goal is a codebase where a newcomer can form a correct mental model in an afternoon.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hardest part
&lt;/h2&gt;

&lt;p&gt;Documentation rots. Comments lie. The only truthful documentation is the code itself — which is why naming and structure matter more than any README.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ysndmr.com/writings/systems-that-survive-turnover?utm_source=devto&amp;amp;utm_medium=cross-post&amp;amp;utm_campaign=writings" rel="noopener noreferrer"&gt;ysndmr.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>engineering</category>
      <category>process</category>
    </item>
    <item>
      <title>Why I stopped using design tokens as implementation details</title>
      <dc:creator>Yasin Demir</dc:creator>
      <pubDate>Tue, 07 Jul 2026 12:14:38 +0000</pubDate>
      <link>https://dev.to/ysndmr/why-i-stopped-using-design-tokens-as-implementation-details-1kca</link>
      <guid>https://dev.to/ysndmr/why-i-stopped-using-design-tokens-as-implementation-details-1kca</guid>
      <description>&lt;p&gt;Design tokens are not CSS variables. This distinction sounds semantic, but getting it wrong produces systems that serve neither designers nor developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mistake
&lt;/h2&gt;

&lt;p&gt;When teams generate tokens from Figma and map them 1:1 to CSS variables, they end up with &lt;code&gt;--color-blue-500&lt;/code&gt; everywhere. This is an implementation detail, not a token. A token is a decision: &lt;code&gt;--color-accent&lt;/code&gt;. The implementation of that decision can change — the token should not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The correct layer
&lt;/h2&gt;

&lt;p&gt;Tokens belong at the semantic layer. They answer the question "what is this for?" not "what is this value?". &lt;code&gt;--color-border-subtle&lt;/code&gt; is a token. &lt;code&gt;--gray-200&lt;/code&gt; is a primitive.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ysndmr.com/writings/design-tokens-not-implementation?utm_source=devto&amp;amp;utm_medium=cross-post&amp;amp;utm_campaign=writings" rel="noopener noreferrer"&gt;ysndmr.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>frontend</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Stop Putting Everything in useState</title>
      <dc:creator>Yasin Demir</dc:creator>
      <pubDate>Tue, 07 Jul 2026 11:00:29 +0000</pubDate>
      <link>https://dev.to/ysndmr/stop-putting-everything-in-usestate-4oeg</link>
      <guid>https://dev.to/ysndmr/stop-putting-everything-in-usestate-4oeg</guid>
      <description>&lt;p&gt;There's a pattern I see constantly: every piece of UI state gets its own &lt;code&gt;useState&lt;/code&gt;, the component re-renders on every interaction, someone adds &lt;code&gt;useMemo&lt;/code&gt; to fix the performance, and the whole thing gets progressively harder to follow.&lt;/p&gt;

&lt;p&gt;Most of that &lt;code&gt;useState&lt;/code&gt; wasn't necessary.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useState&lt;/code&gt; is for values React needs to track in order to render correctly. Not every value in a component qualifies.&lt;/p&gt;

&lt;p&gt;Three things that routinely end up in state when they shouldn't:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Derived values.&lt;/strong&gt; If you have &lt;code&gt;items&lt;/code&gt; in state and you're also storing &lt;code&gt;filteredItems&lt;/code&gt; in state, you have two sources of truth that can diverge. &lt;code&gt;filteredItems&lt;/code&gt; isn't state, it's a computation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// what I see&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setItems&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;filteredItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setFilteredItems&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

&lt;span class="c1"&gt;// what it should be&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setItems&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Values you only read in event handlers.&lt;/strong&gt; If a value never influences what gets rendered, it doesn't need to trigger a re-render. &lt;code&gt;useRef&lt;/code&gt; holds it without the overhead. A closure variable sometimes works too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every form field.&lt;/strong&gt; Controlled inputs are occasionally the right call. More often, you're adding a re-render on every keystroke for no benefit. Reading from &lt;code&gt;FormData&lt;/code&gt; on submit, or using an uncontrolled input with a ref, covers most cases without the noise.&lt;/p&gt;

&lt;p&gt;The check I use before reaching for &lt;code&gt;useState&lt;/code&gt;: if this value changes and nothing in the rendered output needs to change, it's probably not state. If it can be computed from something that is state, it definitely isn't.&lt;/p&gt;

&lt;p&gt;React re-renders fast, but not free. The easiest performance wins don't come from memoizing expensive renders — they come from not rendering when you don't need to.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ysndmr.com/writings/stop-putting-everything-in-usestate?utm_source=devto&amp;amp;utm_medium=cross-post&amp;amp;utm_campaign=writings" rel="noopener noreferrer"&gt;ysndmr.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Angular Signals: six months of production observations</title>
      <dc:creator>Yasin Demir</dc:creator>
      <pubDate>Tue, 07 Jul 2026 10:37:34 +0000</pubDate>
      <link>https://dev.to/ysndmr/angular-signals-six-months-of-production-observations-3eoc</link>
      <guid>https://dev.to/ysndmr/angular-signals-six-months-of-production-observations-3eoc</guid>
      <description>&lt;p&gt;Six months ago I started migrating the MKX Capital frontend from a BehaviorSubject-heavy architecture to Angular Signals. Here is what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Signals win
&lt;/h2&gt;

&lt;p&gt;Computed state is where Signals are genuinely superior to RxJS. With &lt;code&gt;computed()&lt;/code&gt;, derived state is lazy, memoized, and glitch-free. The mental model is simpler: a Signal is a value, not a stream.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where they fall short
&lt;/h2&gt;

&lt;p&gt;Time-based operations. Anything involving &lt;code&gt;debounceTime&lt;/code&gt;, &lt;code&gt;throttleTime&lt;/code&gt;, or complex multicasting still belongs in RxJS. The async pipe story for Signals is better with &lt;code&gt;toSignal()&lt;/code&gt;, but the interop layer adds cognitive overhead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule I settled on
&lt;/h2&gt;

&lt;p&gt;Signals for synchronous reactive state. RxJS for async streams (HTTP, WebSockets, timers). The two cooperate well via &lt;code&gt;toSignal()&lt;/code&gt; and &lt;code&gt;toObservable()&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ysndmr.com/writings/angular-signals-production?utm_source=devto&amp;amp;utm_medium=cross-post&amp;amp;utm_campaign=writings" rel="noopener noreferrer"&gt;ysndmr.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>signals</category>
      <category>rxjs</category>
      <category>frontend</category>
    </item>
    <item>
      <title>State colocation is not a preference, it is an architecture</title>
      <dc:creator>Yasin Demir</dc:creator>
      <pubDate>Tue, 07 Jul 2026 09:53:35 +0000</pubDate>
      <link>https://dev.to/ysndmr/state-colocation-is-not-a-preference-it-is-an-architecture-2p5k</link>
      <guid>https://dev.to/ysndmr/state-colocation-is-not-a-preference-it-is-an-architecture-2p5k</guid>
      <description>&lt;p&gt;The first question I ask when reviewing a frontend architecture is: where does the state live relative to where it is used?&lt;/p&gt;

&lt;p&gt;In most codebases I have reviewed, the answer is "in a global store, regardless of scope." This is the wrong default.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule
&lt;/h2&gt;

&lt;p&gt;State should live as close to its consumers as possible. If only one component needs it, it is component state. If a subtree needs it, it is a context or service scoped to that subtree. Global state is for truly global concerns: authentication, locale, theme.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://ysndmr.com/writings/state-colocation?utm_source=devto&amp;amp;utm_medium=cross-post&amp;amp;utm_campaign=writings" rel="noopener noreferrer"&gt;ysndmr.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>frontend</category>
      <category>javascript</category>
    </item>
    <item>
      <title>What ref() and reactive() Are Actually For</title>
      <dc:creator>Yasin Demir</dc:creator>
      <pubDate>Tue, 07 Jul 2026 09:39:53 +0000</pubDate>
      <link>https://dev.to/ysndmr/what-ref-and-reactive-are-actually-for-c52</link>
      <guid>https://dev.to/ysndmr/what-ref-and-reactive-are-actually-for-c52</guid>
      <description>&lt;p&gt;Vue 3 gives you two ways to make something reactive: &lt;code&gt;ref()&lt;/code&gt; and &lt;code&gt;reactive()&lt;/code&gt;. The docs explain the difference clearly enough. What they don't tell you is which one to reach for by default.&lt;/p&gt;

&lt;p&gt;The short answer: &lt;strong&gt;&lt;code&gt;ref()&lt;/code&gt;, almost always.&lt;/strong&gt; &lt;code&gt;reactive()&lt;/code&gt; for specific situations where you know what you're trading away.&lt;/p&gt;

&lt;p&gt;Here's the problem with &lt;code&gt;reactive()&lt;/code&gt;. It loses reactivity when you destructure it or reassign the root object, which catches people off guard more than it should.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;       &lt;span class="c1"&gt;// works, Vue tracks this&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;             &lt;span class="c1"&gt;// doesn't work, Vue has no idea&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ref()&lt;/code&gt; sidesteps this because the reactive pointer lives on the &lt;code&gt;.value&lt;/code&gt; property. You can pass a ref anywhere, destructure the container, and the ref itself stays tracked.&lt;/p&gt;

&lt;p&gt;The case where &lt;code&gt;reactive()&lt;/code&gt; actually earns its place: a form object where all the values are always read and written together, passed as-is to a composable, and you're never going to replace the whole reference. Some people find dropping the &lt;code&gt;.value&lt;/code&gt; noise worth that constraint. I get it. But in composables especially, &lt;code&gt;ref()&lt;/code&gt; is much easier to work with because you can return individual refs that stay reactive when the caller destructures them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;increment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;increment&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;increment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCounter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// both still reactive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can't do that cleanly with &lt;code&gt;reactive()&lt;/code&gt; — destructuring breaks the connection.&lt;/p&gt;

&lt;p&gt;For most of the time I was figuring out the ref/reactive split, I was making it more complicated than it needed to be. Default to &lt;code&gt;ref()&lt;/code&gt;. Reach for &lt;code&gt;reactive()&lt;/code&gt; when you have a specific reason. That's the whole decision.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Watchers Are Usually the Wrong Answer</title>
      <dc:creator>Yasin Demir</dc:creator>
      <pubDate>Tue, 07 Jul 2026 09:39:46 +0000</pubDate>
      <link>https://dev.to/ysndmr/watchers-are-usually-the-wrong-answer-15an</link>
      <guid>https://dev.to/ysndmr/watchers-are-usually-the-wrong-answer-15an</guid>
      <description>&lt;p&gt;When I started with Vue's Composition API I used &lt;code&gt;watch()&lt;/code&gt; constantly. State changes, something needs to happen — that's what watchers are for. Right?&lt;/p&gt;

&lt;p&gt;Most of the time, no.&lt;/p&gt;

&lt;p&gt;Watchers are for side effects: persisting to &lt;code&gt;localStorage&lt;/code&gt;, calling an API when something changes, syncing with a library that Vue doesn't control. They're not for computing derived values, and they're not for keeping reactive state in sync with other reactive state.&lt;/p&gt;

&lt;p&gt;The most common misuse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;watch&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;last&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a computed property written the hard way. It runs asynchronously by default, it's harder to read, and it can produce frames where &lt;code&gt;fullName&lt;/code&gt; is stale before the watcher fires.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always in sync. No timing edge cases. One line.&lt;/p&gt;

&lt;p&gt;The other pattern that causes problems: chaining watchers. Watch A to update B, watch B to update C. I've done this. Every time I've eventually produced a cycle or a timing issue that was annoying to debug. If values derive from each other, &lt;code&gt;computed&lt;/code&gt; handles it.&lt;/p&gt;

&lt;p&gt;Where &lt;code&gt;watch()&lt;/code&gt; actually belongs: real side effects. &lt;code&gt;userId&lt;/code&gt; changes and you need to fetch new data, that's a watcher. The route changes and you need to update a third-party map instance, watcher. You need to debounce an API call on search input, watcher with a cleanup function.&lt;/p&gt;

&lt;p&gt;The question I ask before adding a &lt;code&gt;watch&lt;/code&gt;: is this a side effect, or is this a value that depends on other values? If it's the second thing, it's a &lt;code&gt;computed&lt;/code&gt;. I've removed a lot of watchers by asking that.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Why I Built ngx-local-vault: Signals, Encryption, and TTL for Angular Storage</title>
      <dc:creator>Yasin Demir</dc:creator>
      <pubDate>Tue, 07 Jul 2026 08:42:14 +0000</pubDate>
      <link>https://dev.to/ysndmr/why-i-built-ngx-local-vault-bringing-signals-encryption-and-ttl-to-angular-storage-3jd6</link>
      <guid>https://dev.to/ysndmr/why-i-built-ngx-local-vault-bringing-signals-encryption-and-ttl-to-angular-storage-3jd6</guid>
      <description>&lt;p&gt;Every app needs to persist something in the browser: user preferences, a sidebar toggle, a short-lived session token. &lt;code&gt;localStorage&lt;/code&gt; and &lt;code&gt;sessionStorage&lt;/code&gt; are the default tools for that.&lt;/p&gt;

&lt;p&gt;But using the raw Web Storage API in Angular still feels like 2015. You end up writing the same boilerplate every time.&lt;/p&gt;

&lt;p&gt;Most storage wrappers give you a getter and a setter and call it done. Reactivity, encryption, expiration — you're on your own for all three.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;ngx-local-vault&lt;/strong&gt;: persistence, encryption, and TTL, wired into a native Angular &lt;code&gt;WritableSignal&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Actually Wrong with Browser Storage
&lt;/h2&gt;

&lt;p&gt;If you've built anything non-trivial in Angular, at least one of these has bitten you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No reactivity.&lt;/strong&gt; &lt;code&gt;localStorage.setItem()&lt;/code&gt; doesn't trigger change detection. You end up writing your own events, or an RxJS Subject, just so a component notices the value changed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plain text.&lt;/strong&gt; JWTs and profile data sitting in &lt;code&gt;localStorage&lt;/code&gt; in plain text are one DevTools tab (or one XSS script) away from being read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No expiry.&lt;/strong&gt; &lt;code&gt;localStorage&lt;/code&gt; just sits there forever. Want a token to die after 15 minutes? Store a timestamp yourself, check it on load, delete it yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSR headaches.&lt;/strong&gt; Touch &lt;code&gt;window.localStorage&lt;/code&gt; during server-side rendering and you get &lt;code&gt;ReferenceError: window is not defined&lt;/code&gt;. Wrapping every access in &lt;code&gt;isPlatformBrowser&lt;/code&gt; gets old fast.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ngx-local-vault
&lt;/h2&gt;

&lt;p&gt;It's a small library — under 2KB gzipped, no dependencies beyond Angular itself — that treats browser storage as something a component can just read reactively.&lt;/p&gt;

&lt;p&gt;Instead of separate read and write calls, there's a &lt;code&gt;watchSignal()&lt;/code&gt; method that hands you back a real Angular Signal. Read it, &lt;code&gt;.set()&lt;/code&gt; it, &lt;code&gt;.update()&lt;/code&gt; it — encryption and TTL happen underneath without you thinking about them.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setup
&lt;/h3&gt;

&lt;p&gt;Add it to &lt;code&gt;app.config.ts&lt;/code&gt;. Works with Angular 17 through 20.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideVault&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ngx-local-vault&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideVault&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;encryptionKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;your-secure-key&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;local&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Using it
&lt;/h3&gt;

&lt;p&gt;Inject &lt;code&gt;VaultService&lt;/code&gt; and watch a key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VaultService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ngx-local-vault&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standalone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;vault&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;VaultService&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vault&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;watchSignal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vault&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;watchSignal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;session-token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;15m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;toggleTheme&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What it does differently
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real signals.&lt;/strong&gt; &lt;code&gt;watchSignal()&lt;/code&gt; returns an actual &lt;code&gt;WritableSignal&amp;lt;T&amp;gt;&lt;/code&gt;, not some wrapper you have to learn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encrypted at rest.&lt;/strong&gt; Open the Application tab in DevTools and you won't find raw JSON or a bare token sitting there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTL that actually deletes itself.&lt;/strong&gt; Pass &lt;code&gt;expiresIn: '15m'&lt;/code&gt; and the entry disappears in-tab when the clock runs out — no reload needed. It takes plain strings: &lt;code&gt;500ms&lt;/code&gt;, &lt;code&gt;30s&lt;/code&gt;, &lt;code&gt;15m&lt;/code&gt;, &lt;code&gt;2h&lt;/code&gt;, &lt;code&gt;1d&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSR-safe.&lt;/strong&gt; Guarded by &lt;code&gt;PLATFORM_ID&lt;/code&gt;, so on the server it's just a no-op. No hydration mismatches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actually zero dependencies.&lt;/strong&gt; Only &lt;code&gt;@angular/core&lt;/code&gt; and &lt;code&gt;@angular/common&lt;/code&gt; as peers. Even &lt;code&gt;tslib&lt;/code&gt; gets stripped at build time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Live demo: &lt;a href="https://ysndmr.github.io/ngx-local-vault" rel="noopener noreferrer"&gt;ysndmr.github.io/ngx-local-vault&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;NPM: &lt;a href="https://npmjs.com/package/ngx-local-vault" rel="noopener noreferrer"&gt;npmjs.com/package/ngx-local-vault&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/ysndmr/ngx-local-vault" rel="noopener noreferrer"&gt;github.com/ysndmr/ngx-local-vault&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;My site: &lt;a href="https://ysndmr.com" rel="noopener noreferrer"&gt;ysndmr.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Original post: &lt;a href="https://dev.to/ysndmr/why-i-built-ngx-local-vault-bringing-signals-encryption-and-ttl-to-angular-storage-3jd6"&gt;dev.to&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're not on Angular, there are React and Vue versions too — links are in the GitHub README.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;Signals changed how Angular handles state. I think storage should have followed the same shift a while ago.&lt;/p&gt;

&lt;p&gt;How are you handling browser storage in your Angular apps right now? Open an issue or drop a star on GitHub if this is useful to you.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>signals</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
