<?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: Odiri Teddie</title>
    <description>The latest articles on DEV Community by Odiri Teddie (@kingteddie01).</description>
    <link>https://dev.to/kingteddie01</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%2F210295%2F834dfecc-cfbe-4d2c-9c4f-1a2139aeba04.jpg</url>
      <title>DEV Community: Odiri Teddie</title>
      <link>https://dev.to/kingteddie01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kingteddie01"/>
    <language>en</language>
    <item>
      <title>Service Workers Explained Like You're Building Your First PWA</title>
      <dc:creator>Odiri Teddie</dc:creator>
      <pubDate>Mon, 20 Jul 2026 14:43:56 +0000</pubDate>
      <link>https://dev.to/kingteddie01/service-workers-explained-like-youre-building-your-first-pwa-24hd</link>
      <guid>https://dev.to/kingteddie01/service-workers-explained-like-youre-building-your-first-pwa-24hd</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frgxvb1wi4r88dv7w6e1e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frgxvb1wi4r88dv7w6e1e.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
Spend any real time poking around Progressive Web Apps (PWAs), and sooner or later you’ll run headfirst into &lt;strong&gt;Service Workers&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The line you’ll hear over and over is that they’re &lt;em&gt;"a proxy between your app and the network."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Which is true, and also close to useless if what you’re actually after is why they exist and what they buy you.&lt;/p&gt;

&lt;p&gt;Here’s the framing that finally made it stick for me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A Service Worker is a programmable layer sitting between your app and the internet, handing you the controls over how every request gets dealt with.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s the thing that unlocks offline support, smarter caching, background sync, push notifications — the stuff that starts closing the gap between a web app and a native one.&lt;/p&gt;

&lt;p&gt;So in this piece I want to build up a mental model of Service Workers, show where they sit inside a PWA, and make the case for why they’re worth your attention even if you never plan to go fully offline.&lt;/p&gt;


&lt;h1&gt;
  
  
  What Problem Do Service Workers Solve?
&lt;/h1&gt;

&lt;p&gt;Say you’ve thrown together a little weather app.&lt;/p&gt;

&lt;p&gt;Each time someone opens it, the browser pulls down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Fonts&lt;/li&gt;
&lt;li&gt;API responses&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now knock out their connection, and the whole thing just falls over.&lt;/p&gt;

&lt;p&gt;Not because the browser can’t paint the UI — it’s because it has no idea what it’s meant to do the moment a request comes back empty.&lt;/p&gt;

&lt;p&gt;With no Service Worker in the picture, every request walks the same simple line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
     ↓
 Internet
     ↓
 Server
     ↓
 Browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the browser gets almost no say in any of it.&lt;/p&gt;

&lt;p&gt;A Service Worker changes the flow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Browser
     ↓
Service Worker
     ↓
Cache or Network
     ↓
Browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your application can decide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should this request come from the cache?&lt;/li&gt;
&lt;li&gt;Should it go to the network?&lt;/li&gt;
&lt;li&gt;If they’re offline, do I fall back to something cached?&lt;/li&gt;
&lt;li&gt;Is this response worth holding onto for next time?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The browser’s defaults stop being the only option, and the decisions become yours.&lt;/p&gt;




&lt;h1&gt;
  
  
  What Exactly Is a Service Worker?
&lt;/h1&gt;

&lt;p&gt;At its core, a Service Worker is just a JavaScript file — but one that runs off to the side of your actual page.&lt;/p&gt;

&lt;p&gt;It never touches the DOM, which already sets it apart from your React or Vue code.&lt;/p&gt;

&lt;p&gt;Picture it more as a background process, sitting quietly and listening for browser events and network traffic.&lt;/p&gt;

&lt;p&gt;Its responsibilities often include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caching application assets&lt;/li&gt;
&lt;li&gt;Intercepting network requests&lt;/li&gt;
&lt;li&gt;Serving cached responses&lt;/li&gt;
&lt;li&gt;Managing offline experiences&lt;/li&gt;
&lt;li&gt;Receiving push notifications&lt;/li&gt;
&lt;li&gt;Performing background synchronization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And because it lives apart from your UI, it can keep doing some of its work even when nobody has your app open.&lt;/p&gt;




&lt;h1&gt;
  
  
  The Service Worker Lifecycle
&lt;/h1&gt;

&lt;p&gt;Once the lifecycle clicks, a lot of the "why does it behave like that?" weirdness around Service Workers goes away.&lt;/p&gt;

&lt;p&gt;It moves through three stages.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Registration
&lt;/h2&gt;

&lt;p&gt;This is your app letting the browser know a Service Worker is around.&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;serviceWorker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/service-worker.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing special happens yet.&lt;/p&gt;

&lt;p&gt;The browser simply knows about the worker.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Installation
&lt;/h2&gt;

&lt;p&gt;Now the worker downloads itself and installs.&lt;/p&gt;

&lt;p&gt;This is typically the moment you cache your static assets.&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="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;install&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Installing...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of it as the app packing a bag for later.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Activation
&lt;/h2&gt;

&lt;p&gt;With install done, the worker flips over to active.&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="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;activate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Activated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is your chance to clear out stale caches from previous versions.&lt;/p&gt;

&lt;p&gt;And from the moment it’s active, it starts catching requests.&lt;/p&gt;




&lt;h1&gt;
  
  
  Intercepting Network Requests
&lt;/h1&gt;

&lt;p&gt;This is the part where Service Workers actually earn their reputation.&lt;/p&gt;

&lt;p&gt;Every single network request can be caught mid-flight.&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="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fetch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rather than letting the browser fetch things the usual way, you get to make the call on each request yourself.&lt;/p&gt;

&lt;p&gt;That freedom is exactly what the more advanced caching strategies are built on.&lt;/p&gt;




&lt;h1&gt;
  
  
  Caching Isn't Just About Speed
&lt;/h1&gt;

&lt;p&gt;Many developers associate caching with performance.&lt;/p&gt;

&lt;p&gt;That’s true, but it’s only half of it.&lt;/p&gt;

&lt;p&gt;Caching also improves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reliability&lt;/li&gt;
&lt;li&gt;Offline support&lt;/li&gt;
&lt;li&gt;Reduced bandwidth usage&lt;/li&gt;
&lt;li&gt;Faster repeat visits&lt;/li&gt;
&lt;li&gt;Better user experience on slow networks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine a news application.&lt;/p&gt;

&lt;p&gt;The newest stories might still need a connection, but the ones a reader already opened can sit there ready offline.&lt;/p&gt;

&lt;p&gt;Which beats staring at the browser’s dinosaur error page by a wide margin.&lt;/p&gt;




&lt;h1&gt;
  
  
  Common Caching Strategies
&lt;/h1&gt;

&lt;p&gt;Different resources benefit from different approaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cache First
&lt;/h2&gt;

&lt;p&gt;Check the cache before the network.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache
   ↓
Network (if needed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Best for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Fonts&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;JavaScript bundles&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Network First
&lt;/h2&gt;

&lt;p&gt;Try the network first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Network
    ↓
Cache (fallback)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API requests&lt;/li&gt;
&lt;li&gt;Dashboards&lt;/li&gt;
&lt;li&gt;Live content&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Stale While Revalidate
&lt;/h2&gt;

&lt;p&gt;Immediately serve cached content while downloading a fresh version in the background.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cache
 ↓
User sees content immediately

Network
 ↓
Cache updated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This strategy creates applications that feel extremely responsive.&lt;/p&gt;

&lt;p&gt;It's widely used because it balances speed with freshness.&lt;/p&gt;




&lt;h1&gt;
  
  
  How PWAs Use Service Workers
&lt;/h1&gt;

&lt;p&gt;A Progressive Web App isn't defined by one feature.&lt;/p&gt;

&lt;p&gt;Instead, several technologies work together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Manifest
        +
Service Worker
        +
HTTPS
        +
Responsive Design
        =
Progressive Web App
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The manifest controls installation.&lt;/p&gt;

&lt;p&gt;Responsive design keeps it usable from a phone to a widescreen monitor.&lt;/p&gt;

&lt;p&gt;HTTPS provides security.&lt;/p&gt;

&lt;p&gt;And the Service Worker is what brings the offline behaviour, the caching, and the background tricks to the table.&lt;/p&gt;

&lt;p&gt;Pull the Service Worker out and most of what makes a PWA feel like a PWA goes with it.&lt;/p&gt;




&lt;h1&gt;
  
  
  Things Service Workers Can't Do
&lt;/h1&gt;

&lt;p&gt;For all they can do, Service Workers aren’t a magic wand.&lt;/p&gt;

&lt;p&gt;They cannot:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Access the DOM directly&lt;/li&gt;
&lt;li&gt;Modify React components&lt;/li&gt;
&lt;li&gt;Read page variables automatically&lt;/li&gt;
&lt;li&gt;Run on insecure HTTP connections (except localhost)&lt;/li&gt;
&lt;li&gt;Replace your backend&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They sit alongside your app and support it; they were never meant to stand in for its architecture.&lt;/p&gt;




&lt;h1&gt;
  
  
  Common Mistakes
&lt;/h1&gt;

&lt;p&gt;Almost everyone trips over the same few things the first time they play with Service Workers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching Everything
&lt;/h3&gt;

&lt;p&gt;Not every response belongs in the cache.&lt;/p&gt;

&lt;p&gt;Dynamic data often needs different strategies than static assets.&lt;/p&gt;




&lt;h3&gt;
  
  
  Forgetting Cache Cleanup
&lt;/h3&gt;

&lt;p&gt;Old cache versions accumulate over time.&lt;/p&gt;

&lt;p&gt;Always remove outdated caches during activation.&lt;/p&gt;




&lt;h3&gt;
  
  
  Assuming Everything Works Offline
&lt;/h3&gt;

&lt;p&gt;Offline support requires planning.&lt;/p&gt;

&lt;p&gt;It’s on you to draw the line between what’s safe to cache and what genuinely needs a live connection.&lt;/p&gt;




&lt;h3&gt;
  
  
  Ignoring Updates
&lt;/h3&gt;

&lt;p&gt;Shipping a new deploy doesn’t just swap out the Service Worker that’s already running.&lt;/p&gt;

&lt;p&gt;If you don’t get your head around the update lifecycle, you’ll end up with users quietly stuck on an old build.&lt;/p&gt;




&lt;h1&gt;
  
  
  Should Every Website Use a Service Worker?
&lt;/h1&gt;

&lt;p&gt;Not necessarily.&lt;/p&gt;

&lt;p&gt;A simple marketing website may not benefit much.&lt;/p&gt;

&lt;p&gt;The apps that tend to get the most out of them include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SaaS dashboards&lt;/li&gt;
&lt;li&gt;E-commerce platforms&lt;/li&gt;
&lt;li&gt;Productivity tools&lt;/li&gt;
&lt;li&gt;Travel applications&lt;/li&gt;
&lt;li&gt;Educational platforms&lt;/li&gt;
&lt;li&gt;News applications&lt;/li&gt;
&lt;li&gt;Field service applications&lt;/li&gt;
&lt;li&gt;Offline-first business tools&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The more your users are counting on the thing being fast and dependable, the more a Service Worker pays for itself.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Service Workers feel daunting early on, mostly because they drop you into a new execution context and ask you to rethink how requests work.&lt;/p&gt;

&lt;p&gt;But the moment the underlying model lands, the rest tends to fall into place quickly.&lt;/p&gt;

&lt;p&gt;Stop picturing each request as something the browser just fires off to a server on autopilot. Picture a gatekeeper instead — every request stops at it first, and that’s where your app gets to choose: go fetch something fresh, hand back a cached copy, or handle a flaky connection without falling apart.&lt;/p&gt;

&lt;p&gt;That one shift in how you see requests is really what the whole idea of a PWA rests on.&lt;/p&gt;

&lt;p&gt;Small weekend project or a full production app, it doesn’t much matter — getting comfortable with Service Workers is one of those steps that quietly makes everything you ship faster, sturdier, and nicer to actually use.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>pwa</category>
      <category>webperf</category>
    </item>
    <item>
      <title>10 Internationalization Mistakes Frontend Engineers Keep Making</title>
      <dc:creator>Odiri Teddie</dc:creator>
      <pubDate>Mon, 13 Jul 2026 14:01:27 +0000</pubDate>
      <link>https://dev.to/kingteddie01/10-internationalization-mistakes-frontend-engineers-keep-making-3h63</link>
      <guid>https://dev.to/kingteddie01/10-internationalization-mistakes-frontend-engineers-keep-making-3h63</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc2sgmqwkxi8luczg1kbx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc2sgmqwkxi8luczg1kbx.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
Say &lt;em&gt;internationalization&lt;/em&gt; (or &lt;strong&gt;i18n&lt;/strong&gt;, if you’ve typed it enough times) to most people and their mind jumps straight to one thing: translating the text.&lt;/p&gt;

&lt;p&gt;That’s part of it, sure. It’s nowhere near the whole thing.&lt;/p&gt;

&lt;p&gt;Internationalisation is really about building your app so it can bend to new languages, regions, and cultural expectations later without you having to tear the architecture apart to get there. And in practice that reaches into far more than you’d expect — routing, layouts, how you format dates and currencies, accessibility, performance, right down to the shape of your backend APIs.&lt;/p&gt;

&lt;p&gt;I’ve watched "let’s add a second language" land on a board as a small ticket, then quietly detonate once someone actually started on it. Turns out the codebase was full of assumptions nobody had written down. Buttons ran out of room, layouts buckled, dates came out wrong, the currency left people second-guessing what they were paying, and the whole navigation fell apart the moment it hit a right-to-left language.&lt;/p&gt;

&lt;p&gt;Here’s the reassuring part: nearly all of this is avoidable once you know where to look.&lt;/p&gt;

&lt;p&gt;So here are ten of the internationalization mistakes I see frontend engineers make again and again, and how to stay clear of each one.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Treating Internationalization as "Just Translation"
&lt;/h2&gt;

&lt;p&gt;This one is the misconception the whole topic suffers from.&lt;/p&gt;

&lt;p&gt;Translation is a single slice of internationalization, not the pie.&lt;/p&gt;

&lt;p&gt;An app that’s genuinely internationalized has to account for a lot more:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Locale-specific date and time formatting&lt;/li&gt;
&lt;li&gt;Currency and number formatting&lt;/li&gt;
&lt;li&gt;Text direction (LTR and RTL)&lt;/li&gt;
&lt;li&gt;Address formats&lt;/li&gt;
&lt;li&gt;Phone numbers&lt;/li&gt;
&lt;li&gt;Units of measurement&lt;/li&gt;
&lt;li&gt;Local conventions and cultural expectations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, replacing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;welcome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;is a fine first step, but it barely scratches what building for a global audience actually asks of you.&lt;/p&gt;

&lt;p&gt;Treat internationalization as an architectural decision, not a job you hand off to translators at the end.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Hardcoding User-Facing Strings
&lt;/h2&gt;

&lt;p&gt;Few things make localization more painful than sprinkling hardcoded text across all your components.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Checkout&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;store the text in your translation files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;checkout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The benefits are immediate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier maintenance&lt;/li&gt;
&lt;li&gt;Cleaner components&lt;/li&gt;
&lt;li&gt;Consistent terminology&lt;/li&gt;
&lt;li&gt;Simpler translation workflows&lt;/li&gt;
&lt;li&gt;Room to scale as the app keeps growing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The moment a translator has to go spelunking through your source to find the strings, you know localization was never really part of the plan.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Concatenating Strings Together
&lt;/h2&gt;

&lt;p&gt;Gluing strings together happens to work in English because English word order is fairly predictable.&lt;/p&gt;

&lt;p&gt;Plenty of other languages aren’t so obliging.&lt;/p&gt;

&lt;p&gt;Consider this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks innocent enough, until you remember that not every language drops the name in that same spot.&lt;/p&gt;

&lt;p&gt;A better approach is using placeholders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight liquid"&gt;&lt;code&gt;Hello &lt;span class="cp"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="cp"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or ICU MessageFormat:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello {name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives translators complete control over sentence structure while keeping your code flexible.&lt;/p&gt;

&lt;p&gt;The rule of thumb I’d hand anyone: translate whole sentences, never fragments.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Designing Layouts That Only Work in English
&lt;/h2&gt;

&lt;p&gt;English is relatively compact.&lt;/p&gt;

&lt;p&gt;Many other languages aren't.&lt;/p&gt;

&lt;p&gt;A button that comfortably displays:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Settings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;may need to display something much longer in another language.&lt;/p&gt;

&lt;p&gt;Likewise:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Account
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;could become:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Paramètres du compte
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or another significantly longer phrase depending on the locale.&lt;/p&gt;

&lt;p&gt;Lean on fixed widths, hardcoded spacing, or layouts squeezed too tight, and translated text will drag every one of those assumptions into the light.&lt;/p&gt;

&lt;p&gt;Some good practices include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using flexible layouts&lt;/li&gt;
&lt;li&gt;Avoiding fixed-width buttons where possible&lt;/li&gt;
&lt;li&gt;Allowing text to wrap gracefully&lt;/li&gt;
&lt;li&gt;Testing with longer translations during development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One of the simplest ways to uncover layout issues is to switch your application to a language known for longer words, such as German.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Forgetting About Right-to-Left Languages
&lt;/h2&gt;

&lt;p&gt;Supporting Arabic or Hebrew involves much more than translating text.&lt;/p&gt;

&lt;p&gt;Entire layouts often need to mirror.&lt;/p&gt;

&lt;p&gt;Common issues include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Navigation alignment&lt;/li&gt;
&lt;li&gt;Icons pointing the wrong direction&lt;/li&gt;
&lt;li&gt;Incorrect spacing&lt;/li&gt;
&lt;li&gt;Misaligned forms&lt;/li&gt;
&lt;li&gt;Broken flex layouts&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;margin-left&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;16&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;prefer logical properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;margin-inline-start&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="err"&gt;16&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Likewise:&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;padding-right&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;padding-inline-end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Logical CSS properties flip themselves based on the text direction, which makes your layouts hold together in places physical properties would’ve broken.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Formatting Dates, Times, and Numbers Manually
&lt;/h2&gt;

&lt;p&gt;Hardcoding a date format is a trap far more people fall into than you’d think.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;07/03/2026
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;July 3rd?&lt;/li&gt;
&lt;li&gt;March 7th?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The honest answer is that it depends entirely on where the person reading it lives.&lt;/p&gt;

&lt;p&gt;Instead of manually formatting values, use the browser's internationalization APIs:&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;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same applies to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time&lt;/li&gt;
&lt;li&gt;Percentages&lt;/li&gt;
&lt;li&gt;Decimal separators&lt;/li&gt;
&lt;li&gt;Thousands separators&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hand the locale-specific formatting to the platform rather than rebuilding it badly yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Assuming Every User Uses the Same Currency
&lt;/h2&gt;

&lt;p&gt;Even applications written entirely in English may serve users from different countries.&lt;/p&gt;

&lt;p&gt;Consider these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;United Kingdom → GBP (£)&lt;/li&gt;
&lt;li&gt;United States → USD ($)&lt;/li&gt;
&lt;li&gt;Canada → CAD ($)&lt;/li&gt;
&lt;li&gt;Australia → AUD ($)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A dollar sign on its own tells you almost nothing about which currency you’re actually looking at.&lt;/p&gt;

&lt;p&gt;Instead of manually formatting values:&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$25&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;use:&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;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NumberFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;currency&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GBP&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the price shows up the way someone in that region would actually expect to read it.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Ignoring Pluralization Rules
&lt;/h2&gt;

&lt;p&gt;Pluralization is more complicated than adding an "s".&lt;/p&gt;

&lt;p&gt;English has relatively simple rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 item&lt;/li&gt;
&lt;li&gt;2 items&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Many languages have several plural forms.&lt;/p&gt;

&lt;p&gt;Assume every language plays by English’s rules and, sooner or later, your translations start coming out grammatically wrong.&lt;/p&gt;

&lt;p&gt;Most internationalization libraries support pluralization out of the box, often through ICU MessageFormat or similar syntax.&lt;/p&gt;

&lt;p&gt;Take advantage of these features rather than trying to build plural logic yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Shipping Every Translation to Every User
&lt;/h2&gt;

&lt;p&gt;As your application supports more languages, translation files can become surprisingly large.&lt;/p&gt;

&lt;p&gt;Loading every language during the initial page load wastes bandwidth and increases bundle size.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Lazy-load translation files&lt;/li&gt;
&lt;li&gt;Split translations into namespaces&lt;/li&gt;
&lt;li&gt;Load only the active locale&lt;/li&gt;
&lt;li&gt;Consider route-level translation loading&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Going international shouldn’t come at the cost of how fast your app feels.&lt;/p&gt;

&lt;p&gt;Nobody should be downloading resources they’re never going to use.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Waiting Until the End of the Project
&lt;/h2&gt;

&lt;p&gt;Of everything on this list, this is the one that costs the most to fix.&lt;/p&gt;

&lt;p&gt;A team builds the whole product around one language, ships it, and then someone in a meeting says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We need to support three more countries."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Suddenly everything needs revisiting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Routing&lt;/li&gt;
&lt;li&gt;URLs&lt;/li&gt;
&lt;li&gt;Forms&lt;/li&gt;
&lt;li&gt;Validation messages&lt;/li&gt;
&lt;li&gt;API responses&lt;/li&gt;
&lt;li&gt;SEO&lt;/li&gt;
&lt;li&gt;Metadata&lt;/li&gt;
&lt;li&gt;Content management&lt;/li&gt;
&lt;li&gt;Layouts&lt;/li&gt;
&lt;li&gt;Design system components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bolting internationalization on after the fact is nearly always harder than having designed for it on day one.&lt;/p&gt;

&lt;p&gt;Even if today it’s one language and one market, building with i18n in the back of your mind buys you room to grow later without a painful rewrite.&lt;/p&gt;




&lt;h1&gt;
  
  
  A Quick Internationalization Checklist
&lt;/h1&gt;

&lt;p&gt;Before shipping a feature, ask yourself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are all user-facing strings externalized?&lt;/li&gt;
&lt;li&gt;Are dates locale-aware?&lt;/li&gt;
&lt;li&gt;Are numbers locale-aware?&lt;/li&gt;
&lt;li&gt;Are currencies formatted correctly?&lt;/li&gt;
&lt;li&gt;Does the layout handle longer translations?&lt;/li&gt;
&lt;li&gt;Have you tested right-to-left layouts?&lt;/li&gt;
&lt;li&gt;Are pluralization rules handled correctly?&lt;/li&gt;
&lt;li&gt;Are translation files loaded efficiently?&lt;/li&gt;
&lt;li&gt;Can your routing support multiple locales?&lt;/li&gt;
&lt;li&gt;Would adding another language require changing application logic?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you can say "yes" to those without wincing, you’re already in better shape than a lot of shipped projects.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Thoughts
&lt;/h1&gt;

&lt;p&gt;Internationalization was never a "we’ll get to it" item. It’s a design principle, and it shapes how the whole thing gets built from the first commit.&lt;/p&gt;

&lt;p&gt;The apps that get this right don’t just swap in translated strings — they feel native to whoever’s using them, wherever they are and whatever they speak. Getting there means treating layouts, formatting, accessibility, performance, routing, and plain user expectations as first-class parts of the build, not afterthoughts bolted to the side of the translation work.&lt;/p&gt;

&lt;p&gt;None of this requires launching in ten languages to pay off. Even a single-market app is easier to live with when it’s built this way. And the day expansion into a new region does land on your desk, past-you will have quietly done present-you an enormous favour.&lt;/p&gt;

&lt;p&gt;What’s the i18n gotcha that blindsided you at some point? Drop it in the comments — I’d genuinely like to hear it.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>frontend</category>
      <category>i18n</category>
    </item>
    <item>
      <title>How to Choose Between Cache First, Network First, and Stale-While-Revalidate</title>
      <dc:creator>Odiri Teddie</dc:creator>
      <pubDate>Sun, 05 Jul 2026 21:57:23 +0000</pubDate>
      <link>https://dev.to/kingteddie01/how-to-choose-between-cache-first-network-first-and-stale-while-revalidate-4h9l</link>
      <guid>https://dev.to/kingteddie01/how-to-choose-between-cache-first-network-first-and-stale-while-revalidate-4h9l</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsj9puf2zfvnltgdzrx8h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsj9puf2zfvnltgdzrx8h.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Caching looks simple enough on paper. Then you start building something real, and the questions pile up fast.&lt;/p&gt;

&lt;p&gt;Should your product images come from the cache?&lt;/p&gt;

&lt;p&gt;Should your shopping cart always hit the server?&lt;/p&gt;

&lt;p&gt;Is it fine to serve data that’s a little stale if it makes the whole thing feel noticeably snappier?&lt;/p&gt;

&lt;p&gt;Sooner or later, most web apps lean on one of three caching strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cache First&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network First&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stale-While-Revalidate (SWR)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Learning what each one does takes an afternoon.&lt;/p&gt;

&lt;p&gt;Working out &lt;strong&gt;when to reach for which&lt;/strong&gt; is where the actual engineering starts.&lt;/p&gt;

&lt;p&gt;So in this piece I want to walk through the trade-offs each one makes, and leave you with a mental model you can actually apply to your own apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Mental Model
&lt;/h2&gt;

&lt;p&gt;Forget implementation for a moment.&lt;/p&gt;

&lt;p&gt;Underneath, all three are wrestling with the exact same question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;When a request arrives, where should I look first?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Should the application check the cache?&lt;/p&gt;

&lt;p&gt;Or skip that and go straight to the network?&lt;/p&gt;

&lt;p&gt;Or hand back what’s cached right away and quietly go fetch a fresher copy in the background?&lt;/p&gt;

&lt;p&gt;Get that question straight in your head and the rest of it stops feeling so slippery.&lt;/p&gt;

&lt;h1&gt;
  
  
  Cache First
&lt;/h1&gt;

&lt;p&gt;Cache First checks whether a resource already exists locally.&lt;/p&gt;

&lt;p&gt;If it’s there, you get it back straight away.&lt;/p&gt;

&lt;p&gt;If it’s not, the app goes and grabs it over the network, then tucks it into the cache so next time is quick.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                Request
                                   │
                                   ▼
                                 Cache
                                   │
                      ┌────────────┴────────────┐
                      │                         │
                    Found                    Not Found
                      │                         │
                      ▼                         ▼
                    Return                 Fetch Network
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The obvious win here is speed.&lt;/p&gt;

&lt;p&gt;Pulling something out of the cache beats a round trip to the server almost every single time.&lt;/p&gt;

&lt;p&gt;That's why Cache First works well for resources that rarely change.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Company logos&lt;/li&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Icons&lt;/li&gt;
&lt;li&gt;Fonts&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;JavaScript bundles&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to avoid it
&lt;/h3&gt;

&lt;p&gt;Don't use Cache First for information that users expect to be current.&lt;/p&gt;

&lt;p&gt;Serving an outdated shopping cart or account balance might be fast, but it creates a poor user experience.&lt;/p&gt;

&lt;h1&gt;
  
  
  Network First
&lt;/h1&gt;

&lt;p&gt;Network First takes the opposite approach.&lt;/p&gt;

&lt;p&gt;Rather than reaching for the cache first, it goes to the server every time to pull the newest version.&lt;/p&gt;

&lt;p&gt;If that call goes through, you get the fresh copy.&lt;/p&gt;

&lt;p&gt;If the network’s down, it drops back to whatever’s in the cache instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                Request
                                   │
                                   ▼
                                Network
                                   │
                      ┌────────────┴────────────┐
                      │                         │
                   Success                    Failure
                      │                         │
                      ▼                         ▼
                    Return                    Cache
                    Fresh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal isn't speed.&lt;/p&gt;

&lt;p&gt;It's correctness.&lt;/p&gt;

&lt;p&gt;Network First is a good fit for data that changes frequently, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shopping carts&lt;/li&gt;
&lt;li&gt;Notifications&lt;/li&gt;
&lt;li&gt;Account information&lt;/li&gt;
&lt;li&gt;Inventory&lt;/li&gt;
&lt;li&gt;Order status&lt;/li&gt;
&lt;li&gt;User permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The trade-off
&lt;/h3&gt;

&lt;p&gt;The user might wait a touch longer, but what lands in front of them is the most current data you’ve got.&lt;/p&gt;

&lt;p&gt;When getting it right beats getting it fast, that’s a trade worth making.&lt;/p&gt;

&lt;h1&gt;
  
  
  Stale-While-Revalidate
&lt;/h1&gt;

&lt;p&gt;Stale-While-Revalidate (SWR) is the middle path between those two.&lt;/p&gt;

&lt;p&gt;Rather than picking speed or freshness, it tries to give you a bit of each.&lt;/p&gt;

&lt;p&gt;When a request arrives:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Return the cached version immediately.&lt;/li&gt;
&lt;li&gt;Go fetch the newer version in the background.&lt;/li&gt;
&lt;li&gt;Swap the fresh copy into the cache for next time.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                                Request
                                   │
                                   ▼
                      Return Cached Data Immediately
                                   │
                                   ▼
                           Fetch Latest Version
                                   │
                                   ▼
                             Update Cache

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

&lt;/div&gt;



&lt;p&gt;To the person using it, the app feels basically instant, and it’s quietly keeping itself current at the same time.&lt;/p&gt;

&lt;p&gt;It’s a great fit for things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product listings&lt;/li&gt;
&lt;li&gt;Blog articles&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;News feeds&lt;/li&gt;
&lt;li&gt;Search results&lt;/li&gt;
&lt;li&gt;Social timelines&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The trade-off
&lt;/h3&gt;

&lt;p&gt;For a moment, someone might be looking at slightly old content before the fresh version swaps in.&lt;/p&gt;

&lt;p&gt;For a lot of apps, that’s a perfectly fine trade.&lt;/p&gt;

&lt;p&gt;For anything touching banking or payments, it usually isn’t.&lt;/p&gt;

&lt;h1&gt;
  
  
  Choosing the Right Strategy
&lt;/h1&gt;

&lt;p&gt;Don’t bother memorising definitions. Just ask yourself a couple of questions instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does this resource rarely change?
&lt;/h3&gt;

&lt;p&gt;Use &lt;strong&gt;Cache First&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Images&lt;/li&gt;
&lt;li&gt;Fonts&lt;/li&gt;
&lt;li&gt;Logos&lt;/li&gt;
&lt;li&gt;Icons&lt;/li&gt;
&lt;li&gt;Static assets&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Does it always need to be current?
&lt;/h3&gt;

&lt;p&gt;Use &lt;strong&gt;Network First&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checkout&lt;/li&gt;
&lt;li&gt;Shopping cart&lt;/li&gt;
&lt;li&gt;User permissions&lt;/li&gt;
&lt;li&gt;Payment status&lt;/li&gt;
&lt;li&gt;Inventory&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Is slightly outdated content acceptable if the application feels much faster?
&lt;/h3&gt;

&lt;p&gt;Use &lt;strong&gt;Stale-While-Revalidate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product catalogues&lt;/li&gt;
&lt;li&gt;Blogs&lt;/li&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Search&lt;/li&gt;
&lt;li&gt;News feeds&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  A Practical Cheat Sheet
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Recommended Strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Company logo&lt;/td&gt;
&lt;td&gt;Cache First&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product images&lt;/td&gt;
&lt;td&gt;Cache First&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fonts&lt;/td&gt;
&lt;td&gt;Cache First&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product catalogue&lt;/td&gt;
&lt;td&gt;Stale-While-Revalidate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search results&lt;/td&gt;
&lt;td&gt;Stale-While-Revalidate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;Stale-While-Revalidate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shopping cart&lt;/td&gt;
&lt;td&gt;Network First&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checkout&lt;/td&gt;
&lt;td&gt;Network First&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment status&lt;/td&gt;
&lt;td&gt;Network First&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Account balance&lt;/td&gt;
&lt;td&gt;Network First&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  Common Mistakes
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Using one strategy everywhere
&lt;/h2&gt;

&lt;p&gt;Not every resource wants the same thing.&lt;/p&gt;

&lt;p&gt;An image, a payment, a notification, a set of search results — treating all four the same way is asking for trouble.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimising for speed instead of user expectations
&lt;/h2&gt;

&lt;p&gt;Faster isn’t automatically better.&lt;/p&gt;

&lt;p&gt;Flashing someone a stale account balance is a far bigger problem than making them wait one more second for the real number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forgetting that caches become stale
&lt;/h2&gt;

&lt;p&gt;Caching was never only about stashing data somewhere.&lt;/p&gt;

&lt;p&gt;It’s just as much about deciding &lt;strong&gt;when that data has gone off&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Whatever you cache, have a plan for how and when it gets refreshed or thrown out.&lt;/p&gt;

&lt;h1&gt;
  
  
  Engineering Takeaways
&lt;/h1&gt;

&lt;p&gt;If only a handful of this sticks, make it these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cache First prioritises responsiveness.&lt;/li&gt;
&lt;li&gt;Network First prioritises correctness.&lt;/li&gt;
&lt;li&gt;Stale-While-Revalidate balances responsiveness and freshness.&lt;/li&gt;
&lt;li&gt;Different kinds of data call for different caching strategies.&lt;/li&gt;
&lt;li&gt;Optimise for what the user expects to see, not just for a green number on a dashboard.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  One Thing to Remember
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Every caching strategy is a trade-off. What decides the right one isn’t the tool in your hands — it’s what your users expect from the data they’re asking for.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once caching clicks into that frame, picking between Cache First, Network First, and Stale-While-Revalidate stops being a guessing game.&lt;/p&gt;

&lt;p&gt;If you want to go deeper, I’m putting together a much fuller guide right now — browser caching, HTTP caching, Service Workers, PWAs, and the architectural calls that sit behind modern frontend caching.&lt;/p&gt;

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