<?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: CodeScoop.dev</title>
    <description>The latest articles on DEV Community by CodeScoop.dev (@codescoop).</description>
    <link>https://dev.to/codescoop</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F2578%2F79cddd4e-7cf7-4c07-b40c-fe749dbd00a6.png</url>
      <title>DEV Community: CodeScoop.dev</title>
      <link>https://dev.to/codescoop</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codescoop"/>
    <language>en</language>
    <item>
      <title>Authentication on the Frontend - More Than Just Tokens</title>
      <dc:creator>Yogesh Yadav</dc:creator>
      <pubDate>Sun, 19 Apr 2026 14:12:36 +0000</pubDate>
      <link>https://dev.to/codescoop/authentication-on-the-frontend-more-than-just-tokens-2kj7</link>
      <guid>https://dev.to/codescoop/authentication-on-the-frontend-more-than-just-tokens-2kj7</guid>
      <description>&lt;p&gt;The decisions behind auth are more consequential than most developers realize.&lt;/p&gt;

&lt;p&gt;In this article we'll cover how token storage works and why the wrong choice creates real security vulnerabilities, how silent refresh keeps users logged in without interrupting their experience, how to handle session expiry gracefully, what actually happens during an OAuth flow, and the authentication decisions most frontend developers get wrong.&lt;/p&gt;




&lt;p&gt;Authentication is one of those things that looks simple from the outside. User logs in, you get a token, you attach it to requests. Done.&lt;/p&gt;

&lt;p&gt;Except it's not done. Not even close.&lt;/p&gt;

&lt;p&gt;I've worked on platforms handling more than 10 million active users across multiple OTT applications. Authentication touches every single one of them. And the decisions you make at the frontend layer - where to store tokens, how to refresh them, how to handle expiry - have consequences that go well beyond a login form.&lt;/p&gt;

&lt;p&gt;Get it wrong and you're looking at XSS vulnerabilities, session hijacking or users getting logged out mid-stream during a live event. At scale, any of those is a serious incident.&lt;/p&gt;

&lt;p&gt;This article is about getting it right.&lt;/p&gt;




&lt;h2&gt;
  
  
  Token Storage - The Decision That Has the Most Security Consequences
&lt;/h2&gt;

&lt;p&gt;When you authenticate a user and get back an access token, the first question is: where do you put it?&lt;/p&gt;

&lt;p&gt;There are three common options. Each has real tradeoffs.&lt;/p&gt;

&lt;h3&gt;
  
  
  localStorage
&lt;/h3&gt;

&lt;p&gt;This is the most common choice because it's the easiest. Persist the token, read it back on page load, attach it to requests. Simple.&lt;/p&gt;

&lt;p&gt;The problem is XSS. If an attacker manages to inject malicious JavaScript into your page - through a compromised dependency, a third-party script or an unsanitized input - they can read everything in localStorage. Your token is gone. The attacker now has it.&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;// Any injected script can do this&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;access_token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;// Token is now in the attacker's hands&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At scale, with multiple third-party scripts running on your platform, the XSS surface area is larger than you think.&lt;/p&gt;

&lt;h3&gt;
  
  
  httpOnly Cookies
&lt;/h3&gt;

&lt;p&gt;The browser sets these cookies automatically on every request to your domain. JavaScript cannot read them. That means an XSS attack cannot steal them.&lt;/p&gt;

&lt;p&gt;This sounds like the obvious winner, and for many server-rendered applications it is. But it comes with its own complexity. You need to think about CSRF protection, SameSite cookie attributes and cross-origin request handling. On a platform with multiple subdomains or a separate API domain, cookie configuration gets complicated quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  In-Memory Storage
&lt;/h3&gt;

&lt;p&gt;This is the approach I consider most secure for single-page applications. Store the access token in a JavaScript variable, never in localStorage, never in a cookie readable by JavaScript.&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;let&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setToken&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="nx"&gt;accessToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&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;function&lt;/span&gt; &lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An XSS attack cannot read a JavaScript variable from another script's scope if you're careful about how you expose it. The token lives only in memory and disappears when the tab closes.&lt;/p&gt;

&lt;p&gt;The tradeoff is that you lose persistence across page refreshes. Which is exactly why silent refresh exists.&lt;/p&gt;




&lt;h2&gt;
  
  
  Silent Refresh - Keeping Users Logged In Without Asking Them To
&lt;/h2&gt;

&lt;p&gt;Access tokens are short-lived by design. Typically 15 minutes to an hour. That's intentional - if one gets stolen, the damage window is limited.&lt;/p&gt;

&lt;p&gt;But you can't ask a user to log in again every hour. Especially on an OTT platform where they might be mid-stream during a live sports event.&lt;/p&gt;

&lt;p&gt;Silent refresh solves this. The idea is straightforward: before the access token expires, use the refresh token to get a new one automatically, without any visible interruption to the user.&lt;/p&gt;

&lt;p&gt;Here's the basic pattern:&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;let&lt;/span&gt; &lt;span class="nx"&gt;refreshTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scheduleTokenRefresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Refresh 60 seconds before expiry&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;refreshIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expiresIn&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
  &lt;span class="nx"&gt;refreshTimeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;refreshAccessToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nf"&gt;setToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nf"&gt;scheduleTokenRefresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expiresIn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Refresh failed, session has expired&lt;/span&gt;
      &lt;span class="nf"&gt;handleSessionExpiry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;refreshIn&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;A few things worth noting here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The refresh token should be stored in an httpOnly cookie.&lt;/strong&gt; It's longer-lived and more sensitive than the access token. You don't want JavaScript touching it directly. The server reads it from the cookie and issues a new access token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handle the case where the tab has been in the background.&lt;/strong&gt; Browsers throttle timers in inactive tabs. When the user comes back to the tab, check if the token has already expired and refresh immediately if so.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Guard against multiple simultaneous refresh calls.&lt;/strong&gt; On platforms with multiple concurrent API requests, several calls can fail at the same time when a token expires. Without a guard, you'll fire multiple refresh requests simultaneously.&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;let&lt;/span&gt; &lt;span class="nx"&gt;refreshPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;refreshAccessToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;refreshPromise&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;refreshPromise&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;refreshPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/auth/refresh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;finally&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;refreshPromise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;refreshPromise&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures only one refresh request goes out regardless of how many callers triggered it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Session Expiry -  Handle It Like It's Part of the Product
&lt;/h2&gt;

&lt;p&gt;Session expiry is inevitable. Refresh tokens expire. Users get logged out on other devices. Admins revoke access. Your job is to handle it in a way that doesn't feel broken.&lt;/p&gt;

&lt;p&gt;The worst experience is a silent failure - the user clicks something, nothing happens, no explanation. Or worse, they get a raw 401 error in the UI.&lt;/p&gt;

&lt;p&gt;A better approach has three parts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Intercept 401 responses globally.&lt;/strong&gt; Don't handle auth errors individually in every API call. Set up a single interceptor that catches 401s, attempts a token refresh, and retries the original request. If the refresh fails, then you handle expiry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;apiFetch&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="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;getToken&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;refreshAccessToken&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="nf"&gt;setToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="c1"&gt;// Retry original request with new token&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;handleSessionExpiry&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&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;Tell the user what happened.&lt;/strong&gt; When the session genuinely expires, show a clear message. "Your session has expired, please log in again." Not a blank screen, not a generic error. A real explanation with a clear action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preserve their intent.&lt;/strong&gt; Store the URL they were trying to access before redirecting to login. After they authenticate, bring them back to where they were. On a content platform this matters - a user who was about to watch something should land back on that content, not the homepage.&lt;/p&gt;




&lt;h2&gt;
  
  
  OAuth and Social Login - What Actually Happens in the Browser
&lt;/h2&gt;

&lt;p&gt;Most developers use a library for OAuth and treat it as a black box. That's fine until something breaks and then you need to understand what's actually happening.&lt;/p&gt;

&lt;p&gt;Here's the simplified flow for a third-party login:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User clicks "Login with Google."&lt;/li&gt;
&lt;li&gt;Your app redirects the browser to Google's authorization endpoint with your client ID, requested scopes and a redirect URI.&lt;/li&gt;
&lt;li&gt;Google authenticates the user and redirects back to your redirect URI with an authorization code.&lt;/li&gt;
&lt;li&gt;Your frontend sends that code to your backend.&lt;/li&gt;
&lt;li&gt;Your backend exchanges the code for tokens directly with Google using your client secret.&lt;/li&gt;
&lt;li&gt;Your backend issues its own session token to your frontend.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A few things to pay attention to here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never exchange the authorization code on the frontend.&lt;/strong&gt; The code exchange requires your client secret. That should never be in frontend code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the state parameter.&lt;/strong&gt; This is a random value you generate before the redirect and verify when the user comes back. It protects against CSRF attacks on the OAuth flow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initiateOAuthLogin&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;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oauth_state&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;REDIRECT_URI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;response_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openid email profile&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://accounts.google.com/o/oauth2/auth?&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleOAuthCallback&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;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URLSearchParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;search&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;returnedState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;state&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;savedState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sessionStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oauth_state&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;returnedState&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;savedState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OAuth state mismatch. Possible CSRF attack.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// Safe to proceed&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;Use PKCE for public clients.&lt;/strong&gt; If you're doing the token exchange from a mobile app or a pure SPA without a backend, PKCE (Proof Key for Code Exchange) replaces the client secret with a cryptographic challenge. It's the current best practice for frontend-only OAuth flows.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Security Decisions Most Developers Get Wrong
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Storing sensitive tokens in localStorage.&lt;/strong&gt; Covered above, but worth repeating. The convenience is not worth the XSS exposure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not setting token expiry short enough.&lt;/strong&gt; Access tokens should be short-lived. If yours are valid for 24 hours, a stolen token gives an attacker a 24-hour window. Keep them short and lean on silent refresh.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trusting the frontend for authorization.&lt;/strong&gt; Hiding a button in the UI is not authorization. The API must enforce access control. Frontend checks are for user experience only, never for security.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not handling token refresh race conditions.&lt;/strong&gt; Already covered with the single refresh promise pattern. This one causes subtle bugs that are hard to reproduce and harder to debug in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Logging out only on the current device.&lt;/strong&gt; When a user logs out or changes their password, invalidate their refresh tokens on the server. Otherwise they're still effectively logged in on every other device they've used.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sending tokens in URL parameters.&lt;/strong&gt; Tokens in URLs end up in browser history, server logs, and referrer headers. Always send them in the Authorization header or a cookie. Never in the URL.&lt;/p&gt;




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

&lt;p&gt;Authentication on the frontend is not just a login form and a token in localStorage. It's a set of decisions that affect the security, reliability, and user experience of your entire platform.&lt;/p&gt;

&lt;p&gt;Get the storage right. Build silent refresh properly. Handle expiry gracefully. Understand the OAuth flow well enough to debug it when it breaks.&lt;/p&gt;

&lt;p&gt;The developers who handle auth well aren't the ones who memorized every OAuth RFC. They're the ones who understood the tradeoffs and made deliberate decisions at each step.&lt;/p&gt;

&lt;p&gt;At scale, deliberate decisions are the only kind that hold up.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have thoughts or questions on frontend authentication? Drop them in the comments, always happy to discuss.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is part of the &lt;a href="https://dev.to/yogeshyadav/series/38132"&gt;&lt;strong&gt;Frontend at Scale&lt;/strong&gt;&lt;/a&gt; series.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>authentication</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Frontend Performance That Actually Moves the Needle</title>
      <dc:creator>Yogesh Yadav</dc:creator>
      <pubDate>Thu, 16 Apr 2026 20:01:05 +0000</pubDate>
      <link>https://dev.to/codescoop/frontend-performance-that-actually-moves-the-needle-41b7</link>
      <guid>https://dev.to/codescoop/frontend-performance-that-actually-moves-the-needle-41b7</guid>
      <description>&lt;p&gt;In this article we'll cover why Lighthouse scores alone don't tell the full story, what metrics actually matter at scale, how real user monitoring changes the way you think about performance, and the optimizations that have the most impact on platforms serving millions of users.&lt;/p&gt;

&lt;p&gt;Lighthouse is a great tool. I'm not here to tell you to ignore it. But I've seen teams chase a perfect Lighthouse score while their real users were experiencing 4-second load times on mid-range android devices with a 4G connection.&lt;/p&gt;

&lt;p&gt;The score looked great. The experience wasn't.&lt;/p&gt;

&lt;p&gt;When you're building for 10 million users, performance stops being about a number in a report. It becomes about real people on real devices with real network conditions. And the gap between a lab score and what your users actually feel is wider than most developers realize.&lt;/p&gt;

&lt;p&gt;This article is about closing that gap.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lighthouse Scores Are Lab Data, Not Reality
&lt;/h2&gt;

&lt;p&gt;Lighthouse runs in a controlled environment. Throttled CPU, simulated network, a clean browser with no extensions, no cached data, no background tabs. That's not how your users browse.&lt;/p&gt;

&lt;p&gt;Your users are on a 3-year-old phone with 15 browser tabs open, on a train with patchy network, while your JavaScript is fighting with a background app for CPU time.&lt;/p&gt;

&lt;p&gt;This is why a 90+ Lighthouse score can still result in a poor user experience. The lab doesn't lie, but it only tells you part of the truth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lab data:&lt;/strong&gt; what Lighthouse gives you - is useful for catching regressions and tracking trends over time. But it should never be your only signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Field data:&lt;/strong&gt; what your real users experience - is where performance work actually pays off.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Metrics That Actually Matter at Scale
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Web Vitals
&lt;/h3&gt;

&lt;p&gt;Google's Core Web Vitals are the closest thing we have to a standardized set of user-centric performance metrics. Three of them matter most:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LCP - Largest Contentful Paint:&lt;/strong&gt; How long does it take for the largest visible element to render? For most platforms this is a hero image, a video thumbnail or a headline. This is what the user perceives as "the page loaded."&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Target: under 2.5 seconds.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;INP - Interaction to Next Paint:&lt;/strong&gt; How quickly does the page respond after a user interaction? Click a button, tap a menu, submit a form - how long before the page visually responds? This replaced FID (First Input Delay) in 2024 and is a much better measure of real interactivity.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Target: under 200 milliseconds.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLS - Cumulative Layout Shift&lt;/strong&gt; How much does the page jump around while loading? Ads loading late, images without dimensions, fonts swapping - these all contribute to CLS. On a content-heavy platform this can quietly destroy the user experience.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Target: under 0.1.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These three metrics directly impact SEO ranking and user retention. At scale, a 0.1 improvement in CLS or a 500ms reduction in LCP translates to measurable engagement and conversion improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  TTFB - Time to First Byte
&lt;/h3&gt;

&lt;p&gt;Before the browser can render anything, it needs a response from the server. TTFB measures that wait time. High TTFB usually points to server-side issues - slow API responses, no CDN or unoptimized server rendering.&lt;/p&gt;

&lt;p&gt;On platforms with a global audience, CDN configuration alone can cut TTFB from 800ms to under 100ms for a significant portion of your users.&lt;/p&gt;

&lt;h3&gt;
  
  
  TTI - Time to Interactive
&lt;/h3&gt;

&lt;p&gt;When can the user actually use the page? Not just see it, but interact with it without the UI freezing. This is where JavaScript bundle size and execution time have the most direct impact.&lt;/p&gt;

&lt;p&gt;A page that looks loaded but isn't responding to clicks is one of the most frustrating experiences a user can have.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real User Monitoring - The Signal You Can't Ignore
&lt;/h2&gt;

&lt;p&gt;If you're only running Lighthouse, you're flying partially blind. Real User Monitoring (RUM) captures performance data from actual user sessions and sends it back to you.&lt;/p&gt;

&lt;p&gt;The difference is significant. With RUM you can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How performance varies by device type, browser and geography&lt;/li&gt;
&lt;li&gt;Which pages have the worst real-world LCP or INP&lt;/li&gt;
&lt;li&gt;How performance degrades over time as your codebase grows&lt;/li&gt;
&lt;li&gt;What percentage of your users are experiencing poor performance right now&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tools like Datadog RUM, SpeedCurve, Mux Data or even the free Chrome User Experience Report (CrUX) give you this visibility.&lt;/p&gt;

&lt;p&gt;On a platform serving millions of users, even if 5% of your users are experiencing poor performance, that's 500,000 people having a bad time. RUM makes that visible. Lighthouse doesn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Optimizations That Actually Move the Needle
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. JavaScript Bundle Size
&lt;/h3&gt;

&lt;p&gt;This is almost always the biggest lever. JavaScript is the most expensive resource on the web - it has to be downloaded, parsed, and executed before it does anything useful.&lt;/p&gt;

&lt;p&gt;Code splitting is non-negotiable at scale. Every route should load only the JavaScript it needs.&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;// Instead of importing everything upfront&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;HeavyComponent&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;./HeavyComponent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Load it only when needed&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HeavyComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./HeavyComponent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Audit your bundle regularly. Tools like &lt;code&gt;webpack-bundle-analyzer&lt;/code&gt; or &lt;code&gt;vite-bundle-visualizer&lt;/code&gt; will show you exactly what's in your bundle and where the weight is coming from. You will almost always find something surprising.&lt;/p&gt;

&lt;p&gt;Third-party scripts are usually the worst offenders. Analytics, chat widgets, ad scripts - these are often loaded synchronously and block rendering. Load them async or defer them entirely until after the page is interactive.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Image Optimization
&lt;/h3&gt;

&lt;p&gt;Images are the largest assets on most pages. Getting this wrong has a direct impact on LCP.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use modern formats. WebP is widely supported and significantly smaller than JPEG or PNG. AVIF is even better where supported.&lt;/li&gt;
&lt;li&gt;Always set explicit width and height on images. This prevents layout shift and helps the browser allocate space before the image loads.&lt;/li&gt;
&lt;li&gt;Use lazy loading for images below the fold.&lt;/li&gt;
&lt;li&gt;Serve appropriately sized images. Don't serve a 2000px wide image to a 400px wide mobile screen.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
  &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"thumbnail.webp"&lt;/span&gt;
  &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"400"&lt;/span&gt;
  &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"225"&lt;/span&gt;
  &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;
  &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Video thumbnail"&lt;/span&gt;
&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a platform with a large content library, image optimization alone can reduce page weight by 40–60%.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Critical Rendering Path
&lt;/h3&gt;

&lt;p&gt;The browser has to download your HTML, parse it, discover CSS and JavaScript, download those, parse them and then render the page. Every step in that chain is an opportunity to either speed things up or slow things down.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Inline critical CSS - the styles needed to render above-the-fold content - directly in the HTML. This eliminates a render-blocking network request for the initial view.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Preload key resources the browser won't discover until late in the parsing process.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"font"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/fonts/main.woff2"&lt;/span&gt; &lt;span class="na"&gt;crossorigin&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"image"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/hero.webp"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Defer non-critical JavaScript. If a script doesn't need to run before the page is interactive, it shouldn't block rendering.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Caching Strategy
&lt;/h3&gt;

&lt;p&gt;I covered this in depth in the previous article in this series, but it's worth mentioning here because caching is one of the highest-impact performance optimizations available to you.&lt;/p&gt;

&lt;p&gt;Repeat visitors on a well-cached platform can load pages almost entirely from cache. No network requests for static assets, no server round trips for unchanged resources. The performance improvement for returning users is dramatic.&lt;/p&gt;

&lt;p&gt;If you haven't read the &lt;a href="https://dev.to/codescoop/frontend-caching-done-right-2lem"&gt;caching article&lt;/a&gt; yet, it's worth going back to.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Reducing Main Thread Work
&lt;/h3&gt;

&lt;p&gt;INP and TTI both suffer when the main thread is busy. JavaScript execution, long tasks, layout recalculations - these all compete for the same thread that handles user interactions.&lt;/p&gt;

&lt;p&gt;A few things that help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Break up long tasks. Any task that takes more than 50ms can cause noticeable jank. Use setTimeout or scheduler.postTask to yield control back to the browser between chunks of work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Avoid layout thrashing. Reading and writing to the DOM in alternating calls forces the browser to recalculate layout repeatedly. Batch your reads and writes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Move heavy computation off the main thread with Web Workers.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Yielding to the browser between heavy tasks&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processLargeDataset&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;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;process&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;i&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Performance Budgets - Making It Stick
&lt;/h2&gt;

&lt;p&gt;One of the hardest parts of performance work at scale is keeping improvements from regressing over time. A performance budget solves this.&lt;/p&gt;

&lt;p&gt;A performance budget sets explicit limits on metrics like bundle size, LCP or TTI. If a pull request would push you over the budget, it fails the build.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resourceSizes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"resourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"script"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"budget"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"resourceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"budget"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"metric"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"first-contentful-paint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"budget"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"metric"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"interactive"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"budget"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3500&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps performance on everyone's radar, not just the engineer who cared enough to optimize it once.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Scale Actually Teaches You About Performance
&lt;/h2&gt;

&lt;p&gt;Here's what I've learned from working on platforms at this size that you don't find in most performance guides:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Device distribution matters more than you think.&lt;/strong&gt; Your development machine is not representative of your users. Profile on a mid-range Android device and you will find issues you never knew existed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Geography matters.&lt;/strong&gt; A platform with a global audience needs a CDN strategy, not just a fast server in one region. Network latency from a distant origin server can add seconds to TTFB for users in certain regions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance degrades gradually.&lt;/strong&gt; Nobody ships a slow app intentionally. It gets slow one dependency, one feature, one third-party script at a time. Without a budget and regular monitoring, you won't notice until users are already complaining.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The 80/20 rule applies.&lt;/strong&gt; A small number of pages usually account for the majority of your traffic. Find those pages, measure them obsessively and optimize them first. That's where your performance work will have the most impact.&lt;/p&gt;




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

&lt;p&gt;Lighthouse is a tool, not a goal. A green score means you've done the basics right. It doesn't mean your users are having a fast experience.&lt;/p&gt;

&lt;p&gt;The teams that get performance right at scale are the ones who measure what their real users experience, set budgets to prevent regression and focus their effort on the optimizations that actually move the needle for their specific platform and audience.&lt;/p&gt;

&lt;p&gt;Start with RUM. Find where your real users are struggling. Fix those things first.&lt;/p&gt;

&lt;p&gt;The Lighthouse score will follow.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have thoughts or questions on frontend performance? Drop them in the comments, always happy to discuss.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is part of the &lt;a href="https://dev.to/yogeshyadav/series/38132"&gt;&lt;strong&gt;Frontend at Scale&lt;/strong&gt;&lt;/a&gt; series.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>performance</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Frontend Caching Done Right</title>
      <dc:creator>Yogesh Yadav</dc:creator>
      <pubDate>Sun, 05 Apr 2026 06:39:19 +0000</pubDate>
      <link>https://dev.to/codescoop/frontend-caching-done-right-2lem</link>
      <guid>https://dev.to/codescoop/frontend-caching-done-right-2lem</guid>
      <description>&lt;p&gt;In this article we’ll cover how the browser cache and HTTP headers work, when and how to use stale-while-revalidate, how service workers give you programmatic control over caching, what you should never cache, and how cache invalidation works in practice. All of it from the perspective of building for platforms that can’t afford to get this wrong.&lt;/p&gt;

&lt;p&gt;Caching is one of those topics that every frontend developer thinks they understand, until they’re staring at a production issue where users are getting stale data, or worse, the server is getting hammered because nothing is being cached at all.&lt;/p&gt;

&lt;p&gt;I’ve worked on platforms handling more than 10 million active users. And I can tell you, caching stops being a “nice to have” the moment your scale starts growing. It becomes the difference between a platform that feels fast and one that quietly falls apart under load.&lt;/p&gt;

&lt;p&gt;This article is everything I’ve learned about frontend caching, written the way I wish someone had explained it to me early in my career.&lt;/p&gt;




&lt;h2&gt;
  
  
  First, Understand What You Are Actually Caching
&lt;/h2&gt;

&lt;p&gt;Before you touch a single HTTP header or write a line of service worker code, ask yourself one question.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the cost of serving stale data here?
&lt;/h3&gt;

&lt;p&gt;That question determines everything. Because caching is always a tradeoff between freshness and performance. The mistake most developers make is treating all resources the same way. They’re not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here’s how I think about it:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Static assets&lt;/strong&gt; (JS bundles, CSS, fonts, images) - these can be cached aggressively, sometimes forever, if you version them correctly.&lt;br&gt;
&lt;strong&gt;API responses&lt;/strong&gt; - depends entirely on how often the data changes and how much it matters if the user sees something slightly outdated.&lt;br&gt;
&lt;strong&gt;HTML documents&lt;/strong&gt; - usually should not be cached aggressively, especially for authenticated apps.&lt;br&gt;
&lt;strong&gt;User-specific data&lt;/strong&gt; - almost never cache this without thinking carefully.&lt;/p&gt;

&lt;p&gt;Get this mental model right first. Everything else follows from it.&lt;/p&gt;


&lt;h2&gt;
  
  
  Browser Cache and HTTP Headers
&lt;/h2&gt;

&lt;p&gt;The browser cache is your first and most powerful caching layer. It lives between the user and your server, and it’s controlled entirely through HTTP response headers.&lt;/p&gt;
&lt;h3&gt;
  
  
  Cache-Control
&lt;/h3&gt;

&lt;p&gt;This is the header you’ll use the most. Here’s what the key directives actually mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: max-age=31536000, immutablety
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;max-age&lt;/code&gt; tells the browser how many seconds to keep this resource before considering it stale. &lt;code&gt;immutable&lt;/code&gt; tells the browser not to bother revalidating it even on a hard refresh, because the content will never change.&lt;/p&gt;

&lt;p&gt;Use this combination for versioned static assets, your JS bundles, CSS files, and images that have a content hash in the filename. Something like &lt;code&gt;main.a3f9c2.js&lt;/code&gt;. The hash changes every build, so the URL changes, so you never serve stale code. Cache it forever.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: no-cache
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Despite the name, this does not mean “don’t cache.” It means “cache it, but check with the server every time before using it.” The server can respond with a &lt;code&gt;304 Not Modified&lt;/code&gt; and the browser uses the cached version. No full download needed.&lt;/p&gt;

&lt;p&gt;Use this for your HTML documents. You want the browser to always check if there’s a new version, but still benefit from caching when nothing has changed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: no-store
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This actually means “don’t cache.” Nothing is stored anywhere. Use this for sensitive data, authentication responses, anything you never want sitting in a cache.&lt;/p&gt;

&lt;h3&gt;
  
  
  ETag and Last-Modified
&lt;/h3&gt;

&lt;p&gt;These work alongside &lt;code&gt;Cache-Control&lt;/code&gt; for revalidation. When the browser asks "has this changed?", the server uses these to answer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;ETag: "abc123"
Last-Modified: Mon, 01 Jan 2024 00:00:00 GMT
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The browser sends back &lt;code&gt;If-None-Match: "abc123"&lt;/code&gt; or &lt;code&gt;If-Modified-Since&lt;/code&gt; on the next request. If nothing changed, the server returns 304 and saves the bandwidth of sending the full response again.&lt;/p&gt;

&lt;p&gt;At scale, these small savings add up to a significant reduction in server load.&lt;/p&gt;




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

&lt;p&gt;This is one of the most underused caching strategies I’ve seen in frontend codebases, and it’s genuinely powerful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Cache-Control: max-age=60, stale-while-revalidate=300
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s what this does. For the first 60 seconds, serve from cache, no questions asked. Between 60 and 300 seconds, serve the stale cached version immediately, but kick off a background request to revalidate it. After 300 seconds, it’s stale and must be revalidated before serving.&lt;/p&gt;

&lt;p&gt;The user gets an instant response. The cache gets updated in the background. No loading spinner, no waiting.&lt;/p&gt;

&lt;p&gt;This pattern is perfect for data that changes occasionally but doesn’t need to be real-time. Think navigation menus, configuration data, content that updates a few times a day. The user always gets a fast experience and the data stays reasonably fresh.&lt;/p&gt;

&lt;p&gt;You’ll also recognize this pattern from TanStack Query’s &lt;code&gt;staleTime&lt;/code&gt; and &lt;code&gt;gcTime&lt;/code&gt; configuration. The underlying idea is exactly the same, just applied at the JavaScript layer instead of the HTTP layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Service Workers - The Programmable Cache
&lt;/h2&gt;

&lt;p&gt;HTTP headers give you declarative control over caching. Service workers give you programmatic control. That’s a significant difference.&lt;/p&gt;

&lt;p&gt;A service worker sits between your app and the network, intercepting every request. You decide what happens with each one.&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="s1"&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respondWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cachedResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cachedResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cachedResponse&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fetch&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="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a simple cache-first strategy. Check the cache first, fall back to the network if nothing is found. For a platform serving millions of users, this means repeat visitors often never hit your server for static assets at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Caching Strategies with Service Workers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cache First&lt;/strong&gt; - Serve from cache, fall back to network. Best for static assets that rarely change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Network First&lt;/strong&gt; - Try the network, fall back to cache if offline. Best for API data where freshness matters but offline support is needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stale While Revalidate&lt;/strong&gt; - Serve from cache immediately, update cache in background. Best for non-critical content where speed matters more than freshness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache Only&lt;/strong&gt; - Only serve from cache. Useful for assets you’ve pre-cached during install.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Network Only&lt;/strong&gt; - Always go to network. For requests that should never be cached, like analytics or payment endpoints.&lt;/p&gt;

&lt;p&gt;Pick your strategy per resource type, not globally. A single strategy for everything is almost always the wrong call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pre-caching vs Runtime Caching
&lt;/h3&gt;

&lt;p&gt;Pre-caching happens when the service worker installs. You explicitly list assets to cache upfront.&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="s1"&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addAll&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/styles/main.css&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;/scripts/main.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;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Runtime caching happens dynamically as requests come in. You cache responses as they’re fetched, so frequently accessed resources end up in cache naturally over time.&lt;/p&gt;

&lt;p&gt;For most platforms, you want both. Pre-cache your critical shell, runtime cache everything else.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Not to Cache
&lt;/h2&gt;

&lt;p&gt;This is the part that trips people up. Caching the wrong things causes bugs that are genuinely hard to debug in production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never aggressively cache:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authentication tokens or session data&lt;/li&gt;
&lt;li&gt;Payment and transaction endpoints&lt;/li&gt;
&lt;li&gt;User-specific personalization data&lt;/li&gt;
&lt;li&gt;Anything that changes per user or per session&lt;/li&gt;
&lt;li&gt;A/B test configurations if they need to be real-time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’ve seen teams cache API responses that included user-specific entitlements. The result was users seeing content they shouldn’t have access to, or not seeing content they’d just purchased. At scale, that’s not just a bug. It’s a trust problem.&lt;/p&gt;

&lt;p&gt;When in doubt, don’t cache it, or use &lt;code&gt;no-cache&lt;/code&gt; so at least revalidation happens.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cache Invalidation - The Hard Part
&lt;/h2&gt;

&lt;p&gt;There’s a famous saying in computer science: there are only two hard things, naming things and cache invalidation.&lt;/p&gt;

&lt;p&gt;It’s funny because it’s true.&lt;/p&gt;

&lt;p&gt;For static assets, content-hashed filenames solve this completely. New deploy, new hash, new URL, new cache entry. Old one expires naturally.&lt;/p&gt;

&lt;p&gt;For API responses and service worker caches, you need a versioning strategy.&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;CACHE_VERSION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&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="s1"&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;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;cacheNames&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;cacheNames&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;name&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;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;CACHE_VERSION&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;name&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;caches&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time you deploy, bump the cache version. The activate event cleans up old caches. Users get fresh data on their next visit without you having to manually purge anything.&lt;/p&gt;




&lt;h2&gt;
  
  
  Caching at Scale - What Actually Changes
&lt;/h2&gt;

&lt;p&gt;When you’re building for 10 million users, the fundamentals don’t change. But the consequences of getting it wrong are amplified significantly.&lt;/p&gt;

&lt;p&gt;A few things I’ve learned from operating at that scale:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measure before you optimize.&lt;/strong&gt; Use Chrome DevTools, Lighthouse, and your RUM (Real User Monitoring) data to understand where your actual cache hit rates are. Don’t guess.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CDN caching and browser caching are different layers.&lt;/strong&gt; Your CDN has its own cache headers, often separate from what the browser sees. Understand both. Misconfiguring your CDN can mean millions of users bypassing the browser cache entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache stampedes are real.&lt;/strong&gt; When a popular cached resource expires simultaneously for millions of users, they all hit your server at once. Stale-while-revalidate and jittered expiry times help prevent this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitor your cache hit ratio.&lt;/strong&gt; If it’s low, you’re leaving performance on the table. If it’s too high and you’re seeing stale data complaints, your TTLs are too aggressive.&lt;/p&gt;




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

&lt;p&gt;Caching is not a set-it-and-forget-it feature. It’s an ongoing engineering decision that touches performance, correctness and user experience all at once.&lt;/p&gt;

&lt;p&gt;Start with HTTP headers and get those right. Layer in stale-while-revalidate for the right resources. Add service workers when you need offline support or more granular control. And always think about invalidation before you think about caching.&lt;/p&gt;

&lt;p&gt;The developers who get this right aren’t the ones who know the most cache directives. They’re the ones who ask the right question first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the cost of serving stale data here?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Answer that honestly for every resource, and the rest follows.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have thoughts or questions on frontend caching? Drop them in the comments, always happy to discuss.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is part of the &lt;a href="https://dev.to/yogeshyadav/series/38132"&gt;&lt;strong&gt;Frontend at Scale&lt;/strong&gt;&lt;/a&gt; series.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>caching</category>
      <category>javascript</category>
      <category>performance</category>
    </item>
    <item>
      <title>Explore the lexical Environment &amp; Environment Record in Javascript 2021</title>
      <dc:creator>Yogesh Yadav</dc:creator>
      <pubDate>Wed, 17 Aug 2022 07:23:00 +0000</pubDate>
      <link>https://dev.to/codescoop/what-has-changed-in-lexical-environment-as-per-ecmascript-2021-2bjb</link>
      <guid>https://dev.to/codescoop/what-has-changed-in-lexical-environment-as-per-ecmascript-2021-2bjb</guid>
      <description>&lt;p&gt;Let's first understand the &lt;code&gt;Lexical Environment&lt;/code&gt; &amp;amp; &lt;code&gt;Environment Record&lt;/code&gt; as per different versions of ECMAScript Specification.&lt;/p&gt;

&lt;p&gt;From &lt;a href="https://262.ecma-international.org/6.0/" rel="noopener noreferrer"&gt;&lt;strong&gt;ES2015&lt;/strong&gt;&lt;/a&gt; till &lt;a href="https://262.ecma-international.org/11.0/" rel="noopener noreferrer"&gt;&lt;strong&gt;ES2020&lt;/strong&gt;&lt;/a&gt; Specification:-&lt;/p&gt;

&lt;h2&gt;Lexical Environment: &lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A lexical environment is a &lt;strong&gt;specification type&lt;/strong&gt; used to define the association of Identifiers to specific variables and functions, based upon the lexical nesting structure of your code.&lt;/li&gt;
&lt;li&gt;A lexical environment consists of two components:

&lt;ol&gt;
&lt;li&gt;
&lt;h4&gt;Environment Record&lt;/h4&gt; It records the &lt;strong&gt;identifier bindings&lt;/strong&gt; that are created within the scope of its associated Lexical Environment. It is referred to as the Lexical Environment's EnvironmentRecord.&lt;/li&gt;
&lt;li&gt;
&lt;h4&gt;Outer Reference&lt;/h4&gt; A reference to outer environment (null in the global environment).&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A conceptual view using pseudo-code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;executioncontext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;environmentRecord&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;// storage&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// reference to the parent environment&lt;/span&gt;
    &lt;span class="nl"&gt;outer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; - The &lt;code&gt;[[Environment]]&lt;/code&gt; created inside Execution Context is of &lt;code&gt;type&lt;/code&gt; &lt;strong&gt;Lexical Environment&lt;/strong&gt; &lt;br&gt;
 &lt;a href="https://262.ecma-international.org/11.0/#sec-ecmascript-function-objects" rel="noopener noreferrer"&gt;[refer ES2020]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foeq4770whr2mksmrwt0s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foeq4770whr2mksmrwt0s.png" alt="Lexical Environment in Global Scope" width="800" height="418"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;According to &lt;a href="https://262.ecma-international.org/12.0/" rel="noopener noreferrer"&gt;&lt;strong&gt;12th Edition ECMAScript2021&lt;/strong&gt;&lt;/a&gt; Specification:&lt;/p&gt;

&lt;h2&gt;Environment Record &lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A Environment Record is a &lt;strong&gt;specification type&lt;/strong&gt; used to define the association of Identifiers to specific variables and functions, based upon the lexical nesting structure of your code.&lt;/li&gt;
&lt;li&gt;Every Environment Record has one component:

&lt;ol&gt;
&lt;li&gt;
&lt;h4&gt;Outer Reference&lt;/h4&gt; An &lt;code&gt;[[OuterEnv]]&lt;/code&gt; field, which is either null or a reference to an outer Environment Record.
A conceptual view using pseudo-code:
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;executioncontext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// storage&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;identifier&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// reference to the parent environment&lt;/span&gt;
    &lt;span class="na"&gt;outer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; - The &lt;code&gt;[[Environment]]&lt;/code&gt; created inside Execution Context is of &lt;code&gt;type&lt;/code&gt; &lt;strong&gt;Environment Record&lt;/strong&gt; &lt;a href="https://262.ecma-international.org/12.0/#sec-ecmascript-function-objects" rel="noopener noreferrer"&gt;[refer ES2021]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06g8v67qzlgcds5i22fk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06g8v67qzlgcds5i22fk.png" alt="Environment Record in Global Scope" width="800" height="399"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Let's also understand the &lt;code&gt;Structure of execution context&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt; Execution Context: &lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An execution context is a specification device that is used to track the runtime evaluation of the code.&lt;/li&gt;
&lt;li&gt;To keeps the track of execution progress of its associated code, it needs various &lt;strong&gt;state components&lt;/strong&gt; like &lt;code&gt;LexicalEnvironment&lt;/code&gt;, &lt;code&gt;VariableEnvironment&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In pseudo-code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;ExecutionContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;VariableEnvironment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;LexicalEnvironment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// other components&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;Note:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Till ES2020&lt;/th&gt;
&lt;th&gt;From ES2021&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;- The &lt;code&gt;LexicalEnvironment component&lt;/code&gt; and &lt;code&gt;VariableEnvironment component&lt;/code&gt; of an execution context are always &lt;strong&gt;Lexical Environments&lt;/strong&gt; &lt;a href="https://262.ecma-international.org/11.0/#table-23" rel="noopener noreferrer"&gt;[refer ES2020]&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;- The &lt;code&gt;LexicalEnvironment component&lt;/code&gt; and &lt;code&gt;VariableEnvironment&lt;/code&gt; components of an execution context are always &lt;strong&gt;Environment Records&lt;/strong&gt; &lt;a href="https://262.ecma-international.org/12.0/#table-23" rel="noopener noreferrer"&gt;[refer ES2021]&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt; Summary &lt;/h2&gt;

&lt;p&gt;Let's have a quick recap of all the steps we perform in the above code snippet.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In ECMAScript2021, the &lt;code&gt;[[environment]]&lt;/code&gt; which is created inside the execution context is of &lt;code&gt;type&lt;/code&gt; &lt;strong&gt;Environment Record&lt;/strong&gt; instead of Lexical Environment.&lt;/li&gt;
&lt;li&gt;So, The &lt;code&gt;LexicalEnvironment component&lt;/code&gt; and &lt;code&gt;VariableEnvironment components&lt;/code&gt; of an execution context are always &lt;strong&gt;Environment Records&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Thank you for your time!! Let's connect to learn and grow together.&lt;br&gt;
&lt;a href="https://github.com/deltanode" rel="noopener noreferrer"&gt;Github&lt;/a&gt; &lt;a href="https://twitter.com/yogesh_yadv" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>computerscience</category>
    </item>
  </channel>
</rss>
