<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Rohit Kuwar on Medium]]></title>
        <description><![CDATA[Stories by Rohit Kuwar on Medium]]></description>
        <link>https://medium.com/@rohitkuwar?source=rss-faedefd908b4------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*J4Cd1nV2XgH61bKIOcJ6yQ.jpeg</url>
            <title>Stories by Rohit Kuwar on Medium</title>
            <link>https://medium.com/@rohitkuwar?source=rss-faedefd908b4------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 05 Nov 2026 04:47:43 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@rohitkuwar/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[HTTP Request & Response Headers Explained: A Beginner’s Guide]]></title>
            <link>https://medium.com/@rohitkuwar/http-request-response-headers-explained-a-beginners-guide-e61e50080d6d?source=rss-faedefd908b4------2</link>
            <guid isPermaLink="false">https://medium.com/p/e61e50080d6d</guid>
            <category><![CDATA[api]]></category>
            <category><![CDATA[https]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[networking]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Rohit Kuwar]]></dc:creator>
            <pubDate>Sat, 31 Oct 2026 02:31:02 GMT</pubDate>
            <atom:updated>2026-10-31T02:31:02.117Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Dz0lqylKEQPii_1UqCoOgQ.png" /><figcaption>HTTP Request &amp; Response Headers Explained</figcaption></figure><p>Have you ever opened the Network tab in your browser’s Developer Tools and noticed a long list of HTTP headers?</p><p>You might have seen headers such as:</p><pre>Content-Type: application/json<br>Authorization: Bearer eyJhbGciOi...<br>Accept: application/json<br>Cache-Control: no-cache<br>Set-Cookie: sessionId=abc123; HttpOnly; Secure</pre><p>But what do these headers actually mean?</p><p>Why does the browser send some headers to the server, while the server sends others back to the browser?</p><p>And as a frontend developer, when should you use headers in your JavaScript or React applications?</p><p>Let’s understand these concepts from scratch.</p><p>In this article, we’ll cover:</p><ul><li>What HTTP headers are and why we need them.</li><li>The difference between request headers and response headers.</li><li>Important headers you’ll encounter in real applications.</li><li>How to send and read headers using JavaScript.</li><li>How headers work with authentication, caching, and CORS.</li><li>How to inspect headers in browser Developer Tools.</li></ul><p>By the end, you’ll be able to look at an HTTP request and understand what most of its important headers are doing.</p><h3>1. First, Understand How HTTP Communication Works</h3><p>Imagine you’re building an e-commerce application.</p><p>A user opens the product listing page, and your React application needs to fetch products from the backend.</p><p>Your frontend sends a request:</p><pre>fetch(&quot;https://api.example.com/products&quot;);</pre><p>The browser sends an HTTP request to the server.</p><p>The server processes the request and returns a response containing the requested data.</p><p>The flow looks like this:</p><pre>       Browser / React Application<br>                  |<br>                  |<br>            HTTP Request<br>         URL + Headers + Body<br>                  |<br>                  v<br>             API Server<br>                  |<br>             Process Request<br>                  |<br>                  v<br>            HTTP Response<br>          Status + Headers<br>               + Body<br>                  |<br>                  v<br>       Browser / React Application<br>                  |<br>                  v<br>             Display Products</pre><p>An HTTP message contains more than just the data you’re requesting.</p><p>It also contains information about the request or response, such as:</p><ul><li>The expected data format.</li><li>The format of the data being sent.</li><li>Authentication credentials.</li><li>Caching instructions.</li><li>Cookie information.</li><li>Security policies.</li></ul><p>This additional information is often communicated through <strong>HTTP headers</strong>.</p><h3>2. What Are HTTP Headers?</h3><p>HTTP headers are key-value pairs that provide metadata about an HTTP request or response.</p><p>For example:</p><pre>Content-Type: application/json</pre><p>Here:</p><ul><li>Content-Type is the header name.</li><li>application/json is the header value.</li></ul><p>Together, they tell the recipient that the message body contains JSON data.</p><p>Another example:</p><pre>Authorization: Bearer my-access-token</pre><p>This header carries an authentication credential that the server can validate.</p><p>A simple HTTP request might look like this:</p><pre>GET /products HTTP/1.1<br>Host: api.example.com<br>Accept: application/json<br>Authorization: Bearer my-access-token</pre><p>The request line identifies the method and requested resource. The headers provide additional information.</p><p>The server might respond with:</p><pre>HTTP/1.1 200 OK<br>Content-Type: application/json<br>Cache-Control: no-store<br><br>[<br>  {<br>    &quot;id&quot;: 1,<br>    &quot;name&quot;: &quot;Laptop&quot;,<br>    &quot;price&quot;: 65000<br>  }<br>]</pre><p>The response includes a status line, headers, and a body.</p><p><strong>Important:</strong> These examples use the traditional HTTP/1.1 text representation to make the messages easy to read. HTTP/2 and HTTP/3 use different wire formats, but the header concepts remain largely the same.</p><h3>3. Request Headers vs Response Headers</h3><p>HTTP headers are commonly discussed in two categories.</p><h4>Request headers</h4><p>Request headers provide information about the request being sent by the client.</p><p>For example:</p><pre>GET /products HTTP/1.1<br>Host: api.example.com<br>Accept: application/json<br>Authorization: Bearer my-access-token</pre><p>The browser is requesting products and indicating that it accepts JSON responses.</p><p>The authorization header provides a credential for the server to validate.</p><h4>Response headers</h4><p>Response headers provide information about the response being returned by the server.</p><p>For example:</p><pre>HTTP/1.1 200 OK<br>Content-Type: application/json<br>Cache-Control: max-age=300</pre><p>The server is returning a successful response containing JSON, with a caching policy that allows the response to be stored and reused for up to 300 seconds, subject to the applicable HTTP caching rules.</p><h4>Quick comparison</h4><pre>+----------------------+--------------------------+<br>| Request Headers      | Response Headers         |<br>+----------------------+--------------------------+<br>| Sent by the client   | Sent by the server       |<br>| Describe the request | Describe the response    |<br>| Accept               | Content-Type             |<br>| Authorization       | Set-Cookie               |<br>| Content-Type         | Cache-Control             |<br>| Origin               | Access-Control-Allow-*   |<br>+----------------------+--------------------------+</pre><p>This is a simplified comparison. Some headers, such as Cache-Control and Content-Type, can appear in both requests and responses.</p><p>Now let’s explore the headers you’ll encounter most frequently.</p><h3>4. Important HTTP Request Headers</h3><h4>4.1 Accept</h4><p>The Accept header tells the server which response media types the client is willing to receive.</p><p>For example:</p><pre>Accept: application/json</pre><p>This tells the server that the client accepts JSON responses.</p><p>Another example:</p><pre>Accept: text/html</pre><p>This indicates that the client accepts HTML.</p><p>You can also specify multiple media types:</p><pre>Accept: application/json, text/plain</pre><p><strong>Real-world use case:</strong> Your frontend communicates with an API that supports different response formats. The Accept header can help the server select an appropriate representation.</p><p>Remember the distinction:</p><ul><li>Accept describes the response format the client can accept.</li><li>Content-Type describes the media type of the message body.</li></ul><h4>4.2 Content-Type</h4><p>The Content-Type header describes the media type of the body being sent.</p><p>For example, suppose your React application sends JSON to the backend:</p><pre>fetch(&quot;https://api.example.com/users&quot;, {<br>  method: &quot;POST&quot;,<br>  headers: {<br>    &quot;Content-Type&quot;: &quot;application/json&quot;<br>  },<br>  body: JSON.stringify({<br>    name: &quot;Rohit&quot;,<br>    role: &quot;Frontend Developer&quot;<br>  })<br>});</pre><p>The request body is:</p><pre>{<br>  &quot;name&quot;: &quot;Rohit&quot;,<br>  &quot;role&quot;: &quot;Frontend Developer&quot;<br>}</pre><p>The Content-Type header tells the server how to interpret that body.</p><p>Other common media types include:</p><pre>application/json<br>text/html<br>text/plain<br>application/x-www-form-urlencoded<br>multipart/form-data</pre><p>One important detail: when uploading a FormData object using fetch, you should generally not manually set Content-Type: multipart/form-data. The browser needs to generate the boundary parameter and include it in the header.</p><h4>4.3 Authorization</h4><p>The Authorization header is used to provide credentials for authenticating a request.</p><p>For example:</p><pre>Authorization: Bearer eyJhbGciOi...</pre><p>In JavaScript:</p><pre>const token = &quot;your-access-token&quot;;<br><br>fetch(&quot;https://api.example.com/profile&quot;, {<br>  headers: {<br>    Authorization: `Bearer ${token}`<br>  }<br>});</pre><p>The backend receives the token and validates it according to the application’s authentication system.</p><p>If the credential is invalid or missing, the server may reject the request.</p><p>A common distinction:</p><ul><li><strong>Authentication:</strong> Who are you?</li><li><strong>Authorization:</strong> Are you allowed to perform this action?</li></ul><p>The header carries credentials; the server must still validate them and enforce authorization rules.</p><p>Also, a Bearer token is only one possible authentication scheme. The Authorization header supports other schemes as well.</p><h4>4.4 Cookie</h4><p>The Cookie header sends cookies that the browser has selected for a request.</p><p>For example:</p><pre>Cookie: sessionId=abc123</pre><p>Browsers typically manage this header automatically when applicable cookies match the request’s domain, path, security, and other cookie rules.</p><p>With an HttpOnly cookie, JavaScript cannot read the cookie value through document.cookie, but the browser can still send it with eligible requests.</p><p>This is one reason cookie-based authentication needs careful configuration.</p><p><strong>Important:</strong> In browser JavaScript, you generally cannot manually set the Cookie request header through fetch. The browser manages it.</p><h4>4.5 Origin</h4><p>The Origin header identifies the origin from which a request was initiated in applicable contexts.</p><p>An origin consists of:</p><pre>scheme + hostname + port</pre><p>For example:</p><pre>Origin: https://myapp.example.com</pre><p>This header is important for Cross-Origin Resource Sharing (CORS).</p><p>Suppose your frontend runs at:</p><pre>https://myapp.example.com</pre><p>And your backend runs at:</p><pre>https://api.example.com</pre><p>These are different origins, even though they share the same parent domain.</p><p>The browser may include an Origin header, and the server can use it as part of its CORS decision.</p><p>The frontend cannot freely set the Origin header manually using fetch; it is controlled by the browser in relevant requests.</p><h4>4.6 User-Agent</h4><p>The User-Agent header traditionally identifies information about the client software making a request.</p><p>For example, a request might include browser and operating-system details.</p><p>Servers have historically used this information for analytics, compatibility decisions, and debugging.</p><p>However, User-Agent information can be reduced or modified for privacy reasons, so it shouldn’t be treated as a reliable way to identify a user or establish trust.</p><h4>4.7 Cache-Control</h4><p>Cache-Control communicates caching directives.</p><p>For example, a request can include:</p><pre>Cache-Control: no-cache</pre><p>This indicates that a stored response should be revalidated before being reused when applicable.</p><p>It does <strong>not</strong> necessarily mean that the browser must avoid storing the response.</p><p>Another directive is:</p><pre>Cache-Control: no-store</pre><p>This tells caches not to store the request or response under the applicable caching rules.</p><p>The distinction matters:</p><ul><li>no-cache → stored responses generally need validation before reuse.</li><li>no-store → don&#39;t store the message in caches.</li></ul><p>The header can appear in both requests and responses, and the meaning depends on the directive and message context.</p><h3>5. Important HTTP Response Headers</h3><p>Now let’s explore headers that the server commonly sends back to the browser.</p><h4>5.1 Content-Type</h4><p>The response Content-Type tells the client what kind of data the response body contains.</p><p>For example:</p><pre>HTTP/1.1 200 OK<br>Content-Type: application/json</pre><p>The response body might be:</p><pre>{<br>  &quot;id&quot;: 101,<br>  &quot;name&quot;: &quot;Rohit&quot;<br>}</pre><p>For an HTML page, the server might send:</p><pre>Content-Type: text/html; charset=utf-8</pre><p>Why is this important?</p><p>The client needs to interpret the response correctly. The server should send the appropriate media type rather than relying on the browser to guess.</p><h4>5.2 Set-Cookie</h4><p>The Set-Cookie response header instructs the browser to create or update a cookie, subject to cookie rules.</p><p>For example:</p><pre>Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax</pre><p>Let’s understand the attributes:</p><ul><li>HttpOnly prevents ordinary JavaScript access to the cookie.</li><li>Secure restricts cookie transmission to secure HTTPS connections.</li><li>SameSite=Lax limits sending the cookie in certain cross-site contexts.</li></ul><p>The browser stores the cookie when the applicable rules permit it.</p><p>On a later matching request, the browser may automatically send it:</p><pre>Cookie: sessionId=abc123</pre><p>This is commonly used in session-based authentication.</p><p>A key distinction:</p><p>Set-Cookie is a response header, while Cookie is a request header.</p><h4>5.3 Cache-Control</h4><p>Servers use Cache-Control to control how responses may be cached.</p><p>For example:</p><pre>Cache-Control: public, max-age=3600</pre><p>This allows eligible shared and private caches to store the response for up to 3,600 seconds, subject to other caching rules.</p><p>For sensitive responses, a server may instead use:</p><pre>Cache-Control: no-store</pre><p>This is useful when the response contains sensitive information that shouldn’t be stored in HTTP caches.</p><p>Caching policies should be selected according to the data and the application’s requirements.</p><h4>5.4 Access-Control-Allow-Origin</h4><p>This is one of the most important response headers for frontend developers.</p><p>The Access-Control-Allow-Origin header tells browsers which origin is permitted to access a response through CORS.</p><p>For example:</p><pre>Access-Control-Allow-Origin: https://myapp.example.com</pre><p>This allows the specified origin to access the response, subject to the rest of the CORS rules.</p><p>A server might return:</p><pre>Access-Control-Allow-Origin: *</pre><p>This allows any origin for requests where wildcard behavior is permitted. However, the wildcard cannot be used to authorize credentialed CORS access.</p><p>For credentialed cross-origin requests, the server generally needs to return a specific allowed origin and:</p><pre>Access-Control-Allow-Credentials: true</pre><p>The browser’s CORS rules determine whether frontend JavaScript can access the response.</p><p><strong>Important:</strong> CORS is enforced by browsers. It is not a replacement for backend authentication or authorization.</p><h4>5.5 Content-Security-Policy</h4><p>The Content-Security-Policy header lets a server define restrictions on the resources a page can load and the script execution it permits.</p><p>For example:</p><pre>Content-Security-Policy: default-src &#39;self&#39;</pre><p>This establishes a default policy allowing resources from the page’s own origin unless a more specific directive applies.</p><p>A real production policy often needs additional directives for scripts, styles, images, fonts, API connections, and other resources.</p><p>CSP can help reduce the impact of certain Cross-Site Scripting (XSS) attacks, but it isn’t a substitute for preventing injection vulnerabilities.</p><h4>5.6 X-Content-Type-Options</h4><p>A commonly used security header is:</p><pre>X-Content-Type-Options: nosniff</pre><p>This tells browsers not to perform certain MIME-type sniffing behaviors.</p><p>It helps ensure that browsers respect the declared media type in contexts where MIME sniffing could otherwise create security problems.</p><p>The server should still return the correct Content-Type.</p><h4>5.7 Location</h4><p>The Location header identifies a URL associated with a redirect or a newly created resource.</p><p>For example, a redirect response might look like:</p><pre>HTTP/1.1 302 Found<br>Location: https://example.com/login</pre><p>The client can use the provided URL as part of redirect handling.</p><p>Another example is a successful resource-creation response:</p><pre>HTTP/1.1 201 Created<br>Location: /api/products/101</pre><p>Here, the header identifies the newly created resource.</p><p>The exact behavior depends on the status code and client.</p><h3>6. A Quick Reference: Common Headers</h3><p>The following fixed-width table is designed to be pasted into Medium as a code block.</p><pre>+------------------------------+-------------------------------+<br>| Header                       | Purpose                       |<br>+------------------------------+-------------------------------+<br>| Accept                       | Response types client accepts |<br>| Content-Type                 | Format of message body        |<br>| Authorization                | Authentication credentials    |<br>| Cookie                       | Sends applicable cookies      |<br>| Set-Cookie                   | Creates or updates cookies    |<br>| Origin                       | Identifies request origin     |<br>| Cache-Control                | Defines caching directives    |<br>| Access-Control-Allow-Origin  | Permitted CORS origin         |<br>| Access-Control-Allow-Methods | Allowed CORS methods          |<br>| Access-Control-Allow-Headers | Allowed CORS request headers  |<br>| Access-Control-Allow-Creds   | Allows credentialed CORS      |<br>| Content-Security-Policy      | Restricts resource loading    |<br>| X-Content-Type-Options       | Helps prevent MIME sniffing   |<br>| Location                     | Redirect or resource URL      |<br>+------------------------------+-------------------------------+</pre><p>This is a quick reference, not a complete list of HTTP headers. Also, a header’s presence alone doesn’t guarantee a particular security outcome; the browser, server, and other protocol rules all matter.</p><h3>7. How to Read Headers Using JavaScript</h3><p>Let’s make the concepts practical.</p><p>Suppose we want to fetch products from an API and inspect the response.</p><pre>async function getProducts() {<br>    const response = await fetch(<br>        &quot;https://api.example.com/products&quot;, {<br>            headers: {<br>                Accept: &quot;application/json&quot;<br>            }<br>        }<br>    );<br><br>    console.log(&quot;Status:&quot;, response.status);<br>    console.log(<br>        &quot;Content-Type:&quot;,<br>        response.headers.get(&quot;Content-Type&quot;)<br>    );<br>    console.log(<br>        &quot;Cache-Control:&quot;,<br>        response.headers.get(&quot;Cache-Control&quot;)<br>    );<br>    const data = await response.json();<br>    console.log(data);<br>}<br><br>getProducts();</pre><p>Let’s understand what’s happening.</p><p>First, we send a request with an Accept header:</p><pre>headers: {<br>  Accept: &quot;application/json&quot;<br>}</pre><p>Then, we inspect the response status:</p><pre>response.status</pre><p>This is a status code, not a header.</p><p>Finally, we read response headers:</p><pre>response.headers.get(&quot;Content-Type&quot;)</pre><p>The Headers interface lets us retrieve a response header by name.</p><h3>One important limitation</h3><p>When calling a cross-origin API, frontend JavaScript can read only response headers that are exposed to it under the applicable CORS rules.</p><p>For example, if the backend needs frontend JavaScript to read a custom response header:</p><pre>X-Total-Count: 250</pre><p>It may need to send:</p><pre>Access-Control-Expose-Headers: X-Total-Count</pre><p>Without the appropriate exposure, the header might appear in browser Developer Tools but still be unavailable through response.headers.get().</p><p>This is a common source of confusion when working with third-party APIs.</p><h3>8. What is a CORS Preflight Request?</h3><p>You may have seen requests with the HTTP method OPTIONS in the Network tab.</p><p>These can be CORS preflight requests.</p><p>A browser sends a preflight request when a cross-origin request needs permission checks under the CORS rules.</p><p>For example, your frontend might send a JSON POST request with an Authorization header.</p><p>The browser may first send:</p><pre>OPTIONS /products HTTP/1.1<br>Origin: https://myapp.example.com<br>Access-Control-Request-Method: POST<br>Access-Control-Request-Headers: authorization, content-type</pre><p>The server may respond with:</p><pre>HTTP/1.1 204 No Content<br>Access-Control-Allow-Origin: https://myapp.example.com<br>Access-Control-Allow-Methods: GET, POST, OPTIONS<br>Access-Control-Allow-Headers: Authorization, Content-Type</pre><p>The browser checks whether the response satisfies the preflight requirements.</p><p>If the checks pass, it can proceed with the actual request. Otherwise, the browser blocks the cross-origin request from proceeding as requested.</p><p>Not every cross-origin request requires a preflight. Some requests qualify as CORS-safelisted requests and can be sent without one.</p><p>Understanding preflight requests is especially useful when integrating React applications with APIs hosted on different origins.</p><h3>9. How to Inspect Headers in Chrome Developer Tools</h3><p>As a frontend developer, you don’t need to memorize every header. You should know how to inspect them.</p><p>Follow these steps:</p><ol><li>Open your application in Chrome.</li><li>Right-click the page and select <strong>Inspect</strong>.</li><li>Open the <strong>Network</strong> tab.</li><li>Reload the page.</li><li>Click an API request.</li><li>Open the <strong>Headers</strong> section.</li></ol><p>You’ll typically see:</p><p><strong>General</strong></p><ul><li>Request URL</li><li>Request method</li><li>Status code</li></ul><p><strong>Request Headers</strong></p><ul><li>Accept</li><li>Authorization, when present</li><li>Content-Type, when applicable</li><li>Origin, when applicable</li><li>Cookie, when applicable</li></ul><p><strong>Response Headers</strong></p><ul><li>Content-Type</li><li>Cache-Control</li><li>Set-Cookie, when present</li><li>Access-Control-Allow-Origin, when present</li></ul><p>Some headers may be hidden, displayed differently, or unavailable to JavaScript due to browser security rules.</p><h3>A practical debugging example</h3><p>Suppose your React application sends a request, but the API returns a 401 Unauthorized response.</p><p>Inspect the request and check:</p><ul><li>Was the expected authorization credential sent?</li><li>Is the token expired or invalid?</li><li>Does the backend expect a different authentication scheme?</li><li>Does the response contain a WWW-Authenticate header with additional information?</li></ul><p>If the request returns a 415 Unsupported Media Type, inspect the request&#39;s Content-Type and verify that the backend accepts the format you&#39;re sending.</p><p>If JavaScript reports a CORS error, inspect the relevant request and response headers, including any preflight request.</p><p><strong>The Network tab is one of the most useful tools for debugging frontend-backend communication.</strong></p><h3>10. Common Mistakes to Avoid</h3><h4>Mistake 1: Confusing Accept and Content-Type</h4><p>Remember:</p><pre>Accept<br>  → What response format can I accept?</pre><pre>Content-Type<br>  → What format is this message body?</pre><h4>Mistake 2: Assuming CORS is authentication</h4><p>CORS controls whether browser JavaScript can access cross-origin responses under certain conditions.</p><p>It doesn’t determine whether a user is authenticated or authorized to access a protected resource.</p><p>The backend must enforce authentication and authorization independently.</p><h4>Mistake 3: Assuming no-cache means no storage</h4><p>Cache-Control: no-cache generally requires validation before reuse.</p><p>Cache-Control: no-store tells caches not to store the message.</p><p>They are not interchangeable.</p><h4>Mistake 4: Trying to set restricted headers from frontend JavaScript</h4><p>Browsers control certain request headers, such as Cookie and Origin, in relevant contexts.</p><p>You can’t freely override every HTTP header using fetch.</p><h4>Mistake 5: Assuming every response header is readable by JavaScript</h4><p>Cross-origin response headers may need to be explicitly exposed through CORS.</p><p>A header visible in Developer Tools isn’t necessarily accessible through the Fetch API.</p><h4>Mistake 6: Treating headers as a replacement for security controls</h4><p>Sending an Authorization header doesn&#39;t make an endpoint secure by itself.</p><p>The server must validate credentials, enforce permissions, validate input, and apply appropriate security controls.</p><h3>Conclusion</h3><p>HTTP headers are an essential part of browser-server communication.</p><p>They provide metadata that helps clients and servers understand how to interpret messages, authenticate requests, manage caching, handle cookies, and apply security policies.</p><p>In this article, we learned that:</p><ul><li>Request headers provide information about outgoing requests.</li><li>Response headers provide information about server responses.</li><li>Accept describes the response formats the client can accept.</li><li>Content-Type describes the format of a message body.</li><li>Authorization carries authentication credentials.</li><li>Cookie and Set-Cookie support browser cookie handling.</li><li>Cache-Control defines caching behavior.</li><li>CORS headers control browser access to cross-origin responses.</li><li>Security headers such as CSP help enforce browser security policies.</li><li>Chrome Developer Tools makes it easy to inspect and debug headers.</li></ul><p>The key takeaway is simple:</p><p><strong>Don’t treat HTTP headers as random key-value pairs. Understand what each header communicates, who sends it, and how the browser or server uses it.</strong></p><p>Once you understand these fundamentals, debugging API requests and working with authentication, caching, and CORS becomes much easier.</p><h3>What’s Next?</h3><p>Now that you understand HTTP request and response headers, here are some topics worth exploring next:</p><ul><li><strong>HTTP Status Codes:</strong> Understand 200, 201, 301, 400, 401, 403, 404, 409, and 500.</li><li><strong>CORS Explained:</strong> Learn how browsers enforce cross-origin access and why preflight requests occur.</li><li><strong>Cookies vs localStorage vs Session IDs:</strong> Understand the trade-offs of browser authentication storage.</li><li><strong>HTTP Caching:</strong> Explore ETag, Last-Modified, Cache-Control, and conditional requests.</li><li><strong>HTTP vs HTTPS:</strong> Understand TLS, certificates, and how secure communication works.</li><li><strong>API Debugging in Chrome DevTools:</strong> Learn to investigate network failures, request payloads, headers, and response timing.</li></ul><p>Happy coding! ✌️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e61e50080d6d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding Blob in JavaScript: A Practical Guide for Frontend Developers]]></title>
            <link>https://medium.com/@rohitkuwar/understanding-blob-in-javascript-a-practical-guide-for-frontend-developers-9fcd131a50f4?source=rss-faedefd908b4------2</link>
            <guid isPermaLink="false">https://medium.com/p/9fcd131a50f4</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[file-handling]]></category>
            <category><![CDATA[blob]]></category>
            <dc:creator><![CDATA[Rohit Kuwar]]></dc:creator>
            <pubDate>Thu, 29 Oct 2026 02:31:02 GMT</pubDate>
            <atom:updated>2026-10-29T02:31:02.049Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ww-aJ-zNC23t4aBpWkLaMw.png" /><figcaption>Understanding Blob in JavaScript</figcaption></figure><p>If you’ve ever worked with file uploads, downloads, or previews in a web application, you’ve probably come across the term <strong>Blob</strong>. At first glance, it can feel abstract and confusing — but once you understand it, a lot of file-handling logic starts to make perfect sense.</p><p>This blog breaks down what a Blob is, why it matters, and how you’ll use it in real-world frontend development.</p><h3>What is a Blob?</h3><p><strong>Blob</strong> stands for <strong>Binary Large Object</strong>.</p><p>In JavaScript, a Blob is simply a <strong>container for raw binary data</strong>. That data could represent:</p><ul><li>an image</li><li>a PDF</li><li>a video</li><li>or even plain text</li></ul><p>Think of it like this:</p><blockquote><em>A Blob is a box that holds file data in memory — but it’s not necessarily a file yet.</em></blockquote><h3>📦 Blob vs File (Important Difference)</h3><p>A common point of confusion is the difference between a <strong>Blob</strong> and a <strong>File</strong>.</p><pre>Feature                | Blob | File<br>--------------------- | ---- | ----<br>Stores binary data    | ✅   | ✅<br>Has file name         | ❌   | ✅<br>Has metadata          | ❌   | ✅<br>Used in file inputs   | ❌   | ✅</pre><p>👉 In simple terms:</p><pre>File = Blob + filename + metadata</pre><p>You can convert a Blob into a File like this:</p><pre>const blob = new Blob([&quot;Hello world&quot;], { type: &quot;text/plain&quot; });<br>const file = new File([blob], &quot;hello.txt&quot;);</pre><h3>🔄 How Blob Fits into Real Workflows</h3><p>Most real-world scenarios involving files follow a pattern like this:</p><pre>fetch → blob → (optional: file) → upload / preview / download</pre><p>Let’s break that into actual use cases.</p><h3>📥 1. Downloading Files</h3><p>You can fetch a file from a URL, convert it into a Blob, and trigger a download.</p><pre>async function downloadFile(fileUrl) {<br>  const response = await fetch(fileUrl);<br>  const blob = await response.blob();<br>  const url = URL.createObjectURL(blob);<br>  const a = document.createElement(&quot;a&quot;);<br><br>  a.href = url;<br>  a.download = &quot;file&quot;;<br>  a.click();<br>  URL.revokeObjectURL(url);<br>}</pre><p>📌 Use cases:</p><ul><li>downloading reports</li><li>saving images</li><li>exporting documents</li></ul><h3>📤 2. Uploading Files</h3><h4>From file input:</h4><pre>const formData = new FormData();<br>formData.append(&quot;file&quot;, file);<br><br>fetch(&quot;/api/upload&quot;, {<br>  method: &quot;POST&quot;,<br>  body: formData,<br>});</pre><h4>From a URL (very common in integrations):</h4><pre>const response = await fetch(fileUrl);<br>const blob = await response.blob();<br><br>const file = new File([blob], &quot;file-from-url&quot;, {<br>  type: blob.type,<br>});<br>const formData = new FormData();<br>formData.append(&quot;file&quot;, file);</pre><p>📌 This is useful when:</p><ul><li>your app receives file URLs from another API</li><li>you need to re-upload files to your own backend</li></ul><h3>👁️ 3. Previewing Files</h3><p>You can convert a Blob into a temporary URL and display it in the browser.</p><pre>const url = URL.createObjectURL(blob);<br>image.src = url;</pre><p>📌 Use cases:</p><ul><li>image preview before upload</li><li>viewing PDFs</li><li>displaying downloaded content</li></ul><h3>🧾 4. Creating Files on the Fly</h3><p>You can generate files dynamically in the browser using Blob.</p><h4>Example: Create a text file</h4><pre>const blob = new Blob([&quot;Hello from Blob!&quot;], {<br>  type: &quot;text/plain&quot;,<br>});<br><br>const url = URL.createObjectURL(blob);<br>const a = document.createElement(&quot;a&quot;);<br>a.href = url;<br>a.download = &quot;sample.txt&quot;;<br>a.click();<br>URL.revokeObjectURL(url);</pre><p>📌 Use cases:</p><ul><li>exporting CSV or Excel</li><li>downloading JSON data</li><li>generating reports on the client side</li></ul><h3>Important Things to Know</h3><h4>1. Blob is immutable</h4><p>Once created, you can’t modify a Blob. You must create a new one if needed.</p><h4>2. Always check Blob size (debugging)</h4><pre>console.log(blob.size);</pre><p>If size === 0, something is wrong (often a CORS issue).</p><h4>3. CORS can block Blob data</h4><p>If you fetch a file from another domain:</p><ul><li>the server must allow CORS</li><li>otherwise the Blob may be empty or the request may fail</li></ul><h4>4. Memory matters</h4><p>Large files = large blobs = high memory usage.</p><p>👉 Avoid handling very large files in the browser when possible.</p><h4>5. Clean up object URLs</h4><p>Always release memory after using:</p><pre>URL.revokeObjectURL(url);</pre><h3>How Much Should a Frontend Developer Know?</h3><h4>Must Know</h4><ul><li>What a Blob is (binary data container)</li><li>How to get it using fetch</li><li>How to convert Blob → File</li><li>How to use it with FormData</li><li>How to preview using URL.createObjectURL</li></ul><h4>Good to Know</h4><ul><li>MIME types (image/png, application/pdf)</li><li>Debugging with blob.size</li><li>File downloads</li></ul><h4>Advanced (Optional)</h4><ul><li>Streams and chunk uploads</li><li>Media processing</li><li>Performance optimization</li></ul><h3>🚀 Conclusion</h3><p>A Blob is one of those concepts that seems abstract at first but becomes incredibly useful once you start working with files.</p><blockquote><em>Think of Blob as raw file data in memory that you can transform into anything — a downloadable file, an upload payload, or a preview in the browser.</em></blockquote><p>If you’re building modern web apps, especially ones involving uploads, reports, or media, understanding Blob is not optional — it’s essential.</p><p>By practicing simple examples like downloading, uploading, previewing, and creating files, you’ll quickly become comfortable with it — and start using it naturally in your projects.</p><p>Happy coding! ✌️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9fcd131a50f4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[RTK Query: Server State Management in React]]></title>
            <link>https://medium.com/@rohitkuwar/rtk-query-server-state-management-in-react-1edf6f649e8c?source=rss-faedefd908b4------2</link>
            <guid isPermaLink="false">https://medium.com/p/1edf6f649e8c</guid>
            <category><![CDATA[redux]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[redux-toolkit]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Rohit Kuwar]]></dc:creator>
            <pubDate>Tue, 27 Oct 2026 02:31:01 GMT</pubDate>
            <atom:updated>2026-10-27T02:31:01.819Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zU74Wd_ueVXbDC-5TNJVmw.png" /><figcaption>RTK Query in React: Guide to API Fetching, Caching, and Mutations</figcaption></figure><p>Imagine you’re building an e-commerce application.</p><p>Your product listing page fetches products from an API. When the user opens a product details page and then returns to the listing page, should the application fetch the same products again?</p><p>What happens when the user adds a new product, updates an existing record, or deletes an item? How do you keep the UI synchronized with the server?</p><p>You could manage all of this manually using useEffect, useState, and fetch(). But as your application grows, you&#39;ll need to handle loading states, errors, caching, duplicate requests, refetching, and keeping data consistent across components.</p><p>This is where <strong>RTK Query</strong> comes in.</p><p>RTK Query is a powerful data-fetching and caching tool included in Redux Toolkit. It simplifies communication with APIs and helps you manage server data without writing repetitive fetching logic.</p><p>In this article, we’ll understand RTK Query from scratch, build a small React application, learn how queries and mutations work, and explore caching, tags, and cache invalidation.</p><p>If you’ve already read my previous article on Redux Toolkit, you’ll find that RTK Query builds on many of the same concepts.</p><h3>1. First, Understand the Problem</h3><p>Let’s start with a common React pattern.</p><p>Suppose you want to fetch a list of products from an API.</p><p>Without RTK Query, you might write something like this:</p><pre>import { useEffect, useState } from &quot;react&quot;;<br><br>function Products() {<br>  const [products, setProducts] = useState([]);<br>  const [loading, setLoading] = useState(true);<br>  const [error, setError] = useState(null);<br><br>  useEffect(() =&gt; {<br>    async function fetchProducts() {<br>      try {<br>        const response = await fetch(<br>          &quot;https://dummyjson.com/products&quot;<br>        );<br>        if (!response.ok) {<br>          throw new Error(&quot;Failed to fetch products&quot;);<br>        }<br>        const data = await response.json();<br>        setProducts(data.products);<br>      } catch (err) {<br>        setError(err.message);<br>      } finally {<br>        setLoading(false);<br>      }<br>    }<br>    fetchProducts();<br>  }, []);<br><br>  if (loading) return &lt;p&gt;Loading products...&lt;/p&gt;;<br><br>  if (error) return &lt;p&gt;{error}&lt;/p&gt;;<br><br>  return (<br>    &lt;div&gt;<br>      {products.map((product) =&gt; (<br>        &lt;p key={product.id}&gt;{product.title}&lt;/p&gt;<br>      ))}<br>    &lt;/div&gt;<br>  );<br>}<br><br>export default Products;</pre><p>This works perfectly well for a simple example.</p><p>But imagine using the same approach across dozens of components.</p><p>Each component might need to manage:</p><ul><li>Loading states.</li><li>Error handling.</li><li>API requests.</li><li>Response data.</li><li>Caching.</li><li>Refetching.</li><li>Duplicate requests.</li><li>Synchronization after creating, updating, or deleting data.</li></ul><p>You can build reusable hooks and utilities to solve these problems yourself, but you’ll need to maintain that infrastructure.</p><p><strong>The real challenge isn’t making an API request. It’s managing server data consistently across an application.</strong></p><p>RTK Query helps solve this problem.</p><h3>2. What Is RTK Query?</h3><p>RTK Query is a data-fetching and caching solution that comes with Redux Toolkit.</p><p>It provides utilities to define API endpoints, execute requests, cache responses, manage request status, and refresh data when necessary.</p><p>Instead of writing repetitive API logic inside every component, you define your API endpoints in one place and use generated React hooks to interact with them.</p><p>Conceptually, the architecture looks like this:</p><pre>             React Components<br>                    |<br>                    v<br>               RTK Query<br>                    |<br>          +---------+---------+<br>          |                   |<br>        Queries            Mutations<br>          |                   |<br>          v                   v<br>      Fetch Data          Change Data<br>          |                   |<br>          +---------+---------+<br>                    |<br>                    v<br>                API Server<br>                    |<br>                    v<br>             Cached API Data<br>                    |<br>                    v<br>             Updated React UI</pre><p>For example, you might define these operations:</p><pre>getProducts     → Fetch all products<br>getProductById  → Fetch one product<br>addProduct      → Create a product<br>updateProduct   → Update a product<br>deleteProduct   → Delete a product</pre><p>RTK Query helps manage the lifecycle of these operations.</p><h4>What does RTK Query provide?</h4><ul><li>Automatically generated React hooks.</li><li>Loading, success, and error states.</li><li>Caching of query results.</li><li>Deduplication of equivalent in-flight queries.</li><li>Refetching when configured conditions are met.</li><li>Cache invalidation after mutations.</li><li>Request cancellation and lifecycle management.</li><li>Integration with the Redux store.</li></ul><p>The key idea is simple:</p><p><strong>You describe what data your application needs and how to fetch or modify it. RTK Query handles much of the repetitive API state management for you.</strong></p><h3>3. Query vs Mutation</h3><p>Before writing code, you need to understand two important concepts: queries and mutations.</p><h4>3.1 What is a Query?</h4><p>A query is used to retrieve data from an API.</p><p>For example:</p><pre>GET /products<br>GET /products/1<br>GET /users<br>GET /orders</pre><p>In RTK Query, we define a query using builder.query().</p><p>For example:</p><pre>getProducts: builder.query({<br>  query: () =&gt; &quot;/products&quot;<br>})</pre><p>RTK Query uses this endpoint definition to make the request and manage its result.</p><p>When you use the generated hook, it provides information such as:</p><ul><li>data — The latest available result.</li><li>isLoading — The initial request is loading and no data is available yet.</li><li>isFetching — A request is currently in progress, including background refetches.</li><li>error — Information about a failed request, when present.</li></ul><p>Queries are generally used for reading data.</p><h4>3.2 What is a Mutation?</h4><p>A mutation is used for operations that create, update, or delete data.</p><p>For example:</p><pre>POST   /products/add<br>PUT    /products/1<br>PATCH  /products/1<br>DELETE /products/1</pre><p>In RTK Query, we define a mutation using builder.mutation().</p><p>For example:</p><pre>addProduct: builder.mutation({<br>  query: (newProduct) =&gt; ({<br>    url: &quot;/products/add&quot;,<br>    method: &quot;POST&quot;,<br>    body: newProduct<br>  })<br>})</pre><p>A mutation generates a hook that you can call when a user performs an action.</p><p>For example, clicking an Add Product button can trigger the mutation.</p><h4>Query vs Mutation</h4><pre>+----------------------+---------------------------+<br>| Query                | Mutation                  |<br>+----------------------+---------------------------+<br>| Reads data           | Changes data              |<br>| builder.query()      | builder.mutation()        |<br>| Often runs on mount  | Usually triggered manually|<br>| Results are cached   | Results aren&#39;t query cache|<br>| Can refetch           | Can invalidate query cache|<br>+----------------------+---------------------------+</pre><p>This is a simplified comparison. Queries can also be triggered manually using lazy query hooks, and mutation results can be retained in their own mutation state. Mutations commonly trigger refetching by invalidating related query cache entries.</p><h3>4. Build Your First RTK Query Application</h3><p>Let’s build a small product listing application using React and RTK Query.</p><p>We’ll use the DummyJSON API:</p><p><a href="https://dummyjson.com/products">https://dummyjson.com/products</a></p><p>Our application will:</p><ol><li>Fetch products.</li><li>Display a loading message.</li><li>Handle errors.</li><li>Display the product list.</li><li>Add a product using a mutation.</li><li>Demonstrate cache invalidation.</li></ol><p>We’ll use Vite for the React project.</p><h4>Step 1: Create the React project</h4><p>Run the following commands:</p><pre>npm create vite@latest rtk-query-demo -- --template react<br>cd rtk-query-demo<br>npm install<br>npm install @reduxjs/toolkit react-redux<br>npm run dev</pre><p>We install two important packages:</p><p><strong>@reduxjs/toolkit</strong> — Provides RTK Query and Redux Toolkit utilities.</p><p><strong>react-redux</strong> — Connects React components to the Redux store.</p><p>Now let’s build the application.</p><h4>Step 2: Create the API slice</h4><p>Create a file named src/services/productsApi.js.</p><pre>import { createApi, fetchBaseQuery } from &quot;@reduxjs/toolkit/query/react&quot;;<br><br>export const productsApi = createApi({<br>  reducerPath: &quot;productsApi&quot;,<br>  baseQuery: fetchBaseQuery({<br>    baseUrl: &quot;https://dummyjson.com&quot;<br>  }),<br>  tagTypes: [&quot;Product&quot;],<br>  endpoints: (builder) =&gt; ({<br>    getProducts: builder.query({<br>      query: () =&gt; &quot;/products&quot;,<br>      providesTags: (result) =&gt;<br>        result<br>          ? [<br>              ...result.products.map(({ id }) =&gt; ({<br>                type: &quot;Product&quot;,<br>                id<br>              })),<br>              { type: &quot;Product&quot;, id: &quot;LIST&quot; }<br>            ]<br>          : [{ type: &quot;Product&quot;, id: &quot;LIST&quot; }]<br>    }),<br>    addProduct: builder.mutation({<br>      query: (newProduct) =&gt; ({<br>        url: &quot;/products/add&quot;,<br>        method: &quot;POST&quot;,<br>        body: newProduct<br>      }),<br>      invalidatesTags: [<br>        { type: &quot;Product&quot;, id: &quot;LIST&quot; }<br>      ]<br>    })<br>  })<br>});<br><br>export const {<br>  useGetProductsQuery,<br>  useAddProductMutation<br>} = productsApi;</pre><p>This is the heart of our RTK Query implementation.</p><p>Let’s understand each part.</p><h4>What is createApi()?</h4><pre>createApi({<br>  reducerPath: &quot;productsApi&quot;,<br>  baseQuery: fetchBaseQuery({<br>    baseUrl: &quot;https://dummyjson.com&quot;<br>  }),<br>  endpoints: (builder) =&gt; ({})<br>});</pre><p>createApi() creates an API service that defines how requests are made, how query results are cached, and how endpoints behave.</p><p>It also generates hooks for the endpoints you define.</p><h4>What is reducerPath?</h4><pre>reducerPath: &quot;productsApi&quot;</pre><p>This specifies the key under which RTK Query’s reducer will be registered in the Redux store.</p><p>It also identifies the API slice’s state namespace.</p><h4>What is fetchBaseQuery()?</h4><pre>baseQuery: fetchBaseQuery({<br>  baseUrl: &quot;https://dummyjson.com&quot;<br>})</pre><p>fetchBaseQuery() is a lightweight wrapper around the browser&#39;s Fetch API.</p><p>It simplifies common request handling, including constructing URLs and processing responses.</p><p>Because we configured a base URL, we can write:</p><pre>query: () =&gt; &quot;/products&quot;</pre><p>instead of repeating the entire URL in every endpoint.</p><h4>What are endpoints?</h4><p>The endpoints property defines the API operations available to our application.</p><p>In this example, we define two endpoints:</p><ul><li>getProducts — Fetches the product list.</li><li>addProduct — Sends a request to create a product.</li></ul><p>RTK Query generates React hooks from these definitions.</p><p>For example:</p><pre>useGetProductsQuery<br>useAddProductMutation</pre><p>These hooks let React components interact with the API.</p><h3>5. Configure the Redux Store</h3><p>Creating the API slice isn’t enough. We also need to register its reducer and middleware in the Redux store.</p><p>Create src/app/store.js.</p><pre>import { configureStore } from &quot;@reduxjs/toolkit&quot;;<br>import { productsApi } from &quot;../services/productsApi&quot;;<br><br>export const store = configureStore({<br>  reducer: {<br>    [productsApi.reducerPath]: productsApi.reducer<br>  },<br>  middleware: (getDefaultMiddleware) =&gt;<br>    getDefaultMiddleware().concat(<br>      productsApi.middleware<br>    )<br>});</pre><p>Let’s understand the two important pieces.</p><h4>Registering the API reducer</h4><pre>[productsApi.reducerPath]: productsApi.reducer</pre><p>This registers the API slice’s reducer under its configured state key.</p><p>RTK Query uses this state to maintain query results, request status, and cache information.</p><h4>Registering the API middleware</h4><pre>middleware: (getDefaultMiddleware) =&gt;<br>  getDefaultMiddleware().concat(<br>    productsApi.middleware<br>  )</pre><p>The middleware handles important RTK Query behavior, including query subscriptions, cache lifetimes, request lifecycle management, and invalidation.</p><p><strong>Both the reducer and middleware are required for the standard RTK Query setup.</strong></p><h3>6. Provide the Store to React</h3><p>Open src/main.jsx and update it:</p><pre>import { StrictMode } from &quot;react&quot;;<br>import { createRoot } from &quot;react-dom/client&quot;;<br>import { Provider } from &quot;react-redux&quot;;<br>import App from &quot;./App&quot;;<br>import { store } from &quot;./app/store&quot;;<br><br>createRoot(document.getElementById(&quot;root&quot;)).render(<br>  &lt;StrictMode&gt;<br>    &lt;Provider store={store}&gt;<br>      &lt;App /&gt;<br>    &lt;/Provider&gt;<br>  &lt;/StrictMode&gt;<br>);</pre><p>The Redux Provider makes the store available to React components.</p><p>Since RTK Query is integrated into that store, its generated hooks can now be used in our components.</p><h3>7. Fetch Products Using useGetProductsQuery()</h3><p>Now create or update src/App.jsx.</p><pre>import {<br>  useGetProductsQuery,<br>  useAddProductMutation<br>} from &quot;./services/productsApi&quot;;<br><br>function App() {<br>  const { data, error, isLoading, isFetching } = useGetProductsQuery();<br>  const [ addProduct, { isLoading: isAdding } ] = useAddProductMutation();<br><br>  const handleAddProduct = async () =&gt; {<br>    try {<br>      await addProduct({<br>        title: &quot;New Keyboard&quot;,<br>        price: 2500<br>      }).unwrap();<br>      alert(&quot;Product added successfully!&quot;);<br>    } catch (error) {<br>      alert(&quot;Failed to add product&quot;);<br>    }<br>  };<br><br>  if (isLoading) {<br>    return &lt;p&gt;Loading products...&lt;/p&gt;;<br>  }<br><br>  if (error) {<br>    return &lt;p&gt;Failed to load products.&lt;/p&gt;;<br>  }<br><br>  return (<br>    &lt;div&gt;<br>      &lt;h1&gt;RTK Query Demo&lt;/h1&gt;<br>      {isFetching &amp;&amp; &lt;p&gt;Refreshing products...&lt;/p&gt;}<br>      &lt;button<br>        onClick={handleAddProduct}<br>        disabled={isAdding}<br>      &gt;<br>        {isAdding ? &quot;Adding...&quot; : &quot;Add Product&quot;}<br>      &lt;/button&gt;<br>      &lt;h2&gt;Products&lt;/h2&gt;<br>      {data.products.map((product) =&gt; (<br>        &lt;div key={product.id}&gt;<br>          &lt;h3&gt;{product.title}&lt;/h3&gt;<br>          &lt;p&gt;Price: ${product.price}&lt;/p&gt;<br>        &lt;/div&gt;<br>      ))}<br>    &lt;/div&gt;<br>  );<br>}<br><br>export default App;</pre><p>Let’s break this down.</p><h4>Reading data</h4><pre>const {<br>  data,<br>  error,<br>  isLoading,<br>  isFetching<br>} = useGetProductsQuery();</pre><p>Calling useGetProductsQuery() subscribes the component to the query identified by the endpoint and its arguments.</p><p>Because our endpoint doesn’t require an argument, we call the hook without one.</p><p>RTK Query starts the request when needed and provides the current query state.</p><h4>Understanding isLoading and isFetching</h4><p>These properties are related, but they aren’t identical.</p><p><strong>isLoading</strong></p><p>Indicates the initial loading state when the query has no data available yet.</p><p><strong>isFetching</strong></p><p>Indicates that a request is in progress. This can include background refetches when previous data is already available.</p><p>For example, you might use isLoading to display a full-page loading indicator and isFetching to display a smaller refreshing message.</p><h4>Adding a product</h4><pre>await addProduct({<br>  title: &quot;New Keyboard&quot;,<br>  price: 2500<br>}).unwrap();</pre><p>The generated mutation hook provides a trigger function.</p><p>Calling addProduct() starts the mutation request.</p><p>The .unwrap() method lets us handle the fulfilled result or catch a rejected request using normal try...catch syntax.</p><p>One important note: DummyJSON simulates product creation. It returns a sample response but does not permanently save the new product on its server. This example demonstrates the request and mutation workflow, not persistent product creation.</p><h3>8. How Does RTK Query Caching Work?</h3><p>Caching is one of the most useful RTK Query features.</p><p>Imagine a user opens the product listing page and RTK Query fetches the products.</p><p>The results are stored in the Redux store’s API cache.</p><p>Later, the user navigates to another page and then returns to the product listing page.</p><p>If the same query is still cached and no refetch condition requires a new request, RTK Query can reuse the cached data instead of immediately fetching it again.</p><p>Conceptually:</p><pre>User opens Products Page<br>          |<br>          v<br>RTK Query requests products<br>          |<br>          v<br>Server returns product list<br>          |<br>          v<br>RTK Query caches the result<br>          |<br>          v<br>User navigates away<br>          |<br>          v<br>User returns to Products Page<br>          |<br>          v<br>Cached data may be reused</pre><p>The exact behavior depends on the query arguments, subscription state, cache lifetime, and refetch configuration.</p><h4>What identifies a cached query?</h4><p>RTK Query identifies query cache entries using the endpoint name and its serialized arguments.</p><p>For example:</p><pre>useGetProductByIdQuery(1)</pre><p>and:</p><pre>useGetProductByIdQuery(2)</pre><p>represent different queries because their arguments differ.</p><p>But two components using:</p><pre>useGetProductByIdQuery(1)</pre><p>can share the same cache entry.</p><p>This helps avoid unnecessary duplicate requests and lets multiple components use the same cached result.</p><h4>How long does cached data stay?</h4><p>By default, RTK Query keeps unused query data cached for 60 seconds after the last subscriber unsubscribes.</p><p>This behavior is controlled by keepUnusedDataFor, which can be configured globally or per endpoint.</p><p>For example:</p><pre>getProducts: builder.query({<br>  query: () =&gt; &quot;/products&quot;,<br>  keepUnusedDataFor: 120<br>})</pre><p>This keeps unused cached data for up to 120 seconds after the last subscription ends.</p><p>Remember that cache lifetime and data freshness are different concepts. Keeping data in the cache doesn’t automatically mean it will be refetched every time the component mounts.</p><h3>9. What Are providesTags and invalidatesTags?</h3><p>Now let’s explore one of RTK Query’s most important features: cache invalidation.</p><p>Imagine the product listing page has already fetched and cached the products.</p><p>The user then adds a product, updates an existing product, or deletes a product.</p><p>The cached list might no longer represent the current server data.</p><p>How do we tell RTK Query which cached queries should be refreshed?</p><p>This is where tags help.</p><h4>What is providesTags?</h4><p>A query can use providesTags to identify the data represented by its cached result.</p><p>For example:</p><pre>getProducts: builder.query({<br>  query: () =&gt; &quot;/products&quot;,<br>  providesTags: [<br>      { type: &quot;Product&quot;, id: &quot;LIST&quot; }<br>    ]<br>})</pre><p>This indicates that the query provides the Product list tag.</p><p>Tags are metadata used by RTK Query to track relationships between queries and mutations.</p><h4>What is invalidatesTags?</h4><p>A mutation can use invalidatesTags to indicate that some cached data may now be stale.</p><p>For example:</p><pre>addProduct: builder.mutation({<br>  query: (newProduct) =&gt; ({<br>    url: &quot;/products/add&quot;,<br>    method: &quot;POST&quot;,<br>    body: newProduct<br>  }),<br>  invalidatesTags: [<br>      { type: &quot;Product&quot;, id: &quot;LIST&quot; }<br>    ]<br>})</pre><p>When the mutation succeeds, RTK Query invalidates the specified tag.</p><p>If an active query provides that tag, RTK Query will normally refetch it so the component can receive updated data.</p><p>The flow looks like this:</p><pre>Product List Query<br>        |<br>        v<br>Provides Product LIST Tag<br>        |<br>        v<br>User Adds a Product<br>        |<br>        v<br>Mutation Succeeds<br>        |<br>        v<br>Product LIST Tag Invalidated<br>        |<br>        v<br>Active Product Query Refetches<br>        |<br>        v<br>UI Receives Updated Data</pre><p>This is the relationship between providesTags and invalidatesTags.</p><p><strong>providesTags connects cached queries to data categories. </strong><strong>invalidatesTags tells RTK Query which cached data may need refreshing after a mutation.</strong></p><h4>A note about our DummyJSON example</h4><p>Because DummyJSON doesn’t persist the newly created product, refetching its product list won’t necessarily show the simulated new product.</p><p>In a real backend that saves the product, the refetched query can return the updated list.</p><p>This distinction is important: cache invalidation triggers data synchronization, but it cannot make a backend persist data that it never saved.</p><h3>10. How Does RTK Query Differ from createAsyncThunk()?</h3><p>If you’ve read my Redux Toolkit article, you might remember createAsyncThunk().</p><p>Both createAsyncThunk() and RTK Query support asynchronous operations, but they solve different problems.</p><h4>createAsyncThunk()</h4><p>createAsyncThunk() helps you manage asynchronous workflows.</p><p>For example, you can fetch products, dispatch pending/fulfilled/rejected actions, and store the result in a slice.</p><p>However, you generally need to implement the surrounding state management yourself, including caching and synchronization when required.</p><h4>RTK Query</h4><p>RTK Query is designed specifically for data fetching and caching.</p><p>It provides endpoint definitions, generated hooks, query caching, subscription management, and cache invalidation.</p><p>Here’s a quick comparison:</p><pre>+----------------------+-------------------------+<br>| Feature              | createAsyncThunk        |<br>+----------------------+-------------------------+<br>| Main purpose         | Async workflows         |<br>| Generated API hooks  | No                      |<br>| Query caching        | Manual implementation   |<br>| Cache invalidation   | Manual implementation   |<br>| Request status       | Lifecycle actions       |<br>| State integration    | Usually slice reducers  |<br>+----------------------+-------------------------+<br><br>+----------------------+-------------------------+<br>| Feature              | RTK Query               |<br>+----------------------+-------------------------+<br>| Main purpose         | API data management     |<br>| Generated API hooks  | Yes, with React entry   |<br>| Query caching        | Built in                |<br>| Cache invalidation   | Tag-based               |<br>| Request status       | Managed by API slice    |<br>| State integration    | Built into Redux store  |<br>+----------------------+-------------------------+</pre><p>Use createAsyncThunk() when you need a custom asynchronous workflow that doesn&#39;t fit naturally into query caching.</p><p>Use RTK Query when you’re managing API data that benefits from fetching, caching, refetching, and synchronization.</p><p>You can also use both in the same application when their responsibilities are clearly separated.</p><h3>11. Useful RTK Query Features to Know</h3><p>Once you’re comfortable with basic queries and mutations, these features are worth exploring.</p><h4>Refetch on mount or argument change</h4><p>You can configure a query to refetch when a component subscribes or its arguments change:</p><pre>useGetProductsQuery(undefined, {<br>  refetchOnMountOrArgChange: true<br>});</pre><p>This tells RTK Query to refetch when the hook mounts or its argument changes, subject to its query subscription behavior.</p><p>You can also use a numeric value to refetch when cached data is older than a specified number of seconds.</p><h4>Refetch on focus</h4><pre>useGetProductsQuery(undefined, {<br>  refetchOnFocus: true<br>});</pre><p>This enables refetching when the browser window regains focus.</p><p>To use focus and reconnect refetching, enable listener support once in your application entry point:</p><pre>import { setupListeners } from &quot;@reduxjs/toolkit/query&quot;;<br><br>setupListeners(store.dispatch);</pre><h4>Refetch on reconnect</h4><pre>useGetProductsQuery(undefined, {<br>  refetchOnReconnect: true<br>});</pre><p>This enables refetching when the browser regains network connectivity, with listener support configured.</p><h4>Polling</h4><p>For data that needs periodic updates, RTK Query supports polling:</p><pre>useGetProductsQuery(undefined, {<br>  pollingInterval: 10000<br>});</pre><p>This requests fresh data every 10 seconds while the subscription is active, subject to the configured polling behavior.</p><p>Polling can be useful for dashboards and status pages, but choose an interval appropriate for the server and application.</p><p>For true real-time communication, technologies such as WebSockets or Server-Sent Events may be more suitable.</p><h3>12. Common Mistakes Beginners Should Avoid</h3><h4>Mistake 1: Forgetting to register the API middleware</h4><p>Registering only the API reducer isn’t enough.</p><p>For the standard RTK Query setup, you need both:</p><pre>[productsApi.reducerPath]: productsApi.reducer</pre><p>and:</p><pre>productsApi.middleware</pre><h4>Mistake 2: Confusing queries and mutations</h4><p>Queries are primarily for retrieving data, while mutations represent operations that change server data.</p><p>Choosing the appropriate endpoint type makes the API behavior easier to understand.</p><h4>Mistake 3: Expecting cache invalidation to modify the server</h4><p>Invalidating a tag doesn’t create or update server data.</p><p>It marks related cached data as potentially stale and can trigger refetching.</p><p>The backend remains responsible for storing and returning the actual data.</p><h4>Mistake 4: Treating caching as permanent storage</h4><p>RTK Query’s cache is designed to manage API data during the application’s runtime.</p><p>It doesn’t automatically provide permanent storage across browser restarts.</p><h4>Mistake 5: Refetching everything after every mutation</h4><p>You don’t always need to invalidate every tag.</p><p>Use specific tags when possible so that only the relevant cached queries are refreshed.</p><p>For example, updating one product can invalidate that product’s individual tag instead of invalidating the entire product list.</p><h4>Mistake 6: Forgetting error handling</h4><p>API requests can fail because of network problems, server errors, invalid credentials, or unexpected responses.</p><p>Always design appropriate loading, error, and retry behavior for your application.</p><h3>Conclusion</h3><p>RTK Query simplifies API data management in Redux Toolkit applications.</p><p>We’ve learned that:</p><ul><li>RTK Query is designed for data fetching and caching.</li><li>createApi() defines API endpoints and generates hooks.</li><li>fetchBaseQuery() provides a lightweight Fetch API wrapper.</li><li>Queries retrieve data.</li><li>Mutations create, update, or delete data.</li><li>The API reducer and middleware must be registered in the Redux store.</li><li>RTK Query caches query results and can share them across components.</li><li>providesTags and invalidatesTags help synchronize cached data after mutations.</li><li>createAsyncThunk() and RTK Query solve different asynchronous data-management problems.</li></ul><p>The key takeaway is simple:</p><p><strong>RTK Query helps you manage server data without manually rebuilding fetching, caching, loading-state, and cache-invalidation logic for every API endpoint.</strong></p><p>Once you understand these concepts, you can use RTK Query to build cleaner and more maintainable React applications.</p><h3>What’s Next?</h3><p>Now that you understand RTK Query, here are some topics worth exploring next:</p><ul><li><strong>RTK Query with TypeScript:</strong> Type your API responses, query arguments, and mutations.</li><li><strong>Cache Invalidation in Depth:</strong> Learn how to invalidate individual records and lists.</li><li><strong>RTK Query Pagination:</strong> Fetch data in pages or implement infinite scrolling.</li><li><strong>Optimistic Updates:</strong> Update the UI before the server confirms a mutation.</li><li><strong>RTK Query Authentication:</strong> Attach access tokens to requests and handle authentication errors.</li><li><strong>RTK Query vs React Query:</strong> Compare two popular server-state management solutions.</li></ul><p>Happy coding! ✌️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1edf6f649e8c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Redux Toolkit: Client State Management in React]]></title>
            <link>https://medium.com/@rohitkuwar/redux-toolkit-client-state-management-in-react-f44ede0b6224?source=rss-faedefd908b4------2</link>
            <guid isPermaLink="false">https://medium.com/p/f44ede0b6224</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[redux-toolkit]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[redux]]></category>
            <dc:creator><![CDATA[Rohit Kuwar]]></dc:creator>
            <pubDate>Sun, 25 Oct 2026 02:31:02 GMT</pubDate>
            <atom:updated>2026-10-25T02:31:02.133Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qM1gLMy2ilVZHC1102wHkg.png" /><figcaption>Redux Toolkit in React: Guide to State Management</figcaption></figure><p>Imagine you’re building an e-commerce application.</p><p>The application has a product listing page, a shopping cart, a navigation bar showing the cart count, and a checkout page.</p><p>When a user adds a product to the cart, the cart page should update. The navigation bar should also display the updated number of items, and the checkout page should have access to the cart information.</p><p>How do you share this data across different components without passing props through multiple layers?</p><p>React provides Context API and other state management techniques, but as an application grows, managing shared state can become more complicated.</p><p>This is where <strong>Redux Toolkit</strong> comes in.</p><p>Redux Toolkit provides a structured way to manage shared application state in React. It simplifies Redux by providing utilities that reduce boilerplate code and make state updates easier to organize.</p><p>In this article, we’ll understand Redux Toolkit from scratch, learn its core concepts, build a working counter application, and then explore how the same approach can be used in a real-world shopping cart.</p><p>No prior knowledge of Redux is required.</p><h3>1. First, Understand the Problem</h3><p>Let’s start with a simple React application.</p><p>Imagine we have the following component structure:</p><pre>App<br> |<br> +-- Navbar<br> |     |<br> |     +-- CartCount<br> |<br> +-- ProductList<br> |     |<br> |     +-- ProductCard<br> |<br> +-- CartPage</pre><p>Suppose the user clicks the <strong>Add to Cart</strong> button inside ProductCard.</p><p>We want three things to happen:</p><ul><li>The product should be added to the cart.</li><li>The cart count in the navbar should increase.</li><li>The cart page should display the updated cart contents.</li></ul><p>Where should we store the cart data?</p><p>One option is to maintain the state inside a parent component and pass it down through props.</p><pre>App<br> |<br> |-- cart state<br> |<br> +-- Navbar<br> |     |<br> |     +-- CartCount (cart count)<br> |<br> +-- ProductList<br> |     |<br> |     +-- ProductCard (add to cart)<br> |<br> +-- CartPage (cart items)</pre><p>This works, and for small applications, it can be a perfectly reasonable approach.</p><p>However, as more components need access to the same data, you may end up passing props through components that don’t actually need that data.</p><p>This is commonly called <strong>prop drilling</strong>.</p><p>Another option is React Context, which can help share data without passing props through every intermediate component.</p><p>But as an application grows, you may also want a predictable way to organize state updates, manage complex interactions, and inspect how the state changes over time.</p><p>Redux Toolkit provides a structured approach to these problems.</p><h3>2. What Is Redux Toolkit?</h3><p>Redux Toolkit (RTK) is the official recommended way to write Redux logic.</p><p>Redux is a state management library that maintains application state in a centralized store. Redux Toolkit simplifies the process of creating that store, defining state updates, and dispatching actions.</p><p>Instead of every component maintaining its own copy of shared data, components can read from a common store.</p><p>Conceptually, the architecture looks like this:</p><pre>React Components<br>                    |<br>          +---------+---------+<br>          |                   |<br>      Read State          Dispatch Action<br>      useSelector         useDispatch<br>          |                   |<br>          v                   v<br>             Redux Store<br>                  |<br>                  v<br>          Reducer Processes<br>               Action<br>                  |<br>                  v<br>            Updated State<br>                  |<br>                  v<br>         React UI Re-renders</pre><p>Let’s understand the flow in simple terms.</p><ol><li>A component reads the state it needs from the Redux store.</li><li>A user performs an action, such as clicking a button.</li><li>The component dispatches an action.</li><li>A reducer determines how the state should change.</li><li>The store updates its state.</li><li>Components subscribed to the relevant state update and React renders the new UI.</li></ol><p><strong>The key idea is that shared application state lives in one central place, and updates follow a predictable pattern.</strong></p><p>Redux Toolkit doesn’t replace React’s local state. You can still use useState for component-specific data such as whether a dropdown is open or which input field is being edited.</p><p>Redux Toolkit is most useful when state needs to be shared across components or when centralized state management makes the application easier to maintain.</p><h3>3. Core Concepts You Need to Understand</h3><p>Before writing code, let’s understand six important concepts.</p><h4>3.1 Store</h4><p>The store is the central place where Redux maintains your application’s state.</p><p>For example:</p><pre>{<br>  counter: {<br>    value: 10<br>  },<br>  cart: {<br>    items: []<br>  }<br>}</pre><p>The actual structure depends on how you configure your application.</p><p>In Redux Toolkit, we commonly create the store using configureStore().</p><h4>3.2 State</h4><p>State is the data maintained by the store.</p><p>For example:</p><pre>{<br>  value: 0<br>}</pre><p>If we’re building a counter application, the state might contain a single number.</p><p>If we’re building an e-commerce application, it might contain cart items, selected filters, or other shared data.</p><h4>3.3 Action</h4><p>An action describes something that happened.</p><p>Examples include:</p><pre>Increment counter<br>Decrement counter<br>Add product to cart<br>Remove product from cart</pre><p>An action is represented as an object containing a type and, optionally, a payload.</p><p>For example:</p><pre>{<br>  type: &quot;counter/increment&quot;<br>}</pre><p>An action carrying a product might look like this:</p><pre>{<br>  type: &quot;cart/addItem&quot;,<br>  payload: {<br>    id: 1,<br>    name: &quot;Laptop&quot;,<br>    price: 65000<br>  }<br>}</pre><p>The payload carries additional data needed to process the action.</p><h4>3.4 Reducer</h4><p>A reducer is a function that determines how state should change in response to an action.</p><p>Conceptually:</p><pre>Previous State + Action<br>           |<br>           v<br>         Reducer<br>           |<br>           v<br>       Next State</pre><p>For example, if the current counter value is 5 and an increment action occurs, the next value becomes 6.</p><p>With Redux Toolkit, reducers are commonly defined inside createSlice().</p><h4>3.5 Dispatch</h4><p>Dispatching means sending an action to the Redux store.</p><p>In a React component, we commonly use useDispatch():</p><pre>const dispatch = useDispatch();<br><br>dispatch(increment());</pre><p>This sends the increment action to the store so the corresponding reducer can process it.</p><h4>3.6 Selector</h4><p>A selector reads the state you need from the store.</p><p>In React, we commonly use useSelector():</p><pre>const count = useSelector(<br>  (state) =&gt; state.counter.value<br>);</pre><p>The component subscribes to the selected state. When the selected value changes, React Redux can trigger a re-render.</p><p>These concepts form the foundation of Redux Toolkit.</p><h3>4. What Is createSlice()?</h3><p>One of the most useful Redux Toolkit functions is createSlice().</p><p>In traditional Redux, developers often had to write separate action types, action creators, and reducer logic.</p><p>For example, incrementing a counter could require several pieces of code:</p><pre>const INCREMENT = &quot;counter/increment&quot;;<br><br>function increment() {<br>  return {<br>    type: INCREMENT<br>  };<br>}<br><br>function counterReducer(state = { value: 0 }, action) {<br>  switch (action.type) {<br>    case INCREMENT:<br>      return {<br>        ...state,<br>        value: state.value + 1<br>      };<br>    default:<br>      return state;<br>  }<br>}</pre><p>This works, but writing all the pieces manually can introduce repetitive code.</p><p>Redux Toolkit simplifies this with createSlice():</p><pre>import { createSlice } from &quot;@reduxjs/toolkit&quot;;<br><br>const counterSlice = createSlice({<br>  name: &quot;counter&quot;,<br>  initialState: {<br>    value: 0<br>  },<br>  reducers: {<br>    increment: (state) =&gt; {<br>      state.value += 1;<br>    }<br>  }<br>});</pre><p>Notice that we only define the slice name, initial state, and reducers.</p><p>Redux Toolkit automatically generates:</p><ul><li>Action creators.</li><li>Action types.</li><li>The slice reducer.</li></ul><p>For example:</p><pre>counterSlice.actions.increment();</pre><p>creates the increment action.</p><p>And:</p><pre>counterSlice.reducer;</pre><p>provides the reducer function that processes the action.</p><h4>Why can we write state.value += 1?</h4><p>You may remember that Redux state should not be mutated directly.</p><p>So why is this allowed?</p><p>Redux Toolkit uses <strong>Immer</strong> internally to let you write reducer logic using a mutation-like syntax.</p><p>Immer tracks the changes and produces the next immutable state.</p><p>This means the following is valid inside a Redux Toolkit reducer:</p><pre>increment: (state) =&gt; {<br>  state.value += 1;<br>}</pre><p>However, this does not mean you can mutate Redux state directly inside a React component.</p><p>The mutation-like syntax is safe because Redux Toolkit handles it inside its reducer logic.</p><h3>5. Build a Counter Application Using Redux Toolkit</h3><p>Let’s build a simple counter application in React.</p><p>Our application will support three operations:</p><ul><li>Increment the count.</li><li>Decrement the count.</li><li>Reset the count.</li></ul><p>We’ll use Vite for the React project.</p><h4>Step 1: Create the React project</h4><p>Run:</p><pre>npm create vite@latest redux-toolkit-demo -- --template react<br>cd redux-toolkit-demo<br>npm install<br>npm install @reduxjs/toolkit react-redux<br>npm run dev</pre><p>We install two important packages:</p><p><strong>@reduxjs/toolkit</strong></p><p>Provides configureStore(), createSlice(), and other Redux Toolkit utilities.</p><p><strong>react-redux</strong></p><p>Connects React components to the Redux store through components and hooks such as Provider, useSelector(), and useDispatch().</p><h4>Step 2: Create the counter slice</h4><p>Create src/features/counter/counterSlice.js:</p><pre>import { createSlice } from &quot;@reduxjs/toolkit&quot;;<br><br>const initialState = {<br>  value: 0<br>};<br><br>const counterSlice = createSlice({<br>  name: &quot;counter&quot;,<br>  initialState,<br>  reducers: {<br>    increment: (state) =&gt; {<br>      state.value += 1;<br>    },<br>    decrement: (state) =&gt; {<br>      state.value -= 1;<br>    },<br>    reset: (state) =&gt; {<br>      state.value = 0;<br>    }<br>  }<br>});<br><br>export const {<br>  increment,<br>  decrement,<br>  reset<br>} = counterSlice.actions;<br><br>export default counterSlice.reducer;</pre><p>Let’s understand this file.</p><p><strong>name</strong></p><pre>name: &quot;counter&quot;</pre><p>Identifies the slice and is used to generate action type prefixes.</p><p>For example, the increment action has the type:</p><pre>counter/increment</pre><p><strong>initialState</strong></p><pre>const initialState = {<br>  value: 0<br>};</pre><p>Defines the initial state for the counter.</p><p><strong>reducers</strong></p><pre>reducers: {<br>  increment: (state) =&gt; {<br>    state.value += 1;<br>  }<br>}</pre><p>Defines how the state changes when the increment action is dispatched.</p><p><strong>Exporting actions</strong></p><pre>export const {<br>  increment,<br>  decrement,<br>  reset<br>} = counterSlice.actions;</pre><p>These generated action creators can be imported into React components.</p><p><strong>Exporting the reducer</strong></p><pre>export default counterSlice.reducer;</pre><p>The reducer will be registered in the Redux store.</p><h4>Step 3: Create the Redux store</h4><p>Create src/app/store.js:</p><pre>import { configureStore } from &quot;@reduxjs/toolkit&quot;;<br>import counterReducer from &quot;../features/counter/counterSlice&quot;;<br><br>export const store = configureStore({<br>  reducer: {<br>    counter: counterReducer<br>  }<br>});</pre><p>Here, configureStore() creates the Redux store.</p><p>The reducer object registers our counter reducer under the key counter.</p><p>This determines how the state is organized.</p><p>For example, the state will look like this:</p><pre>{<br>  counter: {<br>    value: 0<br>  }<br>}</pre><p>This is why we later access the counter using:</p><pre>state.counter.value</pre><p>If we register additional slices, they can be added to this reducer object.</p><h4>Step 4: Provide the store to React</h4><p>Open src/main.jsx and update it:</p><pre>import { StrictMode } from &quot;react&quot;;<br>import { createRoot } from &quot;react-dom/client&quot;;<br>import { Provider } from &quot;react-redux&quot;;<br>import App from &quot;./App&quot;;<br>import { store } from &quot;./app/store&quot;;<br><br>createRoot(document.getElementById(&quot;root&quot;)).render(<br>  &lt;StrictMode&gt;<br>    &lt;Provider store={store}&gt;<br>      &lt;App /&gt;<br>    &lt;/Provider&gt;<br>  &lt;/StrictMode&gt;<br>);</pre><p>The important part is:</p><pre>&lt;Provider store={store}&gt;<br>  &lt;App /&gt;<br>&lt;/Provider&gt;</pre><p>The Provider makes the Redux store available to React components beneath it.</p><p>Without the appropriate Provider, components using React Redux hooks won’t have access to that store.</p><p>Think of the Provider as the bridge between React and Redux.</p><h4>Step 5: Read and update state in the React component</h4><p>Update src/App.jsx:</p><pre>import { useSelector, useDispatch } from &quot;react-redux&quot;;<br>import { increment, decrement, reset } from &quot;./features/counter/counterSlice&quot;;<br><br>function App() {<br>  const count = useSelector(state =&gt; state.counter.value);<br>  const dispatch = useDispatch();<br><br>  return (<br>    &lt;div&gt;<br>      &lt;h1&gt;Redux Toolkit Counter&lt;/h1&gt;<br>      &lt;h2&gt;Count: {count}&lt;/h2&gt;<br>      &lt;button onClick={() =&gt; dispatch(increment())}&gt;<br>        Increment<br>      &lt;/button&gt;<br>      &lt;button onClick={() =&gt; dispatch(decrement())}&gt;<br>        Decrement<br>      &lt;/button&gt;<br>      &lt;button onClick={() =&gt; dispatch(reset())}&gt;<br>        Reset<br>      &lt;/button&gt;<br>    &lt;/div&gt;<br>  );<br>}<br><br>export default App;</pre><p>Congratulations! You’ve built your first Redux Toolkit application.</p><p>Let’s understand how the component interacts with the store.</p><p><strong>Reading state with </strong><strong>useSelector()</strong></p><pre>const count = useSelector(<br>  (state) =&gt; state.counter.value<br>);</pre><p>This reads the counter value from the Redux store.</p><p>When the selected value changes, the component updates to display the new count.</p><p><strong>Dispatching an action with </strong><strong>useDispatch()</strong></p><pre>dispatch(increment());</pre><p>This dispatches the action generated by the slice.</p><p>Redux processes the action using the counter reducer, updates the state, and notifies subscribed components as appropriate.</p><h4>What happens when the user clicks Increment?</h4><pre>User clicks Increment<br>          |<br>          v<br>dispatch(increment())<br>          |<br>          v<br>Action: counter/increment<br>          |<br>          v<br>Counter Reducer<br>          |<br>          v<br>value increases by 1<br>          |<br>          v<br>Redux Store updates<br>          |<br>          v<br>useSelector detects<br>selected value change<br>          |<br>          v<br>React updates the UI</pre><p>For example, if the current value is 5, clicking Increment makes it 6.</p><p>The component doesn’t manually update the displayed number. It reads the current value from the store, and React updates the UI when that value changes.</p><p>This is the core Redux Toolkit workflow.</p><h3>6. Let’s Apply Redux Toolkit to a Real-World Shopping Cart</h3><p>The counter example helps us understand the basics, but let’s return to our original e-commerce scenario.</p><p>Suppose the product listing page contains an Add to Cart button.</p><p>When the user clicks it, we want to add the product to the shared cart state.</p><p>Let’s create a cart slice.</p><h4>Example: cartSlice.js</h4><pre>import { createSlice } from &quot;@reduxjs/toolkit&quot;;<br><br>const initialState = {<br>  items: []<br>};<br><br>const cartSlice = createSlice({<br>  name: &quot;cart&quot;,<br>  initialState,<br>  reducers: {<br>    addItem: (state, action) =&gt; {<br>      const product = action.payload;<br>      const existingItem = state.items.find(<br>        (item) =&gt; item.id === product.id<br>      );<br>      if (existingItem) {<br>        existingItem.quantity += 1;<br>      } else {<br>        state.items.push({<br>          ...product,<br>          quantity: 1<br>        });<br>      }<br>    },<br>    removeItem: (state, action) =&gt; {<br>      state.items = state.items.filter(<br>        (item) =&gt; item.id !== action.payload<br>      );<br>    },<br>    clearCart: (state) =&gt; {<br>      state.items = [];<br>    }<br>  }<br>});<br><br>export const {<br>  addItem,<br>  removeItem,<br>  clearCart<br>} = cartSlice.actions;<br><br>export default cartSlice.reducer;</pre><p>Let’s understand the important part.</p><h4>Adding a product</h4><pre>addItem: (state, action) =&gt; {<br>  const product = action.payload;<br><br>  const existingItem = state.items.find(<br>      (item) =&gt; item.id === product.id<br>    );<br><br>    if (existingItem) {<br>      existingItem.quantity += 1;<br>    } else {<br>      state.items.push({<br>        ...product,<br>        quantity: 1<br>      });<br>    }<br>}</pre><p>The reducer checks whether the product already exists in the cart.</p><p>If it exists, the quantity increases.</p><p>Otherwise, a new item is added with a quantity of 1.</p><p>For example, a component can dispatch:</p><pre>dispatch(<br>  addItem({<br>    id: 1,<br>    name: &quot;Laptop&quot;,<br>    price: 65000<br>  })<br>);</pre><p>The product object becomes the action’s payload.</p><p>Redux Toolkit processes the action and updates the cart state.</p><h4>Register the cart reducer</h4><p>Update src/app/store.js:</p><pre>import { configureStore } from &quot;@reduxjs/toolkit&quot;;<br>import counterReducer from &quot;../features/counter/counterSlice&quot;;<br>import cartReducer from &quot;../features/cart/cartSlice&quot;;<br><br>export const store = configureStore({<br>  reducer: {<br>    counter: counterReducer,<br>    cart: cartReducer<br>  }<br>});</pre><p>The state now has two top-level slices:</p><pre>{<br>  counter: {<br>    value: 0<br>  },<br>  cart: {<br>    items: []<br>  }<br>}</pre><p>Each slice manages its own portion of the state.</p><h4>Read the cart count</h4><p>In a navbar component, we can calculate the total number of items:</p><pre>import { useSelector } from &quot;react-redux&quot;;<br><br>function Navbar() {<br>  const items = useSelector(<br>    (state) =&gt; state.cart.items<br>  );<br>  const cartCount = items.reduce(<br>    (total, item) =&gt; total + item.quantity,<br>    0<br>  );<br>  return (<br>    &lt;nav&gt;<br>      &lt;h2&gt;My Store&lt;/h2&gt;<br>      &lt;p&gt;Cart: {cartCount}&lt;/p&gt;<br>    &lt;/nav&gt;<br>  );<br>}<br>export default Navbar;</pre><p>Notice that the navbar doesn’t need to receive the cart count through props from the product listing page.</p><p>It reads the shared state directly from Redux.</p><p>Likewise, the cart page can read the same state:</p><pre>const items = useSelector(<br>  (state) =&gt; state.cart.items<br>);</pre><p>When a product is added, both components can display updated information based on the shared state.</p><p><strong>This is where Redux Toolkit becomes useful: multiple components can work with the same application state without tightly coupling their component hierarchies.</strong></p><p>In a production application, you would also need to consider price validation, inventory, persistence, and server-side cart behavior. The frontend cart should not be treated as the authoritative source for checkout prices or stock availability.</p><h3>7. What Is the Difference Between Redux and Redux Toolkit?</h3><p>You may hear developers refer to both Redux and Redux Toolkit.</p><p>Redux is the underlying state management library and its core principles.</p><p>Redux Toolkit is the recommended way to write Redux applications today.</p><p>Here’s a compact comparison:</p><pre>+----------------------+-------------------------+<br>| Traditional Redux    | Redux Toolkit           |<br>+----------------------+-------------------------+<br>| Manual action types  | Generated action types   |<br>| Manual action creators| Generated action creators|<br>| Switch-based reducers| createSlice reducers     |<br>| Manual store setup   | configureStore           |<br>| More boilerplate     | Less boilerplate         |<br>| Immutable updates    | Immer-powered reducers   |<br>+----------------------+-------------------------+</pre><p>The key point is that Redux Toolkit doesn’t remove Redux’s core principles. It makes those principles easier to implement.</p><p>You still have a store, actions, reducers, and predictable state updates.</p><h3>8. What Is Redux Thunk?</h3><p>You may have noticed that the previous examples handled synchronous operations.</p><p>But real applications frequently need to fetch data from an API.</p><p>For example, a product listing page might need to request products from the backend.</p><p>What happens when we need asynchronous logic?</p><p>Redux Toolkit’s configureStore() includes Redux Thunk middleware by default. Thunk lets you dispatch functions that can perform asynchronous work and dispatch actions as the work progresses.</p><p>For example:</p><pre>import { createAsyncThunk } from &quot;@reduxjs/toolkit&quot;;<br><br>export const fetchProducts = createAsyncThunk(<br>  &quot;products/fetchProducts&quot;,<br>  async () =&gt; {<br>    const response = await fetch(<br>      &quot;https://dummyjson.com/products&quot;<br>    );<br>    if (!response.ok) {<br>      throw new Error(&quot;Failed to fetch products&quot;);<br>    }<br>    const data = await response.json();<br>    return data.products;<br>  }<br>);</pre><p>This creates an asynchronous thunk action.</p><p>Redux Toolkit automatically provides three lifecycle action types:</p><pre>products/fetchProducts/pending<br>products/fetchProducts/fulfilled<br>products/fetchProducts/rejected</pre><p>These represent the request being started, completed successfully, or rejected.</p><p>A slice can handle them using extraReducers:</p><pre>extraReducers: (builder) =&gt; {<br>  builder<br>    .addCase(fetchProducts.pending, (state) =&gt; {<br>      state.status = &quot;loading&quot;;<br>    })<br>    .addCase(fetchProducts.fulfilled, (state, action) =&gt; {<br>      state.status = &quot;succeeded&quot;;<br>      state.items = action.payload;<br>    })<br>    .addCase(fetchProducts.rejected, (state) =&gt; {<br>      state.status = &quot;failed&quot;;<br>    });<br>}</pre><p>In this example, state is assumed to contain status and items fields, which must be included in the slice&#39;s initialState.</p><p>A component can then dispatch the thunk:</p><pre>dispatch(fetchProducts());</pre><p>This lets the application manage loading, success, and failure states in a structured way.</p><p>For applications with more advanced data-fetching requirements, caching, automatic refetching, and request lifecycle management, Redux Toolkit also provides <strong>RTK Query</strong>. It can reduce the need to write custom fetching thunks for common API operations.</p><h3>9. When Should You Use Redux Toolkit?</h3><p>Redux Toolkit is useful when your application has shared state that benefits from centralized management.</p><p>Common examples include:</p><ul><li>Shopping cart data shared across pages.</li><li>Authentication and user-related application state.</li><li>Complex multi-step workflows.</li><li>Shared filters and sorting settings.</li><li>Data used by multiple distant components.</li><li>State transitions that need to be easy to inspect and test.</li></ul><p>However, you don’t need Redux Toolkit for every state variable.</p><p>For example:</p><pre>const [isModalOpen, setIsModalOpen] = useState(false);</pre><p>If this state is only used by one component, React’s local state is usually sufficient.</p><p>A useful rule of thumb:</p><p><strong>Use local React state for local UI behavior, and consider Redux Toolkit when shared state or complex state transitions justify centralized management.</strong></p><p>For server data that requires caching and synchronization, also consider whether RTK Query or another data-fetching solution is more appropriate than manually maintaining everything in a global store.</p><h3>10. Common Mistakes Beginners Should Avoid</h3><h4>Mistake 1: Forgetting to wrap the application in Provider</h4><p>Without the appropriate Provider, React Redux hooks cannot access the store.</p><h4>Mistake 2: Registering the wrong reducer key</h4><p>If the store is configured like this:</p><pre>reducer: {<br>  counter: counterReducer<br>}</pre><p>Then the selector should use:</p><pre>state.counter.value</pre><p>Not:</p><pre>state.value</pre><p>The state shape depends on how the reducers are registered.</p><h4>Mistake 3: Forgetting to dispatch an action</h4><p>Calling an action creator alone doesn’t update the store.</p><p>For example:</p><pre>increment();</pre><p>creates an action object, but doesn’t dispatch it.</p><p>You need:</p><pre>dispatch(increment());</pre><h4>Mistake 4: Mutating state outside a reducer</h4><p>Redux Toolkit supports mutation-like syntax inside its reducer logic because of Immer.</p><p>That doesn’t mean you should mutate values returned by useSelector() directly in a component.</p><h4>Mistake 5: Putting everything into Redux</h4><p>Not every input value, dropdown state, or temporary UI flag needs to be stored globally.</p><p>Use Redux where it provides real value.</p><h4>Mistake 6: Confusing client state with server state</h4><p>Redux Toolkit can manage client-side state and API workflows, but the browser’s state isn’t automatically the source of truth for backend data.</p><p>For important operations such as payments, stock availability, or order creation, the backend must validate the data independently.</p><h3>Conclusion</h3><p>Redux Toolkit provides a structured and predictable way to manage shared state in React applications.</p><p>We’ve learned that:</p><ul><li>The Redux store holds application state.</li><li>Actions describe events that should be processed.</li><li>Reducers determine how state changes.</li><li>createSlice() generates action creators and reducers.</li><li>configureStore() sets up the store.</li><li>Provider makes the store available to React components.</li><li>useSelector() reads state.</li><li>useDispatch() dispatches actions.</li><li>createAsyncThunk() supports asynchronous workflows.</li><li>RTK Query can simplify API fetching and caching.</li></ul><p>The key takeaway is simple:</p><p><strong>Redux Toolkit helps you manage shared application state through a predictable flow of actions, reducers, and store updates.</strong></p><p>You don’t need Redux for every React application. But when multiple components depend on the same data and state transitions become complicated, Redux Toolkit can make the codebase easier to understand, maintain, and test.</p><h3>What’s Next?</h3><p>Now that you understand Redux Toolkit, here are some topics worth exploring next:</p><ul><li><strong>Redux Toolkit vs Context API:</strong> Understand the differences and when to use each.</li><li><strong>Redux Middleware:</strong> Learn how middleware intercepts dispatched actions and supports additional behavior.</li><li><strong>Redux Toolkit with TypeScript:</strong> Define typed state, actions, and selectors.</li><li><strong>RTK Query:</strong> Explore API caching, mutations, loading states, and cache invalidation.</li><li><strong>Redux DevTools:</strong> Inspect actions and state changes while debugging.</li><li><strong>Redux Toolkit in a Real E-Commerce App:</strong> Combine product fetching, cart management, and API integration.</li></ul><p>Happy coding! ✌️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f44ede0b6224" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Micro Frontend Architecture: A Beginner’s Guide to Host, Remote, and Module Federation]]></title>
            <link>https://medium.com/@rohitkuwar/micro-frontend-architecture-a-beginners-guide-to-host-remote-and-module-federation-85ff518a3d3d?source=rss-faedefd908b4------2</link>
            <guid isPermaLink="false">https://medium.com/p/85ff518a3d3d</guid>
            <category><![CDATA[micro-frontends]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[module-federation]]></category>
            <category><![CDATA[software-architecture]]></category>
            <dc:creator><![CDATA[Rohit Kuwar]]></dc:creator>
            <pubDate>Fri, 23 Oct 2026 02:31:01 GMT</pubDate>
            <atom:updated>2026-10-23T02:31:01.734Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aIV6ez2QOKtBDqivXDHPYQ.png" /><figcaption>Micro Frontend Architecture: A Beginner’s Guide to Host, Remote, and Module Federation</figcaption></figure><p>Imagine opening Amazon or Flipkart.</p><p>You see a navigation bar, product categories, a search bar, product listings, recommendations, a shopping cart, and a checkout flow.</p><p>Now imagine that different teams are responsible for developing these features.</p><p>One team works on the product listing page. Another develops the shopping cart. A third manages checkout, while another team handles search.</p><p>How can all these teams develop, test, and deploy their features independently while delivering a single, consistent experience to the user?</p><p>One possible solution is <strong>Micro Frontend Architecture</strong>.</p><p>Micro Frontends apply the ideas of microservices to frontend development. Instead of building an entire user interface as one tightly coupled application, we divide it into smaller, independently manageable frontend applications.</p><p>In this article, we’ll explore the concept from scratch, understand why it is useful, learn about Host and Remote applications, and see how Webpack Module Federation helps different frontend applications work together.</p><p>We’ll also build a small conceptual example using React and Webpack.</p><p>No prior knowledge of Micro Frontends is required.</p><h3>1. First, Understand the Problem</h3><p>Let’s consider an e-commerce application similar to Amazon or Flipkart.</p><p>A simplified version might contain these features:</p><ul><li>Header and navigation</li><li>Product search</li><li>Product listing</li><li>Product details</li><li>Shopping cart</li><li>Checkout</li><li>User account</li></ul><p>Initially, a company might build all these features inside a single React application.</p><p>The project could look something like this:</p><pre>ecommerce-app/<br>    src/<br>        components/<br>            Header.jsx<br>            SearchBar.jsx<br>            ProductCard.jsx<br>            Cart.jsx<br>            Checkout.jsx<br>        pages/<br>            Home.jsx<br>            Products.jsx<br>            ProductDetails.jsx<br>        App.jsx</pre><p>This is a perfectly reasonable architecture for many applications.</p><p>However, as the application grows, several challenges can emerge.</p><h4>Problem 1: Multiple teams work on the same codebase</h4><p>Imagine five teams working on the same frontend application.</p><p>The product team modifies product components, the checkout team changes checkout logic, and the cart team updates cart functionality.</p><p>Changes can overlap, dependencies can become complicated, and teams may need to coordinate releases.</p><h4>Problem 2: Independent deployment becomes difficult</h4><p>Suppose the shopping cart team finishes a feature today.</p><p>However, the application contains other features that are still under development.</p><p>Depending on how the project is structured and deployed, the cart team may have to wait for the larger application release.</p><h4>Problem 3: The codebase becomes harder to maintain</h4><p>As the application grows, shared components, business logic, dependencies, and team responsibilities can become tightly coupled.</p><p>This doesn’t mean every large React application will develop these problems. Good modular design can keep a monolithic frontend maintainable for a long time.</p><p>But at a certain organizational and technical scale, a different architecture may become useful.</p><p>This brings us to Micro Frontends.</p><h3>2. What Is Micro Frontend Architecture?</h3><p><strong>Micro Frontend Architecture is an approach in which a large frontend application is divided into smaller frontend applications or independently developed features that work together to deliver a unified user experience.</strong></p><p>Instead of one team owning the entire frontend, different teams can own different parts of the application.</p><p>For example:</p><pre>              E-Commerce Application<br>                       |<br>        +--------------+--------------+<br>        |              |              |<br>     Products         Cart          Checkout<br>        |              |              |<br>     Team A          Team B         Team C<br>        |              |              |<br>     Frontend       Frontend       Frontend<br>     Application    Application    Application</pre><p>Each frontend can have its own:</p><ul><li>Components and business logic</li><li>Development workflow</li><li>Tests and build process</li><li>Team ownership</li><li>Release process, depending on the architecture</li></ul><p>The different parts are then composed into a single experience for the user.</p><p>The important point is that <strong>the user sees one application, even though multiple frontend applications may contribute to it</strong>.</p><h3>A real-world example: An online shopping platform</h3><p>Imagine an e-commerce platform with the following ownership:</p><pre>                   Shopping Website<br>                           |<br>          +----------------+----------------+<br>          |                |                |<br>      Product UI        Cart UI        Checkout UI<br>          |                |                |<br>       Team A            Team B           Team C<br>          |                |                |<br>      Product data      Cart state       Order flow</pre><p>The product team can improve product cards without needing to coordinate every small change with the checkout team.</p><p>The cart team can work on cart-related features independently, provided the teams maintain compatible integration contracts.</p><p>The checkout team can focus on the checkout experience.</p><p>This is the basic idea behind Micro Frontends.</p><h3>3. Why Do We Need Micro Frontends?</h3><p>Micro Frontends aren’t required for every application. A single React application is often the better choice for small and medium-sized projects.</p><p>Micro Frontends become valuable when an organization needs stronger team independence, clearer ownership, and more flexible delivery.</p><p>Let’s look at the main reasons.</p><h4>1. Independent team ownership</h4><p>Different teams can own different parts of the frontend.</p><p>For example:</p><pre>Product Team → Product listing<br>Cart Team    → Shopping cart<br>Checkout Team → Checkout experience</pre><p>Each team can make decisions within its area without constantly modifying another team’s code.</p><h4>2. Independent deployment</h4><p>Suppose the cart team adds a new discount feature.</p><p>With a suitable Micro Frontend architecture, the team may be able to build and deploy the cart application independently of the product listing application.</p><p>The exact deployment model depends on how the applications are composed.</p><p>For example, a runtime-composed architecture can load a newly deployed remote application without requiring the host’s entire bundle to be rebuilt, provided the remote’s integration contract remains compatible.</p><h4>3. Better ownership of large codebases</h4><p>Instead of organizing every feature around one enormous frontend repository, teams can establish clearer boundaries.</p><p>These boundaries can make it easier to understand who owns a feature, which team handles bugs, and where new functionality belongs.</p><h4>4. Technology flexibility</h4><p>In some architectures, individual frontend applications can use different frameworks.</p><p>For example, one part might use React while another uses Vue.</p><p>However, this flexibility comes with costs. Different frameworks can increase bundle size, create inconsistent user experiences, and complicate integration.</p><p>Using the same framework across all Micro Frontends is often simpler.</p><h4>5. Parallel development</h4><p>Multiple teams can develop features simultaneously, reducing coordination overhead when their responsibilities and integration contracts are well defined.</p><p><strong>Remember:</strong> Micro Frontends solve organizational and architectural problems. They don’t automatically make an application faster or its code better.</p><h3>4. How Do Micro Frontends Work?</h3><p>There are several ways to implement Micro Frontends.</p><p>For example:</p><ul><li>Build-time integration</li><li>Runtime integration</li><li>Web Components</li><li>Client-side routing and separate applications</li><li>Server-side composition</li></ul><p>One popular runtime integration approach in the Webpack ecosystem is <strong>Module Federation</strong>.</p><p>Before exploring it, let’s understand two important terms: Host and Remote.</p><h3>What is a Host application?</h3><p>The Host is the application that brings together features exposed by other frontend applications.</p><p>It may provide:</p><ul><li>The main application shell</li><li>Navigation and routing</li><li>Global layout</li><li>Authentication integration</li><li>Shared application infrastructure</li></ul><p>For our example, imagine that the Host is the main shopping website.</p><pre>Host Application<br>    |<br>    +-- Header<br>    +-- Navigation<br>    +-- Product Remote<br>    +-- Cart Remote</pre><p>The Host can load features from other applications at runtime.</p><h3>What is a Remote application?</h3><p>A Remote is an application that exposes selected modules so that another application can consume them.</p><p>For example, the Product Remote might expose a product listing component.</p><pre>Product Remote<br>    |<br>    +-- ProductList.jsx<br>    +-- ProductCard.jsx<br>    +-- Product-related logic</pre><p>The Host can load the exposed ProductList component and render it as part of its own user interface.</p><p>A Remote doesn’t have to expose its entire application. It can expose specific components, utilities, or other modules.</p><h3>Host vs Remote</h3><pre>+------------------------+------------------------+---------------------------+<br>| Feature                | Host                   | Remote                    |<br>+------------------------+------------------------+---------------------------+<br>| Main responsibility    | Composes the app       | Exposes modules           |<br>| Loads remote modules   | Usually yes            | Not necessarily           |<br>| Exposes modules        | Optional               | Commonly does             |<br>| Own build config       | Yes                    | Yes                       |<br>| Independent deployment | Depends on architecture| Often supported           |<br>| Example                | Shopping website       | Product listing app       |<br>+------------------------+------------------------+---------------------------+</pre><p><strong>In simple terms:</strong></p><ul><li><strong>Host:</strong> The main application that brings different frontend features together.</li><li><strong>Remote:</strong> An application that exposes components or modules for other applications to consume.</li></ul><h3>5. What Is Webpack Module Federation?</h3><p>Now that we understand Host and Remote applications, let’s explore the mechanism that connects them.</p><p><strong>Webpack Module Federation allows separately built JavaScript applications to share and consume modules at runtime.</strong></p><p>It was introduced in Webpack 5.</p><p>Traditionally, applications commonly bundle their dependencies during the build process. If one application needs code from another, that code may be imported through a package, shared library, or another integration mechanism.</p><p>Module Federation provides a way to expose selected modules from one build and consume those modules from another build at runtime.</p><p>For example:</p><pre>                Host Application<br>                       |<br>                       |<br>               Loads remote module<br>                       |<br>                       v<br>                Remote Application<br>                       |<br>                       |<br>                 ProductList.jsx</pre><p>The Host doesn’t necessarily need the Remote’s source code in its own repository.</p><p>Instead, it can load the Remote’s federation entry file, commonly named remoteEntry.js, and use the modules exposed by that Remote.</p><p>Let’s understand the key terms.</p><h4>1. ModuleFederationPlugin</h4><p>Webpack provides the ModuleFederationPlugin to configure federation.</p><p>It allows an application to define:</p><ul><li>Its federation name</li><li>The remote entry filename</li><li>The modules it exposes</li><li>The remote applications it consumes</li><li>Dependencies shared between applications</li></ul><h4>2. remoteEntry.js</h4><p>The Remote build generates a federation entry file, commonly called remoteEntry.js.</p><p>This file provides the runtime information needed to access the modules exposed by the Remote.</p><p>Think of it as the entry point through which the Host discovers and loads the Remote’s exposed modules.</p><p>It is not necessarily the complete application bundle. Additional JavaScript chunks may be downloaded when needed.</p><h4>3. exposes</h4><p>The exposes configuration specifies which modules a Remote makes available.</p><p>For example:</p><pre>exposes: {<br>  &quot;./ProductList&quot;: &quot;./src/ProductList.jsx&quot;<br>}</pre><p>This means that another application can reference the exposed module using the name ./ProductList.</p><h4>4. remotes</h4><p>The remotes configuration tells the Host where to find another application.</p><p>For example:</p><pre>remotes: {<br>  productApp: &quot;productApp@http://localhost:3001/remoteEntry.js&quot;<br>}</pre><p>Here:</p><ul><li>productApp is the local alias used by the Host.</li><li>productApp before @ is the Remote container name.</li><li>The URL points to the Remote’s federation entry file.</li></ul><h4>5. shared</h4><p>The shared configuration lets applications share dependencies rather than always loading separate copies.</p><p>React is a common example.</p><pre>shared: {<br>  react: {<br>    singleton: true<br>  },<br>  &quot;react-dom&quot;: {<br>    singleton: true<br>  }<br>}</pre><p>The singleton option tells the federation runtime to use a single shared instance of that dependency within the relevant sharing scope, subject to the configured versions and runtime negotiation.</p><p>This is especially important for React because separate incompatible React instances can cause problems when components and hooks are integrated across application boundaries.</p><p>For production applications, dependency versions and sharing requirements should be configured carefully.</p><h3>6. Let’s Build a Simple Example Using React and Webpack</h3><p>Let’s create two applications:</p><ol><li><strong>Host Application</strong> — Displays the main page and loads the product component.</li><li><strong>Product Remote</strong> — Exposes a reusable ProductList component.</li></ol><p>Our goal is simple:</p><p>When a user opens the Host application, it loads the ProductList component from the Remote application.</p><p>We’ll use Webpack 5 and React.</p><h4>Project structure</h4><pre>micro-frontend-demo/<br>    host/<br>        src/<br>            App.jsx<br>            index.js<br>        webpack.config.js<br><br>    product-remote/<br>        src/<br>            ProductList.jsx<br>            index.js<br>        webpack.config.js</pre><p>For simplicity, we’ll focus on the federation configuration and the React components rather than reproducing all the boilerplate required to configure two complete Webpack projects.</p><h4>Step 1: Create the Product Remote component</h4><p>Inside product-remote/src/ProductList.jsx:</p><pre>import React from &quot;react&quot;;<br><br>const products = [<br>  { id: 1, name: &quot;Laptop&quot;, price: 65000 },<br>  { id: 2, name: &quot;Headphones&quot;, price: 2500 },<br>  { id: 3, name: &quot;Keyboard&quot;, price: 1500 }<br>];<br><br>function ProductList() {<br>  return (<br>    &lt;section&gt;<br>      &lt;h2&gt;Products&lt;/h2&gt;<br>      {products.map((product) =&gt; (<br>        &lt;div key={product.id}&gt;<br>          &lt;h3&gt;{product.name}&lt;/h3&gt;<br>          &lt;p&gt;Price: ₹{product.price}&lt;/p&gt;<br>        &lt;/div&gt;<br>      ))}<br>    &lt;/section&gt;<br>  );<br>}<br><br>export default ProductList;</pre><p>This is a simple React component that displays a list of products.</p><p>Normally, we might import this component directly into another React application.</p><p>But with Module Federation, we will expose it through the Remote application’s build configuration.</p><h4>Step 2: Configure the Product Remote</h4><p>Inside product-remote/webpack.config.js:</p><pre>const HtmlWebpackPlugin = require(&quot;html-webpack-plugin&quot;);<br>const { ModuleFederationPlugin } = require(&quot;webpack&quot;).container;<br>const deps = require(&quot;./package.json&quot;).dependencies;<br><br>module.exports = {<br>  mode: &quot;development&quot;,<br>  entry: &quot;./src/index.js&quot;,<br>  devServer: {<br>    port: 3001,<br>    historyApiFallback: true<br>  },<br>  output: {<br>    publicPath: &quot;auto&quot;<br>  },<br>  resolve: {<br>    extensions: [&quot;.js&quot;, &quot;.jsx&quot;]<br>  },<br>  module: {<br>    rules: [<br>      {<br>        test: /\.jsx?$/,<br>        exclude: /node_modules/,<br>        use: &quot;babel-loader&quot;<br>      }<br>    ]<br>  },<br>  plugins: [<br>    new HtmlWebpackPlugin({<br>      template: &quot;./public/index.html&quot;<br>    }),<br>    new ModuleFederationPlugin({<br>      name: &quot;productApp&quot;,<br>      filename: &quot;remoteEntry.js&quot;,<br>      exposes: {<br>        &quot;./ProductList&quot;: &quot;./src/ProductList.jsx&quot;<br>      },<br>      shared: {<br>        react: {<br>          singleton: true,<br>          requiredVersion: deps.react<br>        },<br>        &quot;react-dom&quot;: {<br>          singleton: true,<br>          requiredVersion: deps[&quot;react-dom&quot;]<br>        }<br>      }<br>    })<br>  ]<br>};</pre><p>Let’s understand the important configuration.</p><p><strong>name: &quot;productApp&quot;</strong></p><p>This identifies the Remote container.</p><p><strong>filename: &quot;remoteEntry.js&quot;</strong></p><p>This specifies the name of the federation entry file generated by Webpack.</p><p><strong>exposes</strong></p><pre>exposes: {<br>  &quot;./ProductList&quot;: &quot;./src/ProductList.jsx&quot;<br>}</pre><p>This exposes the product component so that another application can import it through Module Federation.</p><p><strong>shared</strong></p><p>We configure React and React DOM as shared dependencies to help avoid unnecessary duplicate framework instances.</p><p><strong>publicPath: &quot;auto&quot;</strong></p><p>This helps Webpack determine the public path for loading assets and additional chunks. It is useful when a Remote is served from a different location than the Host.</p><p>The configuration assumes a standard Webpack project with dependencies such as webpack, webpack-cli, webpack-dev-server, html-webpack-plugin, babel-loader, and the appropriate Babel presets already installed.</p><h4>Step 3: Configure the Host application</h4><p>Now let’s configure the main shopping website.</p><p>Inside host/webpack.config.js:</p><pre>const HtmlWebpackPlugin = require(&quot;html-webpack-plugin&quot;);<br>const { ModuleFederationPlugin } = require(&quot;webpack&quot;).container;<br>const deps = require(&quot;./package.json&quot;).dependencies;<br><br>module.exports = {<br>  mode: &quot;development&quot;,<br>  entry: &quot;./src/index.js&quot;,<br>  devServer: {<br>    port: 3000,<br>    historyApiFallback: true<br>  },<br>  output: {<br>    publicPath: &quot;auto&quot;<br>  },<br>  resolve: {<br>    extensions: [&quot;.js&quot;, &quot;.jsx&quot;]<br>  },<br>  module: {<br>    rules: [<br>      {<br>        test: /\.jsx?$/,<br>        exclude: /node_modules/,<br>        use: &quot;babel-loader&quot;<br>      }<br>    ]<br>  },<br>  plugins: [<br>    new HtmlWebpackPlugin({<br>      template: &quot;./public/index.html&quot;<br>    }),<br>    new ModuleFederationPlugin({<br>      name: &quot;hostApp&quot;,<br>      remotes: {<br>        productApp:<br>          &quot;productApp@http://localhost:3001/remoteEntry.js&quot;<br>      },<br>      shared: {<br>        react: {<br>          singleton: true,<br>          requiredVersion: deps.react<br>        },<br>        &quot;react-dom&quot;: {<br>          singleton: true,<br>          requiredVersion: deps[&quot;react-dom&quot;]<br>        }<br>      }<br>    })<br>  ]<br>};</pre><p>The most important part is:</p><pre>remotes: {<br>  productApp:<br>    &quot;productApp@http://localhost:3001/remoteEntry.js&quot;<br>}</pre><p>This tells the Host where to find the Product Remote.</p><p>The Remote must be running and serving its federation entry file at that URL.</p><p>In production, the URL would typically point to a deployed asset location or CDN rather than localhost.</p><h4>Step 4: Import the Remote component in the Host</h4><p>Inside host/src/App.jsx:</p><pre>import React, { Suspense, lazy } from &quot;react&quot;;<br><br>const ProductList = lazy(<br>  () =&gt; import(&quot;productApp/ProductList&quot;)<br>);<br><br>function App() {<br>  return (<br>    &lt;div&gt;<br>      &lt;h1&gt;My Shopping Website&lt;/h1&gt;<br>      &lt;nav&gt;<br>        &lt;a href=&quot;/&quot;&gt;Home&lt;/a&gt;<br>        {&quot; | &quot;}<br>        &lt;a href=&quot;/cart&quot;&gt;Cart&lt;/a&gt;<br>      &lt;/nav&gt;<br>      &lt;Suspense fallback={&lt;p&gt;Loading products...&lt;/p&gt;}&gt;<br>        &lt;ProductList /&gt;<br>      &lt;/Suspense&gt;<br>    &lt;/div&gt;<br>  );<br>}<br><br>export default App;</pre><p>Let’s break this down.</p><p><strong>import(&quot;productApp/ProductList&quot;)</strong></p><p>This references the exposed component from the Remote application.</p><p>It isn’t a normal relative import such as:</p><pre>import ProductList from &quot;./ProductList&quot;;</pre><p>Instead, Webpack resolves it through the configured federation relationship.</p><p><strong>React.lazy()</strong></p><p>This lets React load the component asynchronously.</p><p><strong>Suspense</strong></p><p>While the component is being loaded, React displays the fallback UI.</p><p>For example:</p><pre>My Shopping Website<br><br>Loading products...</pre><p>Once the component loads successfully, the product list appears.</p><p>Remember that React.lazy() and Suspense handle the component&#39;s loading state, but you should also design explicit error handling for failed Remote downloads or incompatible integrations.</p><h4>Step 5: What happens when the application runs?</h4><p>Assume both applications are running:</p><pre>Host:<br>http://localhost:3000<br><br>Product Remote:<br>http://localhost:3001</pre><p>The browser opens the Host application.</p><p>The Host discovers that ProductList comes from productApp.</p><p>It loads the Remote’s federation entry file, resolves the exposed module, and loads the required code.</p><p>The result looks like this:</p><pre>Browser<br>   |<br>   v<br>Host Application :3000<br>   |<br>   | Loads remoteEntry.js<br>   v<br>Product Remote :3001<br>   |<br>   | Provides ProductList<br>   v<br>Host renders ProductList<br>   |<br>   v<br>User sees one shopping website</pre><p>The user doesn’t need to know that the product component originated from another application.</p><p>The Host provides the surrounding page, and the Remote provides the product listing feature.</p><p><strong>Important:</strong> This example illustrates runtime integration. To run it, both applications need valid Webpack configurations, React dependencies, entry files, HTML templates, Babel configuration, and scripts to start their development servers. The Remote must be available at the configured URL.</p><h3>7. How Does Independent Deployment Work?</h3><p>One of the major attractions of Micro Frontends is the ability to deploy different frontend features independently.</p><p>Imagine the following deployment setup:</p><pre>                 Production Website<br>                         |<br>                         v<br>                  Host Application<br>                         |<br>                         v<br>                  Remote Entry URL<br>                         |<br>                         v<br>                  Product Remote</pre><p>Suppose the product team changes the product card design.</p><p>The team builds and deploys a new version of the Product Remote.</p><p>If the Host continues loading the same stable Remote entry URL, it can load the updated implementation without rebuilding the Host, provided the Remote remains compatible with the Host’s expectations.</p><p>This is an important benefit, but it requires good release management.</p><p>For example, teams need to consider:</p><ul><li>Backward-compatible exposed module APIs.</li><li>Remote availability and fallback behavior.</li><li>Dependency compatibility.</li><li>Cache invalidation for remote assets.</li><li>Versioning and rollback strategies.</li><li>Testing integration between the Host and Remote.</li></ul><p>Independent deployment doesn’t mean there are no dependencies. It means teams can manage and release their parts more independently when those dependencies are well controlled.</p><h3>8. Advantages of Micro Frontend Architecture</h3><p>Let’s summarize the major benefits.</p><h4>1. Independent development</h4><p>Different teams can own different frontend features and work in parallel.</p><h4>2. Independent deployment</h4><p>A Remote can often be deployed separately from the Host in a runtime-composed architecture.</p><h4>3. Clear ownership</h4><p>Teams can maintain responsibility for their components, functionality, and release processes.</p><h4>4. Incremental modernization</h4><p>Organizations can gradually replace or modernize parts of a legacy frontend instead of rewriting the entire application at once.</p><h4>5. Organizational scalability</h4><p>As teams grow, clearer boundaries can reduce coordination overhead and allow teams to move more independently.</p><p>These benefits are most valuable when the application and organization are large enough to justify the extra architecture.</p><h3>9. Disadvantages of Micro Frontend Architecture</h3><p>Micro Frontends are not a free improvement.</p><p>They introduce complexity that a well-structured monolithic frontend may not need.</p><h4>1. Increased architectural complexity</h4><p>Instead of managing one frontend build, you may need to manage multiple builds, deployment pipelines, runtime dependencies, and integration contracts.</p><h4>2. Dependency management</h4><p>Different applications may use incompatible versions of React or other libraries.</p><p>Module Federation’s shared dependency configuration helps, but version negotiation and compatibility still require careful planning.</p><h4>3. Performance overhead</h4><p>Multiple frontend applications may introduce additional JavaScript, network requests, and runtime overhead.</p><p>Poorly designed Micro Frontends can make an application slower rather than faster.</p><h4>4. Shared state becomes more complicated</h4><p>Suppose the cart count appears in the global header, product pages, and checkout.</p><p>How should these parts share cart information?</p><p>Possible approaches include:</p><ul><li>A shared backend API.</li><li>Explicit props and callbacks where appropriate.</li><li>A carefully designed shared state layer.</li><li>Custom browser events.</li><li>A shared state library, when the architecture justifies it.</li></ul><p>There is no universal solution. The important thing is to define clear ownership and avoid creating tightly coupled global state across all frontend applications.</p><h4>5. Inconsistent user experience</h4><p>Different teams might implement different button styles, spacing, loading states, or accessibility behavior.</p><p>A shared design system and consistent interface guidelines can help maintain a unified experience.</p><h4>6. Debugging becomes harder</h4><p>An issue might originate in the Host, a Remote, a shared dependency, a network request, or an integration contract.</p><p>Teams need good logging, source maps, monitoring, and clear ownership to troubleshoot effectively.</p><h4>7. Operational overhead</h4><p>Multiple independently deployed applications mean more pipelines, monitoring, release coordination, and potentially more infrastructure to maintain.</p><p>For a small application, this overhead may outweigh the benefits.</p><h3>10. When Should You Use Micro Frontends?</h3><p>Micro Frontends are worth considering when:</p><ul><li>Multiple teams own distinct frontend domains.</li><li>Teams need to release features independently.</li><li>The application is large and has clear domain boundaries.</li><li>A gradual migration from a legacy frontend is necessary.</li><li>The organization can support the added operational complexity.</li></ul><p>They may be unnecessary when:</p><ul><li>The application is small.</li><li>One team owns the entire frontend.</li><li>Features are tightly coupled.</li><li>A modular monolithic React application already meets the requirements.</li><li>Independent deployment offers little practical benefit.</li></ul><p>A modular monolith is often an excellent starting point.</p><p>You can organize a React application into well-defined feature modules without immediately splitting it into independently deployed applications.</p><p><strong>The goal isn’t to use the most advanced architecture. The goal is to choose the simplest architecture that meets the application’s actual needs.</strong></p><h3>Conclusion</h3><p>Micro Frontend Architecture provides a way to divide a large frontend into smaller applications that can be developed and, in suitable architectures, deployed independently.</p><p>We’ve learned that:</p><ul><li>Micro Frontends help organize large frontend applications around independently owned features.</li><li>The Host composes the overall experience.</li><li>Remote applications expose modules that other applications can consume.</li><li>Webpack Module Federation enables runtime sharing and loading of modules across separate builds.</li><li>exposes defines the modules a Remote makes available.</li><li>remotes tells the Host where to find other applications.</li><li>shared configures dependencies that can be reused across application boundaries.</li><li>Independent deployment requires stable contracts, compatibility, and reliable release processes.</li><li>Micro Frontends introduce trade-offs in performance, testing, state management, and operational complexity.</li></ul><p>The key takeaway is simple:</p><p><strong>Micro Frontends aren’t just about splitting frontend code into multiple projects. They’re about creating clear boundaries that allow teams to build and deliver frontend features more independently.</strong></p><p>Before adopting this architecture, understand the problem you’re trying to solve. If a modular monolith is enough, keep things simple. If independent ownership and deployment are becoming genuine challenges, Micro Frontends may be worth exploring.</p><h3>What’s Next?</h3><p>Now that you understand the basics, consider exploring these topics next:</p><ul><li><strong>Module Federation in Depth:</strong> Understand runtime module loading, shared dependency negotiation, and version compatibility.</li><li><strong>Micro Frontend Communication:</strong> Learn how independent applications exchange data using props, custom events, APIs, or shared state.</li><li><strong>Micro Frontend Routing:</strong> Explore how the Host and Remote applications coordinate navigation.</li><li><strong>Error Boundaries and Fallback UI:</strong> Handle failures when a Remote application cannot load.</li><li><strong>Micro Frontend Performance:</strong> Learn about bundle splitting, caching, shared dependencies, and Core Web Vitals.</li><li><strong>Micro Frontend vs Monolithic Frontend:</strong> Understand when each architecture makes sense.</li></ul><p>Happy coding! ✌️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=85ff518a3d3d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[WebSockets in JavaScript: A Beginner’s Guide to Real-Time Communication]]></title>
            <link>https://javascript.plainenglish.io/websockets-in-javascript-a-beginners-guide-to-real-time-communication-9e2ad7f168bc?source=rss-faedefd908b4------2</link>
            <guid isPermaLink="false">https://medium.com/p/9e2ad7f168bc</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[websocket]]></category>
            <category><![CDATA[real-time-application]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[frontend-development]]></category>
            <dc:creator><![CDATA[Rohit Kuwar]]></dc:creator>
            <pubDate>Wed, 21 Oct 2026 02:31:01 GMT</pubDate>
            <atom:updated>2026-10-21T02:31:01.724Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nuBuUHvBN9IuIOHpChwHnA.png" /><figcaption>WebSockets in JavaScript: A Beginner’s Guide to Real-Time Communication</figcaption></figure><p>Have you ever used a chat application where messages appear instantly without refreshing the page?</p><p>Or visited a dashboard where stock prices, notifications, or live order statuses update automatically?</p><p>How does the browser receive new information from the server when the user hasn’t clicked anything?</p><p>One common solution is <strong>WebSockets</strong>.</p><p>WebSockets allow the browser and server to establish a persistent connection through which they can exchange messages in both directions. Once the connection is established, either side can send data without creating a new HTTP request for every message.</p><p>In this article, we’ll understand WebSockets from scratch, learn how messages travel between the browser and server, build a small example using HTML and JavaScript, and then implement the same functionality in React.</p><p>We’ll also explore why we store the WebSocket instance in useRef, how to manage its lifecycle, and what happens when the connection closes.</p><p>No prior knowledge of WebSockets is required.</p><h3>1. First, Understand the Problem</h3><p>Let’s start with a simple scenario.</p><p>Imagine you’re building a chat application.</p><p>User A sends a message to User B. Ideally, User B should receive that message immediately, without refreshing the page.</p><p>But how does the application know when a new message arrives?</p><p>With a traditional HTTP request-response flow, the browser sends a request and the server responds.</p><p>For example:</p><pre>Browser                 Server<br>   |                        |<br>   |------ Request --------&gt;|<br>   |                        |<br>   |&lt;------ Response -------|<br>   |                        |<br>   |   Connection ends      |</pre><p>This works perfectly for many operations, such as fetching a user profile or loading a product list.</p><p>But consider a real-time application where the server needs to send new information whenever something happens.</p><p>One simple approach is polling.</p><h3>What is polling?</h3><p>Polling means the browser periodically asks the server whether anything has changed.</p><p>For example:</p><pre>setInterval(async () =&gt; {<br>  const response = await fetch(&quot;/api/messages&quot;);<br>  const messages = await response.json();<br><br>  console.log(messages);<br>}, 3000);</pre><p>This sends an HTTP request every three seconds.</p><p>The flow looks like this:</p><pre>Browser                  Server<br>   |                         |<br>   |---- Any new messages? -&gt;|<br>   |&lt;---- No new messages ---|<br>   |                         |<br>   |---- Any new messages? -&gt;|<br>   |&lt;---- New messages ------|<br>   |                         |<br>   |---- Any new messages? -&gt;|<br>   |&lt;---- No new messages ---|</pre><p>Polling is simple, but it has disadvantages:</p><ul><li>It generates repeated HTTP requests.</li><li>The server receives requests even when nothing has changed.</li><li>Updates may be delayed until the next polling interval.</li><li>Short polling intervals can create unnecessary traffic.</li></ul><p>What if the browser and server could maintain an open connection and send messages whenever needed?</p><p>That’s where WebSockets come in.</p><h3>2. What Is a WebSocket?</h3><p>A WebSocket is a communication protocol that provides a persistent, two-way communication channel between a client and a server.</p><p>Instead of repeatedly creating requests to check for updates, the browser establishes a connection that can remain open.</p><p>Once connected:</p><ul><li>The browser can send messages to the server.</li><li>The server can send messages to the browser.</li><li>Either side can initiate a message.</li><li>The connection remains open until it is closed or interrupted.</li></ul><p>This is called <strong>full-duplex communication</strong>.</p><p>Let’s visualize it:</p><pre>WebSocket Connection<br><br>Browser  &lt;==========================&gt;  Server<br>          Message              Message<br>          Message              Message<br>          Message              Message<br><br>          Persistent connection</pre><p>Unlike ordinary HTTP request-response communication, the server doesn’t need to wait for a new browser request before sending a WebSocket message.</p><h3>How is the connection established?</h3><p>WebSockets typically begin with an HTTP-based handshake.</p><p>The browser requests an upgrade from HTTP to WebSocket. If the server accepts, the connection switches to the WebSocket protocol.</p><p>Conceptually:</p><pre>Browser                         Server<br>   |                                |<br>   |--- HTTP Upgrade Request ------&gt;|<br>   |                                |<br>   |&lt;------ Upgrade Accepted -------|<br>   |                                |<br>   |====== WebSocket Connection ====|<br>   |                                |<br>   |------ Message ----------------&gt;|<br>   |&lt;----- Message -----------------|<br>   |------ Message ----------------&gt;|</pre><p>After the handshake succeeds, the connection can carry WebSocket messages in both directions.</p><p>WebSocket URLs typically use these schemes:</p><ul><li>ws:// — WebSocket without transport encryption.</li><li>wss:// — WebSocket protected by TLS encryption.</li></ul><p>For production applications, use wss:// over a secure connection.</p><h3>3. How Does WebSocket Communication Work?</h3><p>Before writing code, let’s understand the main concepts.</p><h4>1. WebSocket</h4><p>The browser provides a built-in WebSocket API.</p><p>You create a connection like this:</p><pre>const socket = new WebSocket(&quot;wss://example.com/socket&quot;);</pre><p>This initiates a connection to the specified WebSocket server.</p><h4>2. onopen</h4><p>This event fires when the connection has been established.</p><pre>socket.onopen = () =&gt; {<br>  console.log(&quot;Connected to server&quot;);<br>};</pre><p>You can use this event to update the UI or enable messaging.</p><h4>3. send()</h4><p>The send() method sends data through the WebSocket connection.</p><pre>socket.send(&quot;Hello Server!&quot;);</pre><p>You can send strings, and the API also supports other types of data, including binary data.</p><p>For JSON messages, convert the object into a string:</p><pre>socket.send(<br>  JSON.stringify({<br>    type: &quot;chat&quot;,<br>    message: &quot;Hello!&quot;<br>  })<br>);</pre><h4>4. onmessage</h4><p>This event fires when a message arrives from the server.</p><pre>socket.onmessage = (event) =&gt; {<br>  console.log(&quot;Received:&quot;, event.data);<br>};</pre><p>For a JSON message, parse the string:</p><pre>socket.onmessage = (event) =&gt; {<br>  const data = JSON.parse(event.data);<br><br>  console.log(data.message);<br>};</pre><h4>5. onclose</h4><p>This event fires when the connection closes.</p><pre>socket.onclose = () =&gt; {<br>  console.log(&quot;Connection closed&quot;);<br>};</pre><p>You can use it to update the connection status or initiate a reconnection strategy.</p><h4>6. onerror</h4><p>This event reports certain connection errors.</p><pre>socket.onerror = (error) =&gt; {<br>  console.error(&quot;WebSocket error:&quot;, error);<br>};</pre><p>An error doesn’t necessarily tell you the precise reason for a failure. In practice, you should also inspect the close event and handle reconnection when appropriate.</p><p>These six concepts are enough to build our first WebSocket application.</p><h3>4. Build Your First WebSocket Application Using HTML and JavaScript</h3><p>Let’s build a small application where the browser sends a message to a server, and the server responds with that message.</p><p>For example:</p><pre>Browser: Hello Server!<br>Server:  Server received: Hello Server!</pre><p>We’ll create four files:</p><pre>websocket-demo/<br>    server.js<br>    package.json<br>    index.html<br>    main.js</pre><p>One important point: <strong>a WebSocket client needs a WebSocket server to communicate with.</strong> The browser’s WebSocket API alone isn’t enough.</p><p>For this example, we’ll use Node.js and the ws package to create a small server.</p><h4>Step 1: Set up the project</h4><p>Install Node.js if it isn’t already installed.</p><p>Create a project folder and run:</p><pre>npm init -y<br>npm install ws</pre><p>The ws package provides a WebSocket server implementation for Node.js.</p><h4>Step 2: Create the WebSocket server</h4><p>Create server.js:</p><pre>const WebSocket = require(&quot;ws&quot;);<br><br>const wss = new WebSocket.Server({<br>  port: 8080<br>});<br><br>wss.on(&quot;connection&quot;, (socket) =&gt; {<br>  console.log(&quot;Client connected&quot;);<br>  socket.on(&quot;message&quot;, (message) =&gt; {<br>    const text = message.toString();<br>    console.log(&quot;Received:&quot;, text);<br>    socket.send(`Server received: ${text}`);<br>  });<br>  socket.on(&quot;close&quot;, () =&gt; {<br>    console.log(&quot;Client disconnected&quot;);<br>  });<br>});<br><br>console.log(&quot;WebSocket server running on port 8080&quot;);</pre><p>Let’s understand what happens here.</p><p>First, we create a WebSocket server:</p><pre>const wss = new WebSocket.Server({<br>  port: 8080<br>});</pre><p>The server listens on port 8080.</p><p>Next, we listen for new connections:</p><pre>wss.on(&quot;connection&quot;, (socket) =&gt; {<br>  // Handle this client&#39;s connection<br>});</pre><p>When a browser connects, the callback receives a socket representing that particular client&#39;s connection.</p><p>We then listen for messages from that client:</p><pre>socket.on(&quot;message&quot;, (message) =&gt; {<br>  const text = message.toString();<br><br>  socket.send(`Server received: ${text}`);<br>});</pre><p>When a message arrives, the server sends a response through the same connection.</p><p>Notice that we’re using socket.send() on the server. The browser also has a send() method, but each one sends data in the opposite direction.</p><h4>Step 3: Create the HTML file</h4><p>Create index.html:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>&lt;head&gt;<br>  &lt;meta charset=&quot;UTF-8&quot; /&gt;<br>  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;<br>  &lt;title&gt;WebSocket Demo&lt;/title&gt;<br>&lt;/head&gt;<br>&lt;body&gt;<br>  &lt;h1&gt;WebSocket Demo&lt;/h1&gt;<br>  &lt;p id=&quot;status&quot;&gt;Connecting...&lt;/p&gt;<br>  &lt;input<br>    id=&quot;messageInput&quot;<br>    type=&quot;text&quot;<br>    placeholder=&quot;Enter your message&quot;<br>  /&gt;<br>  &lt;button id=&quot;sendBtn&quot;&gt;Send Message&lt;/button&gt;<br>  &lt;h3&gt;Messages&lt;/h3&gt;<br>  &lt;ul id=&quot;messages&quot;&gt;&lt;/ul&gt;<br>  &lt;script src=&quot;./main.js&quot;&gt;&lt;/script&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</pre><p>This page contains an input field, a button, a connection status, and a list where messages will appear.</p><h4>Step 4: Create the browser-side JavaScript</h4><p>Create main.js:</p><pre>const status = document.getElementById(&quot;status&quot;);<br>const input = document.getElementById(&quot;messageInput&quot;);<br>const sendBtn = document.getElementById(&quot;sendBtn&quot;);<br>const messages = document.getElementById(&quot;messages&quot;);<br><br>const socket = new WebSocket(&quot;ws://localhost:8080&quot;);<br>socket.onopen = () =&gt; {<br>    status.textContent = &quot;Connected&quot;;<br>};<br>socket.onmessage = (event) =&gt; {<br>    const li = document.createElement(&quot;li&quot;);<br>    li.textContent = event.data;<br>    messages.appendChild(li);<br>};<br>socket.onclose = () =&gt; {<br>    status.textContent = &quot;Disconnected&quot;;<br>};<br>socket.onerror = (error) =&gt; {<br>    console.error(&quot;WebSocket error:&quot;, error);<br>    status.textContent = &quot;Connection error&quot;;<br>};<br>sendBtn.addEventListener(&quot;click&quot;, () =&gt; {<br>    const message = input.value.trim();<br>    if (!message) return;<br>    if (socket.readyState !== WebSocket.OPEN) {<br>        status.textContent = &quot;Not connected&quot;;<br>        return;<br>    }<br>    socket.send(message);<br>    input.value = &quot;&quot;;<br>});</pre><p>Let’s break down the important parts.</p><p><strong>Creating the connection:</strong></p><pre>const socket = new WebSocket(&quot;ws://localhost:8080&quot;);</pre><p>This initiates the WebSocket handshake with our Node.js server.</p><p><strong>Waiting for the connection:</strong></p><pre>socket.onopen = () =&gt; {<br>  status.textContent = &quot;Connected&quot;;<br>};</pre><p>The browser should send messages only when the connection is open.</p><p><strong>Sending a message:</strong></p><pre>socket.send(message);</pre><p>The message travels to the server through the established connection.</p><p><strong>Receiving the server response:</strong></p><pre>socket.onmessage = (event) =&gt; {<br>  const li = document.createElement(&quot;li&quot;);<br><br>  li.textContent = event.data;<br>  messages.appendChild(li);<br>};</pre><p>The browser receives the response and displays it on the page.</p><h4>Step 5: Run the application</h4><p>Start the WebSocket server:</p><pre>node server.js</pre><p>Then open index.html through a local development server, such as VS Code Live Server.</p><p>Type a message and click <strong>Send Message</strong>.</p><p>You’ll see the server’s response appear in the list.</p><p>For example:</p><pre>Browser sends:<br>Hello Server!<br><br>Server responds:<br>Server received: Hello Server!</pre><h3>What happens behind the scenes?</h3><p>Let’s put everything together:</p><ol><li>The browser creates a WebSocket connection.</li><li>The server accepts the connection.</li><li>The browser sends a message using socket.send().</li><li>The server receives the message through its message event.</li><li>The server sends a response using socket.send().</li><li>The browser receives the response through onmessage.</li><li>The UI displays the response without a page refresh.</li></ol><p>This is the basic request-and-response pattern using WebSockets.</p><p>Although our example echoes messages back to the same client, a chat application can use the same connection to send messages to other connected users. The server would need to manage clients, authenticate them, and route messages to the appropriate recipients.</p><h3>5. Implement the Same Example in React</h3><p>Now let’s implement the same application using React.</p><p>The communication protocol doesn’t change. We still create a WebSocket connection, send messages, and receive responses.</p><p>What changes is how we manage the connection and update the UI.</p><p>Assume we’re using React with Vite.</p><p>Our project structure will look like this:</p><pre>src/<br>    App.jsx</pre><p>We’ll use the same Node.js WebSocket server from the previous example.</p><h4>Step 1: Create the React component</h4><p>Create App.jsx:</p><pre>import { useEffect, useRef, useState } from &quot;react&quot;;<br><br>function App() {<br>  const socketRef = useRef(null);<br>  const [message, setMessage] = useState(&quot;&quot;);<br>  const [messages, setMessages] = useState([]);<br>  const [status, setStatus] = useState(&quot;Connecting...&quot;);<br><br>  useEffect(() =&gt; {<br>    const socket = new WebSocket(&quot;ws://localhost:8080&quot;);<br>    socketRef.current = socket;<br>    socket.onopen = () =&gt; {<br>      setStatus(&quot;Connected&quot;);<br>    };<br>    socket.onmessage = (event) =&gt; {<br>      setMessages((prevMessages) =&gt; [...prevMessages, event.data]);<br>    };<br>    socket.onclose = () =&gt; {<br>      setStatus(&quot;Disconnected&quot;);<br>    };<br>    socket.onerror = (error) =&gt; {<br>      console.error(&quot;WebSocket error:&quot;, error);<br>      setStatus(&quot;Connection error&quot;);<br>    };<br>    return () =&gt; {<br>      socket.close();<br>      socketRef.current = null;<br>    };<br>  }, []);<br><br>  const handleSend = () =&gt; {<br>    const trimmedMessage = message.trim();<br>    const socket = socketRef.current;<br>    if (!trimmedMessage) return;<br>    if (!socket || socket.readyState !== WebSocket.OPEN) {<br>      setStatus(&quot;Not connected&quot;);<br>      return;<br>    }<br>    socket.send(trimmedMessage);<br>    setMessage(&quot;&quot;);<br>  };<br><br>  return (<br>    &lt;div&gt;<br>      &lt;h1&gt;WebSocket in React&lt;/h1&gt;<br>      &lt;p&gt;Status: {status}&lt;/p&gt;<br>      &lt;input<br>        value={message}<br>        onChange={(event) =&gt; setMessage(event.target.value)}<br>        placeholder=&quot;Enter your message&quot;<br>      /&gt;<br>      &lt;button onClick={handleSend}&gt;Send Message&lt;/button&gt;<br>      &lt;h3&gt;Messages&lt;/h3&gt;<br>      &lt;ul&gt;<br>        {messages.map((item, index) =&gt; (<br>          &lt;li key={index}&gt;{item}&lt;/li&gt;<br>        ))}<br>      &lt;/ul&gt;<br>    &lt;/div&gt;<br>  );<br>}<br><br>export default App;</pre><p>That’s it! We’ve implemented the same WebSocket communication flow in React.</p><p>Let’s understand the two most important concepts in this implementation.</p><h3>6. Why Do We Store the WebSocket Instance in useRef?</h3><p>This is a common question in React interviews.</p><p>Look at this line:</p><pre>const socketRef = useRef(null);</pre><p>We use useRef to store a reference to the WebSocket instance.</p><p>Later, when we create the connection, we assign it:</p><pre>const socket = new WebSocket(&quot;ws://localhost:8080&quot;);<br><br>socketRef.current = socket;</pre><p>When the user clicks the Send Message button, we access the same instance:</p><pre>const socket = socketRef.current;<br><br>if (socket?.readyState === WebSocket.OPEN) {<br>  socket.send(message);<br>}</pre><p>But why don’t we create a new WebSocket directly inside the component?</p><h3>What happens if we create a WebSocket during every render?</h3><p>Consider this incorrect approach:</p><pre>function App() {<br>  const socket = new WebSocket(&quot;ws://localhost:8080&quot;);<br><br>  const [count, setCount] = useState(0);<br><br>  return (<br>    &lt;button onClick={() =&gt; setCount(count + 1)}&gt;<br>      Count: {count}<br>    &lt;/button&gt;<br>  );<br>}</pre><p>Every time React renders this component, the component function runs again.</p><p>That means a new WebSocket connection is initiated on each render.</p><p>Imagine the following sequence:</p><pre>Initial Render<br>      |<br>      v<br>WebSocket A created<br><br>User clicks button<br>      |<br>      v<br>State changes<br>      |<br>      v<br>Component renders again<br>      |<br>      v<br>WebSocket B created<br><br>User clicks again<br>      |<br>      v<br>Component renders again<br>      |<br>      v<br>WebSocket C created</pre><p>The previous connections aren’t automatically closed just because React rendered again.</p><p>This can result in multiple unnecessary connections, duplicated messages, wasted resources, and difficult-to-debug behavior.</p><p><strong>A React component can render many times, but we generally want to maintain one connection for its intended lifetime.</strong></p><h3>How does useRef solve the problem?</h3><p>A ref provides a stable object whose .current property persists across renders.</p><pre>const socketRef = useRef(null);</pre><p>React preserves that ref object between renders. Updating .current doesn&#39;t trigger a re-render.</p><p>We create the WebSocket inside useEffect:</p><pre>useEffect(() =&gt; {<br>  const socket = new WebSocket(&quot;ws://localhost:8080&quot;);<br><br>  socketRef.current = socket;<br>    return () =&gt; {<br>      socket.close();<br>      socketRef.current = null;<br>    };<br>}, []);</pre><p>The empty dependency array means the Effect doesn’t rerun after every ordinary re-render. It sets up the connection after mounting and cleans it up when the component unmounts.</p><p>In development, React Strict Mode can perform an additional setup-and-cleanup cycle. This is intentional and helps reveal lifecycle bugs.</p><p>The resulting flow is:</p><pre>Component mounts<br>       |<br>       v<br>Effect creates WebSocket<br>       |<br>       v<br>socketRef.current = Socket A<br>       |<br>       v<br>User sends a message<br>       |<br>       v<br>State updates<br>       |<br>       v<br>Component re-renders<br>       |<br>       v<br>Same ref object<br>       |<br>       v<br>Same WebSocket A</pre><p>We don’t initiate a new connection on every render.</p><h3>Why not use useState for the WebSocket?</h3><p>You technically can store a WebSocket instance in state, but it usually isn’t necessary.</p><p><strong>useState</strong></p><ul><li>Stores data that affects the UI.</li><li>Updating state triggers a re-render.</li><li>The WebSocket instance itself doesn’t usually need to be rendered.</li></ul><p><strong>useRef</strong></p><ul><li>Stores a mutable reference across renders.</li><li>Updating .current doesn&#39;t trigger a re-render.</li><li>Works well for imperative objects such as WebSockets, Web Workers, and DOM references.</li></ul><p>The WebSocket instance is an external resource that we need to interact with. It isn’t UI data.</p><p>That’s why useRef is a natural choice.</p><p>Meanwhile, messages and connection status belong in state:</p><pre>const [messages, setMessages] = useState([]);<br>const [status, setStatus] = useState(&quot;Connecting...&quot;);</pre><p>When a message arrives, we update state:</p><pre>socket.onmessage = (event) =&gt; {<br>  setMessages((prevMessages) =&gt; [<br>    ...prevMessages,<br>    event.data<br>  ]);<br>};</pre><p>React then renders the updated messages.</p><p>The key distinction is:</p><p><strong>useRef manages the connection; </strong><strong>useState manages the UI.</strong></p><p>One nuance worth remembering: useRef by itself doesn&#39;t prevent a new WebSocket from being created on every render. The important part is creating the connection inside a lifecycle-managed Effect and using the ref to retain access to that instance.</p><h3>7. What Happens When the WebSocket Disconnects?</h3><p>Real-world connections aren’t permanent.</p><p>A connection can close because of:</p><ul><li>Network interruptions.</li><li>Server restarts.</li><li>Browser or device connectivity changes.</li><li>Server-side idle timeouts.</li><li>Application shutdown.</li></ul><p>In our example, we handle a close event:</p><pre>socket.onclose = () =&gt; {<br>  setStatus(&quot;Disconnected&quot;);<br>};</pre><p>But displaying a disconnected status isn’t enough for a production application.</p><p>A chat application, for example, may need to reconnect automatically.</p><h3>A basic reconnection strategy</h3><p>Conceptually, the flow might look like this:</p><pre>WebSocket disconnects<br>         |<br>         v<br>Wait before reconnecting<br>         |<br>         v<br>Attempt a new connection<br>         |<br>    +----+----+<br>    |         |<br> Success    Failure<br>    |         |<br>    v         v<br>Connected   Wait longer<br>              |<br>              v<br>        Retry connection</pre><p>A common technique is <strong>exponential backoff</strong>, where the delay increases after consecutive failures.</p><p>For example:</p><pre>Attempt 1 → Wait 1 second<br>Attempt 2 → Wait 2 seconds<br>Attempt 3 → Wait 4 seconds<br>Attempt 4 → Wait 8 seconds</pre><p>In production, you’d typically cap the delay and add jitter to avoid many clients reconnecting simultaneously.</p><p>You should also consider:</p><ul><li>Cleaning up pending timers.</li><li>Avoiding duplicate connections.</li><li>Detecting when the user is intentionally disconnecting.</li><li>Re-authenticating if necessary.</li><li>Resubscribing to the required channels or topics.</li><li>Handling messages that were missed while disconnected.</li></ul><p>Our beginner example intentionally doesn’t implement reconnection logic so that the core communication flow remains easy to understand.</p><h3>8. When Should You Use WebSockets?</h3><p>WebSockets are especially useful when an application requires ongoing, low-latency communication between the browser and server.</p><p>Common examples include:</p><p><strong>1. Chat applications</strong></p><p>Messages can be delivered to connected users without repeated polling.</p><p><strong>2. Live notifications</strong></p><p>Applications can push new notifications when events occur.</p><p><strong>3. Real-time dashboards</strong></p><p>Monitoring dashboards can receive live updates from the backend.</p><p><strong>4. Multiplayer games</strong></p><p>Players can exchange frequent state updates with a game server.</p><p><strong>5. Collaborative applications</strong></p><p>Multiple users can exchange editing or presence updates in near real time.</p><p><strong>6. Live order tracking</strong></p><p>A delivery or order-status page can receive updates as the backend reports new events.</p><p>However, WebSockets aren’t necessary for every API interaction.</p><p>If a page simply needs to fetch a product list once, a normal HTTP request is usually sufficient.</p><p>If the server needs to stream updates in one direction, Server-Sent Events (SSE) may be a simpler option.</p><p>The correct choice depends on how frequently data changes, which direction messages need to travel, and how the application handles connections.</p><h3>9. WebSockets vs HTTP vs Server-Sent Events</h3><p>It’s useful to understand how these technologies differ.</p><p><strong>HTTP request-response</strong></p><pre>Client → Request → Server<br>Client ← Response ← Server</pre><p>Best suited for fetching data, submitting forms, and ordinary API operations.</p><p><strong>WebSockets</strong></p><pre>Client ⇄ Server</pre><p>Supports two-way communication over a persistent connection.</p><p>Best suited for chat, interactive real-time features, and frequent bidirectional messaging.</p><p><strong>Server-Sent Events (SSE)</strong></p><pre>Client → Establish connection → Server<br>Client ← Updates ← Server</pre><p>Provides a persistent connection for server-to-client event streaming. The client normally uses HTTP to establish the stream, and the server sends events over it.</p><p>Best suited for live feeds, progress updates, and notifications when the client doesn’t need a bidirectional messaging channel.</p><p>Remember: WebSockets don’t automatically guarantee delivery of every message across disconnections. If your application needs reliable message history, acknowledgements, or recovery after reconnecting, you’ll need to design those features at the application or protocol level.</p><h3>10. Important Security Considerations</h3><p>A persistent connection still needs the same careful security thinking as other network communication.</p><p>Keep these points in mind:</p><ul><li><strong>Use </strong><strong>wss:// in production.</strong> This encrypts communication using TLS.</li><li><strong>Authenticate connections.</strong> Establish how the server identifies the user.</li><li><strong>Authorize messages.</strong> Being connected doesn’t mean the user can perform every action.</li><li><strong>Validate incoming data.</strong> Never blindly trust client messages.</li><li><strong>Consider Cross-Site WebSocket Hijacking.</strong> Validate the Origin header and use appropriate authentication and CSRF-related protections for cookie-authenticated connections.</li><li><strong>Handle connection limits.</strong> A server must be prepared to manage large numbers of concurrent clients.</li><li><strong>Avoid logging sensitive data.</strong> Authentication tokens, private messages, and personal information should not be exposed in logs.</li></ul><p>WebSockets provide the communication channel, but the application remains responsible for security, permissions, validation, and business logic.</p><h3>Conclusion</h3><p>WebSockets are a powerful browser technology for building real-time applications.</p><p>We’ve learned that:</p><ul><li>Traditional HTTP request-response communication is useful for ordinary API operations.</li><li>Polling repeatedly asks the server whether new information is available.</li><li>WebSockets establish a persistent, bidirectional communication channel.</li><li>send() transmits messages, while onmessage receives them.</li><li>A WebSocket application needs both a client and a server.</li><li>In React, useRef stores the connection instance across renders.</li><li>useEffect manages the connection lifecycle and cleanup.</li><li>useState updates the UI when messages or connection status change.</li><li>Real-world applications need to consider reconnection, authentication, authorization, and error handling.</li></ul><p>The key takeaway is simple:</p><p><strong>Use HTTP when you need ordinary request-response communication, and consider WebSockets when your application needs ongoing, bidirectional communication with the server.</strong></p><p>Once you understand this pattern, you can apply it to chat applications, live dashboards, collaborative tools, and other real-time experiences.</p><h3>What’s Next?</h3><p>Now that you understand the basics of WebSockets, here are some topics worth exploring next:</p><ul><li><strong>WebSockets vs SSE vs Long Polling:</strong> Understand which communication strategy fits different use cases.</li><li><strong>WebSocket Reconnection:</strong> Implement exponential backoff, retry limits, and connection recovery.</li><li><strong>WebSockets in React:</strong> Build a reusable custom hook that manages connection setup, messages, and cleanup.</li><li><strong>WebSocket Authentication:</strong> Learn how to authenticate connections and authorize messages securely.</li><li><strong>Scaling WebSocket Applications:</strong> Explore how multiple backend instances coordinate events using a shared messaging system.</li><li><strong>WebSocket vs Web Worker:</strong> Understand the difference between background computation and real-time network communication.</li></ul><p>Happy coding! ✌️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9e2ad7f168bc" width="1" height="1" alt=""><hr><p><a href="https://javascript.plainenglish.io/websockets-in-javascript-a-beginners-guide-to-real-time-communication-9e2ad7f168bc">WebSockets in JavaScript: A Beginner’s Guide to Real-Time Communication</a> was originally published in <a href="https://javascript.plainenglish.io">JavaScript in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Web Workers in JavaScript: A Beginner’s Guide to Background Processing]]></title>
            <link>https://javascript.plainenglish.io/web-workers-in-javascript-a-beginners-guide-to-background-processing-a75dafa34a30?source=rss-faedefd908b4------2</link>
            <guid isPermaLink="false">https://medium.com/p/a75dafa34a30</guid>
            <category><![CDATA[web-workers-in-javascript]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[performance]]></category>
            <category><![CDATA[frontend-development]]></category>
            <dc:creator><![CDATA[Rohit Kuwar]]></dc:creator>
            <pubDate>Mon, 19 Oct 2026 09:14:45 GMT</pubDate>
            <atom:updated>2026-10-19T11:05:39.992Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*q5prXmV-JYaAisXyvRlxrw.png" /><figcaption>Web Workers in JavaScript: A Beginner’s Guide to Background Processing</figcaption></figure><p>Have you ever clicked a button on a website and noticed that the entire page becomes unresponsive for a few seconds?</p><p>Maybe the application is processing a large dataset, performing complex calculations, filtering thousands of records, or parsing a huge JSON file.</p><p>The browser feels frozen. Animations stop, buttons stop responding, and the user has to wait until the operation finishes.</p><p><strong>Why does this happen, and how can we prevent it?</strong></p><p>One solution is to use <strong>Web Workers</strong>.</p><p>Web Workers allow JavaScript to execute tasks in a background thread, separate from the main thread that handles the user interface. This helps keep the UI responsive while computationally expensive work is running.</p><p>In this article, we’ll understand Web Workers from scratch, learn how they communicate with the main thread, build a simple example using HTML and JavaScript, and then implement the same example in React.</p><p>No prior knowledge of Web Workers is required.</p><h3>1. First, Understand the Problem</h3><p>Before learning about Web Workers, let’s understand how JavaScript typically executes code in a browser.</p><p>JavaScript running on the main thread is responsible for executing application code and coordinating UI-related work, including DOM updates, event handlers, and rendering-related tasks.</p><p>Consider this example:</p><pre>console.log(&quot;Start&quot;);<br><br>let sum = 0;<br>for (let i = 0; i &lt; 1000000000; i++) {<br>  sum += i;<br>}<br><br>console.log(&quot;Calculation completed&quot;);</pre><p>The loop performs a large number of calculations synchronously.</p><p>While this loop is running, the browser’s main thread cannot process other JavaScript tasks normally. As a result, the UI may become unresponsive.</p><p>Imagine this happening in a React application while a user is typing in a search box or clicking a button.</p><p>That’s a poor user experience.</p><p>The problem isn’t that JavaScript cannot perform calculations. The problem is <strong>where those calculations are running</strong>.</p><h3>2. What Is a Web Worker?</h3><p>A Web Worker is a browser API that allows JavaScript code to run in a separate background thread.</p><p>Instead of performing a computationally expensive operation on the main thread, we can move that operation into a separate JavaScript file and execute it inside a Worker.</p><p>Conceptually, the architecture looks like this:</p><pre>Browser<br>                    |<br>          +---------+---------+<br>          |                   |<br>      Main Thread         Worker Thread<br>          |                   |<br>     UI and DOM            Heavy work<br>     Event handlers        Calculations<br>     User interactions     Data processing<br>          |                   |<br>          +---- Messages -----+</pre><p>The main thread remains responsible for the user interface, while the Worker performs its assigned task.</p><p>For example, suppose your application needs to process a large dataset.</p><p>Without a Worker:</p><pre>Main Thread<br>    |<br>    | Process 10 million records<br>    |<br>    | UI may become unresponsive<br>    |<br>    v<br>Calculation completed</pre><p>With a Worker:</p><pre>Main Thread                 Worker Thread<br>     |                            |<br>     | Send data ----------------&gt;|<br>     |                            | Process records<br>     | UI remains responsive      | Perform calculation<br>     |                            |<br>     |&lt;---------------- Send result<br>     |<br>     v<br>Update UI</pre><p>This is the basic idea behind Web Workers.</p><h3>Important things to know</h3><ul><li>A Worker runs separately from the page’s main JavaScript thread.</li><li>A Worker cannot directly access the page’s DOM.</li><li>A Worker communicates with the main thread through messages.</li><li>A Worker has its own JavaScript execution context.</li><li>Workers are useful for CPU-intensive tasks, not for making every asynchronous operation faster.</li></ul><p><strong>One important distinction:</strong> Web Workers don’t automatically make a calculation faster. Their primary benefit is that the work no longer blocks the main thread. They can also use additional CPU resources, depending on the task and available hardware.</p><h3>3. How Does a Web Worker Exchange Data?</h3><p>This is one of the most important concepts to understand.</p><p>The main thread and the Worker do not normally share ordinary JavaScript variables.</p><p>For example, this variable in your main JavaScript file:</p><pre>let count = 10;</pre><p>isn’t automatically available inside your Worker.</p><p>Instead, the two communicate using a message-passing mechanism.</p><p>There are four important concepts:</p><p><strong>new Worker()</strong> — Creates a Worker instance.</p><p><strong>postMessage()</strong> — Sends a message to the other side.</p><p><strong>onmessage</strong> — Receives messages from the other side.</p><p><strong>terminate()</strong> — Immediately stops a Worker when you no longer need it.</p><p>The communication flow looks like this:</p><pre>Main Thread                      Worker<br>     |                              |<br>     | new Worker(&quot;worker.js&quot;)      |<br>     |-----------------------------&gt;|<br>     |                              |<br>     | postMessage(input)           |<br>     |-----------------------------&gt;|<br>     |                              |<br>     |                         onmessage<br>     |                         Process input<br>     |                              |<br>     |&lt;-----------------------------|<br>     |         postMessage(result)  |<br>     |                              |<br>     | onmessage                    |<br>     | Receive result               |</pre><h3>How is data transferred?</h3><p>When you call postMessage(), the browser generally uses the <strong>structured clone algorithm</strong> to copy supported data into the receiving context.</p><p>For example:</p><pre>worker.postMessage({<br>  type: &quot;calculate&quot;,<br>  number: 100<br>});</pre><p>The Worker receives a corresponding message:</p><pre>self.onmessage = (event) =&gt; {<br>  console.log(event.data.number); // 100<br>};</pre><p>The Worker can then send a response:</p><pre>self.postMessage({<br>  result: 5050<br>});</pre><p>And the main thread receives it:</p><pre>worker.onmessage = (event) =&gt; {<br>  console.log(event.data.result); // 5050<br>};</pre><p>You can send supported objects, arrays, strings, numbers, and other structured-cloneable values.</p><p>Some values, such as functions and DOM nodes, cannot be cloned this way.</p><p>For large binary data, transferable objects such as ArrayBuffer can be useful because ownership can be transferred instead of copying the underlying data. We&#39;ll leave that advanced topic for another article.</p><p>Now let’s build something practical.</p><h3>4. Build Your First Web Worker Using HTML and JavaScript</h3><p>We’ll create a small application that calculates the sum of numbers from 1 to 50 million.</p><p>The user clicks a button, the Worker performs the calculation, and the result appears on the page.</p><p>Our project contains three files:</p><pre>web-worker-demo/<br>    index.html<br>    main.js<br>    worker.js</pre><h4>Step 1: Create the HTML file</h4><p>Create index.html:</p><pre>&lt;!DOCTYPE html&gt;<br>&lt;html lang=&quot;en&quot;&gt;<br>&lt;head&gt;<br>  &lt;meta charset=&quot;UTF-8&quot; /&gt;<br>  &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&gt;<br>  &lt;title&gt;Web Worker Demo&lt;/title&gt;<br>&lt;/head&gt;<br>&lt;body&gt;<br>  &lt;h1&gt;Web Worker Demo&lt;/h1&gt;<br>  &lt;button id=&quot;calculateBtn&quot;&gt;<br>    Calculate Sum<br>  &lt;/button&gt;<br>  &lt;p id=&quot;status&quot;&gt;Ready&lt;/p&gt;<br>  &lt;p id=&quot;result&quot;&gt;&lt;/p&gt;<br>  &lt;script src=&quot;./main.js&quot;&gt;&lt;/script&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</pre><p>The page contains a button, a status message, and a place to display the result.</p><h4>Step 2: Create the Worker file</h4><p>Create worker.js:</p><pre>self.onmessage = (event) =&gt; {<br>  const number = event.data;<br>  let sum = 0;<br><br>  for (let i = 1; i &lt;= number; i++) {<br>    sum += i;<br>  }<br>  self.postMessage(sum);<br>};</pre><p>Let’s understand what happens here.</p><p>self refers to the Worker&#39;s global scope.</p><p>self.onmessage listens for messages sent from the main thread.</p><p>When a message arrives, the Worker reads the number from event.data and performs the calculation.</p><p>Finally:</p><pre>self.postMessage(sum);</pre><p>sends the calculated result back to the main thread.</p><p>Notice that the Worker doesn’t access document.getElementById() or update the HTML. It only performs the calculation and sends back the result.</p><h4>Step 3: Connect the Worker to the main thread</h4><p>Create main.js:</p><pre>const calculateBtn = document.getElementById(&quot;calculateBtn&quot;);<br>const status = document.getElementById(&quot;status&quot;);<br>const result = document.getElementById(&quot;result&quot;);<br>const worker = new Worker(&quot;./worker.js&quot;);<br>calculateBtn.addEventListener(&quot;click&quot;, () =&gt; {<br>    status.textContent = &quot;Calculating...&quot;;<br>    result.textContent = &quot;&quot;;<br>    worker.postMessage(50000000);<br>});<br>worker.onmessage = (event) =&gt; {<br>    result.textContent = `Result: ${event.data}`;<br>    status.textContent = &quot;Calculation completed!&quot;;<br>};<br>worker.onerror = (event) =&gt; {<br>    status.textContent = &quot;Something went wrong.&quot;;<br>    console.error(event.message);<br>};</pre><h4>Step 4: Run the application</h4><p>Run the project using a local development server. For example, you can use the Live Server extension in VS Code.</p><p>Avoid opening the HTML file directly through a file:// URL, because browser security restrictions can interfere with Worker loading.</p><p>Click <strong>Calculate Sum</strong>.</p><p>The application sends 50000000 to the Worker. The Worker performs the calculation and sends the result back.</p><p>The result will be:</p><pre>1250000025000000</pre><p>While the calculation is running, try clicking other parts of the page or selecting text. The page should remain responsive because the calculation is running outside the main thread.</p><p>The exact calculation time depends on your device and browser.</p><h3>What did we accomplish?</h3><p>Let’s summarize the flow:</p><ol><li>The main thread creates a Worker.</li><li>The user clicks the button.</li><li>The main thread sends a number using postMessage().</li><li>The Worker receives the number through onmessage.</li><li>The Worker calculates the sum.</li><li>The Worker sends the result back using postMessage().</li><li>The main thread receives the result and updates the UI.</li></ol><p>That’s a complete, working example of Web Worker communication.</p><h3>5. Implement the Same Example in React</h3><p>Now let’s implement the same functionality in React.</p><p>The calculation remains exactly the same. The difference is how we manage the Worker instance and update the UI.</p><p>For this example, assume you’re using React with Vite.</p><p>Our files will look like this:</p><pre>src/<br>    App.jsx<br>    sum.worker.js</pre><h4>Step 1: Create the Worker</h4><p>Create sum.worker.js:</p><pre>self.onmessage = (event) =&gt; {<br>  const number = event.data;<br>  let sum = 0;<br><br>  for (let i = 1; i &lt;= number; i++) {<br>    sum += i;<br>  }<br>  self.postMessage(sum);<br>};</pre><p>This is the same Worker we created earlier.</p><p>The Worker doesn’t need to know whether the main thread belongs to a plain JavaScript application or a React application.</p><p>It only needs to receive a message, perform its work, and return the result.</p><h4>Step 2: Create the React component</h4><p>Create App.jsx:</p><pre>import { useEffect, useRef, useState } from &quot;react&quot;;<br><br>function App() {<br>  const workerRef = useRef(null);<br>  const [result, setResult] = useState(null);<br>  const [status, setStatus] = useState(&quot;Ready&quot;);<br><br>  useEffect(() =&gt; {<br>    const worker = new Worker(<br>      new URL(&quot;./sum.worker.js&quot;, import.meta.url),<br>      { type: &quot;module&quot; }<br>    );<br>    workerRef.current = worker;<br>    worker.onmessage = (event) =&gt; {<br>      setResult(event.data);<br>      setStatus(&quot;Calculation completed!&quot;);<br>    };<br>    worker.onerror = (event) =&gt; {<br>      setStatus(&quot;Something went wrong.&quot;);<br>      console.error(event.message);<br>    };<br>    return () =&gt; {<br>      worker.terminate();<br>      workerRef.current = null;<br>    };<br>  }, []);<br><br>  const handleCalculate = () =&gt; {<br>    setStatus(&quot;Calculating...&quot;);<br>    setResult(null);<br>    workerRef.current?.postMessage(50000000);<br>  };<br><br>  return (<br>    &lt;div&gt;<br>      &lt;h1&gt;Web Worker in React&lt;/h1&gt;<br>      &lt;button onClick={handleCalculate}&gt;<br>        Calculate Sum<br>      &lt;/button&gt;<br>      &lt;p&gt;{status}&lt;/p&gt;<br>      {result !== null &amp;&amp; (<br>        &lt;p&gt;Result: {result}&lt;/p&gt;<br>      )}<br>    &lt;/div&gt;<br>  );<br>}<br><br>export default App;</pre><p>The example uses Vite’s Worker-friendly URL pattern so the bundler can resolve the Worker file correctly.</p><p>Let’s focus on the most important part of this implementation.</p><h3>6. Why Do We Store the Worker Instance in useRef?</h3><p>This is a common interview question, and understanding the reason is more important than memorizing the code.</p><p>Look at this line:</p><pre>const workerRef = useRef(null);</pre><p>We use useRef to store a reference to the Worker instance.</p><p>Later, we assign the instance:</p><pre>workerRef.current = worker;</pre><p>And when the user clicks the button, we access it:</p><pre>workerRef.current?.postMessage(50000000);</pre><p>But why not simply create the Worker inside the component?</p><h3>What happens if we create a Worker during every render?</h3><p>Consider this incorrect approach:</p><pre>function App() {<br>  const worker = new Worker(<br>    new URL(&quot;./sum.worker.js&quot;, import.meta.url),<br>    { type: &quot;module&quot; }<br>  );<br><br>  const [count, setCount] = useState(0);<br>  return (<br>    &lt;button onClick={() =&gt; setCount(count + 1)}&gt;<br>      Count: {count}<br>    &lt;/button&gt;<br>  );<br>}</pre><p>Every time the component renders, JavaScript executes the component function again.</p><p>That means a new Worker is created each time.</p><p>For example:</p><pre>Initial Render<br>      |<br>      v<br>Worker A created<br><br>User clicks button<br>      |<br>      v<br>State changes<br>      |<br>      v<br>Component renders again<br>      |<br>      v<br>Worker B created<br><br>User clicks button again<br>      |<br>      v<br>Component renders again<br>      |<br>      v<br>Worker C created</pre><p>The previous Workers aren’t automatically terminated just because React rendered again.</p><p>This can cause unnecessary resource consumption and leave multiple Workers running.</p><p><strong>The important point is that React components can render many times, but we usually want one Worker instance for the component’s lifetime.</strong></p><h3>How does useRef help?</h3><p>A ref provides a stable object whose .current property persists across renders.</p><pre>const workerRef = useRef(null);</pre><p>React retains that ref object between renders. Updating workerRef.current does not trigger a new render.</p><p>In our implementation, we create the Worker inside useEffect:</p><pre>useEffect(() =&gt; {<br>    const worker = new Worker(<br>        new URL(&quot;./sum.worker.js&quot;, import.meta.url), {<br>            type: &quot;module&quot;<br>        }<br>    );<br><br>    workerRef.current = worker;<br>    return () =&gt; {<br>        worker.terminate();<br>        workerRef.current = null;<br>    };<br>}, []);</pre><p>The empty dependency array means the Effect doesn’t rerun after every ordinary re-render. React runs the Effect after mounting and performs cleanup when the component unmounts.</p><p>In development, React Strict Mode may run an extra setup-and-cleanup cycle to help detect bugs. That’s expected; the cleanup ensures that the temporary Worker is terminated.</p><p>Now the flow looks like this:</p><pre>Component mounts<br>       |<br>       v<br>Effect creates Worker<br>       |<br>       v<br>workerRef.current = Worker A<br>       |<br>       v<br>User clicks button<br>       |<br>       v<br>State updates<br>       |<br>       v<br>Component re-renders<br>       |<br>       v<br>Same ref object<br>       |<br>       v<br>Same Worker A</pre><p>We don’t create a new Worker on every render.</p><h3>Why not use useState instead?</h3><p>You technically can store an object in state, but it’s usually unnecessary for a Worker instance.</p><p>Compare the two:</p><p><strong>useState</strong></p><ul><li>Designed to store values that affect rendering.</li><li>Updating state triggers a re-render.</li><li>A Worker instance itself generally doesn’t need to be rendered.</li></ul><p><strong>useRef</strong></p><ul><li>Stores a mutable reference across renders.</li><li>Updating .current does not trigger a re-render.</li><li>Useful for imperative objects such as Workers, DOM references, and WebSocket instances.</li></ul><p>The Worker instance is an imperative resource, not UI data. That’s why useRef is a natural fit.</p><p>Meanwhile, the result and status belong in state:</p><pre>const [result, setResult] = useState(null);<br>const [status, setStatus] = useState(&quot;Ready&quot;);</pre><p>When the Worker returns a result, we call setResult(), which tells React to update the UI.</p><p>This gives us a useful separation:</p><p><strong>useRef manages the Worker instance; </strong><strong>useState manages the UI.</strong></p><p>One nuance: useRef alone doesn&#39;t prevent Worker creation on every render. The real solution is to create the Worker inside an Effect (or another appropriate lifecycle-managed setup) and use the ref to retain access to that instance.</p><h3>7. When Should You Use Web Workers?</h3><p>Web Workers are especially useful when the main thread needs to stay responsive while performing CPU-intensive operations.</p><p>Common examples include:</p><ul><li>Processing large datasets.</li><li>Performing complex calculations.</li><li>Parsing large files or JSON payloads.</li><li>Processing images or other binary data.</li><li>Running computationally intensive algorithms.</li><li>Performing client-side data transformations.</li><li>Supporting certain AI or machine-learning workloads in the browser.</li></ul><p>However, not every asynchronous operation needs a Worker.</p><p>For example, fetching data with fetch() is already asynchronous. You usually don&#39;t need a Worker simply to make an API request.</p><p>Similarly, small calculations that finish quickly may not justify the additional complexity of Worker creation and message passing.</p><p>Use a Worker when the work is sufficiently expensive to block the main thread.</p><h3>8. Important Limitations to Remember</h3><p>Before using Web Workers in a real application, keep these points in mind:</p><p><strong>1. Workers cannot directly manipulate the DOM.</strong></p><p>The Worker should send data back to the main thread, and the main thread should update the UI.</p><p><strong>2. Communication has a cost.</strong></p><p>Messages containing large objects can require significant copying and memory. Transferable objects can help with certain types of data.</p><p><strong>3. Workers need lifecycle management.</strong></p><p>Create them at the appropriate time and terminate them when they’re no longer needed.</p><p><strong>4. Error handling matters.</strong></p><p>Handle Worker errors and consider how the UI should respond if processing fails.</p><p><strong>5. Workers don’t replace good algorithms.</strong></p><p>An inefficient algorithm remains inefficient inside a Worker. Improve the algorithm when possible, and use a Worker to prevent expensive work from blocking the UI.</p><h3>Conclusion</h3><p>Web Workers are a useful browser feature for running JavaScript outside the main UI thread.</p><p>We’ve learned that:</p><ul><li>The main thread manages the UI.</li><li>A Worker performs background computation.</li><li>postMessage() and onmessage enable communication.</li><li>The Worker can return results to the main thread.</li><li>In React, useRef provides a stable reference to the Worker instance.</li><li>useEffect is used to create the Worker and clean it up when appropriate.</li><li>useState updates the UI when the result arrives.</li></ul><p>The key takeaway is simple:</p><p><strong>Keep UI rendering and interaction on the main thread, and move suitable CPU-intensive work into a Web Worker.</strong></p><p>Once you understand this pattern, you can apply it to data processing, large-file handling, image processing, and other tasks that would otherwise make a web application feel slow.</p><h3>What’s Next?</h3><p>Now that you understand the basics of Web Workers, here are some topics worth exploring next:</p><ul><li><strong>Web Workers vs Service Workers:</strong> Understand how background computation differs from caching, offline support, and network interception.</li><li><strong>Transferable Objects:</strong> Learn how to transfer ownership of an ArrayBuffer without copying its underlying data.</li><li><strong>Worker Communication:</strong> Explore request IDs, multiple concurrent tasks, and structured message formats.</li><li><strong>Web Worker Cleanup in React:</strong> Learn how to handle pending tasks, errors, cancellation, and component unmounting.</li><li><a href="https://github.com/googlechromelabs/comlink"><strong>Comlink</strong></a><strong>:</strong> Explore a library that makes communicating with Web Workers feel more like calling ordinary functions.</li></ul><p>Happy coding! ✌️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a75dafa34a30" width="1" height="1" alt=""><hr><p><a href="https://javascript.plainenglish.io/web-workers-in-javascript-a-beginners-guide-to-background-processing-a75dafa34a30">Web Workers in JavaScript: A Beginner’s Guide to Background Processing</a> was originally published in <a href="https://javascript.plainenglish.io">JavaScript in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Where Should You Store Authentication Credentials? localStorage vs HttpOnly Cookies vs Session IDs]]></title>
            <link>https://javascript.plainenglish.io/where-should-you-store-authentication-credentials-localstorage-vs-httponly-cookies-vs-session-ids-97d073873521?source=rss-faedefd908b4------2</link>
            <guid isPermaLink="false">https://medium.com/p/97d073873521</guid>
            <category><![CDATA[web-security]]></category>
            <category><![CDATA[authentication]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <dc:creator><![CDATA[Rohit Kuwar]]></dc:creator>
            <pubDate>Tue, 18 Aug 2026 14:32:42 GMT</pubDate>
            <atom:updated>2026-10-19T11:04:56.742Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PVuOJY8tcqPKquExrE_9aA.png" /><figcaption>Where should you store your token?</figcaption></figure><h3>Authentication is one of those topics that every frontend developer eventually encounters.</h3><p>You log in, the backend validates your credentials, and somehow the application needs to remember:</p><blockquote><strong><em>“This user is authenticated.”</em></strong></blockquote><p>But where should that authentication information be stored?</p><p>You will commonly come across three approaches:</p><ol><li><strong>Store a token in <em>localstorage</em></strong></li><li><strong>Store a token in an <em>httpOnly </em>cookie</strong></li><li><strong>Use a server-side session with a Session ID</strong></li></ol><p>All three approaches can work. The important question is <strong>when should we use each one, and what security trade-offs do they introduce?</strong></p><p>Let’s break them down from a frontend developer’s perspective.</p><h3>First: What Are We Actually Storing?</h3><p>Before comparing the three approaches, we need to understand one important distinction.</p><p>There are two common authentication models:</p><h3>Token-based authentication</h3><p>The server generates a token after successful login.</p><p>For example:</p><pre>User → Login<br>        ↓<br>Backend validates credentials<br>        ↓<br>Backend generates token<br>        ↓<br>Browser stores token<br>        ↓<br>Browser uses token for future requests</pre><p>The token could be a JWT:</p><pre>eyJhbGciOiJIUzI1NiIs...</pre><p>The backend can verify the token and determine who the user is.</p><h3>Session-based authentication</h3><p>Instead of putting all authentication information into a token, the server creates a session.</p><p>For example:</p><pre>Session ID: abc123xyz</pre><p>The server maintains something like:</p><pre>abc123xyz → {<br>    userId: 101,<br>    role: &quot;admin&quot;<br>}</pre><p>The browser only needs to send the Session ID.</p><p>This distinction becomes important when we compare the three approaches.</p><h3>1. Storing a Token in localStorage</h3><p>This is probably the easiest approach to understand.</p><p>After login, the backend returns a token:</p><pre>{<br>  &quot;accessToken&quot;: &quot;abc123xyz&quot;<br>}</pre><p>The frontend stores it:</p><pre>localStorage.setItem(&quot;accessToken&quot;, data.accessToken);</pre><p>For future API requests:</p><pre>const token = localStorage.getItem(&quot;accessToken&quot;);<br><br>fetch(&quot;/api/profile&quot;, {<br>  headers: {<br>    Authorization: `Bearer ${token}`<br>  }<br>});</pre><p>The request looks conceptually like:</p><pre>GET /api/profile<br>Authorization: Bearer abc123xyz</pre><p>The backend reads the Authorization header and validates the token.</p><h4>How the Flow Works</h4><pre>            Login<br>Browser ───────────────→ Backend<br>                          │<br>                          │ Validate credentials<br>                          │ Generate token<br>                          ↓<br>Browser ←─────────────── Token<br><br>        ↓<br><br>localStorage<br>accessToken = abc123xyz<br><br>        ↓<br><br>API Request<br>Authorization: Bearer abc123xyz<br><br>        ↓<br><br>Backend validates token</pre><h4>Advantages of localStorage</h4><p><strong>1. Simple to implement</strong></p><p>It is very straightforward:</p><pre>localStorage.setItem(&quot;token&quot;, token);</pre><p>And later:</p><pre>const token = localStorage.getItem(&quot;token&quot;);</pre><p><strong>2. Useful for APIs</strong></p><p>You can explicitly attach the token to requests:</p><pre>headers: {<br>  Authorization: `Bearer ${token}`<br>}</pre><p>This is particularly convenient for applications communicating with APIs.</p><p><strong>3. The browser doesn’t automatically send it</strong></p><p>Unlike cookies, localStorage data isn&#39;t automatically attached to every HTTP request.</p><p>Your JavaScript code decides when to send it.</p><h4>Disadvantages of localStorage</h4><p>The biggest concern is <strong>XSS — Cross-Site Scripting</strong>.</p><p>JavaScript running on your page can access localStorage.</p><p>For example:</p><pre>const token = localStorage.getItem(&quot;accessToken&quot;);</pre><p>So if an attacker manages to execute malicious JavaScript in your application, that script may be able to read the token.</p><p>Conceptually:</p><pre>// Malicious script<br>const token = localStorage.getItem(&quot;accessToken&quot;);<br><br>// Attacker could potentially send the token somewhere</pre><p>That’s why storing sensitive authentication tokens in localStorage requires careful consideration of XSS protections.</p><p>Other considerations include:</p><ul><li>JavaScript can read the token.</li><li>You have to manually attach the token to requests.</li><li>You have to manually remove it during logout.</li><li>Token theft can allow an attacker to impersonate the user until the token expires or is revoked.</li></ul><h4>When might we use localStorage?</h4><p>You may encounter this approach in:</p><ul><li>Simple SPA applications</li><li>Internal applications</li><li>Learning projects</li><li>Some API-driven frontend applications</li><li>Applications where the team has consciously accepted the security trade-off</li></ul><p>For security-sensitive production applications, you should carefully evaluate whether JavaScript-accessible token storage is appropriate.</p><h3>2. Storing a Token in an HttpOnly Cookie</h3><p>Now let’s look at cookies.</p><p>Instead of JavaScript storing the token:</p><pre>localStorage.setItem(&quot;token&quot;, token);</pre><p>the backend can instruct the browser to store it in a cookie.</p><p>For example, conceptually:</p><pre>Set-Cookie: accessToken=abc123xyz;</pre><p>A more secure authentication cookie would commonly look something like:</p><pre>Set-Cookie:<br>  accessToken=abc123xyz;<br>  HttpOnly;<br>  Secure;<br>  SameSite=Lax;</pre><p>The important part here is:</p><pre>HttpOnly</pre><p>An HttpOnly cookie cannot be accessed through JavaScript.</p><p>So this won’t work:</p><pre>document.cookie</pre><p>for retrieving that HttpOnly authentication cookie.</p><p>But the browser can still send it with applicable requests.</p><h4>How the Flow Works</h4><pre>Login<br>Browser ───────────────→ Backend<br>                          │<br>                          │ Validate credentials<br>                          │ Generate token<br>                          ↓<br>Browser ←─────────────── Set-Cookie<br><br>        ↓<br><br>Browser stores cookie<br><br>        ↓<br><br>API Request<br>Cookie: accessToken=abc123xyz<br><br>        ↓<br><br>Backend reads cookie<br><br>        ↓<br><br>Validates token</pre><p>The frontend doesn’t need to manually do:</p><pre>Authorization: Bearer ...</pre><p>The browser handles the cookie according to its cookie rules.</p><h4>Conceptual Backend Code</h4><p>For example, with Express:</p><pre>res.cookie(&quot;accessToken&quot;, token, {<br>  httpOnly: true,<br>  secure: true,<br>  sameSite: &quot;lax&quot;<br>});<br><br>res.json({<br>  message: &quot;Login successful&quot;<br>});</pre><p>The frontend simply makes the request.</p><p>For cross-origin requests where cookies need to be included, the client may need:</p><pre>fetch(&quot;/api/profile&quot;, {<br>  credentials: &quot;include&quot;<br>});</pre><p>The exact CORS and cookie configuration depends on how the frontend and backend are hosted.</p><h4>Advantages of HttpOnly Cookies</h4><p><strong>1. JavaScript cannot read the authentication cookie</strong></p><p>This is the biggest advantage.</p><p>If the cookie is:</p><pre>HttpOnly</pre><p>JavaScript cannot retrieve its value through normal browser APIs.</p><p>That makes token theft through a straightforward localStorage read much harder.</p><p><strong>2. Browser handles the cookie</strong></p><p>The application doesn’t have to manually retrieve the token and add:</p><pre>Authorization: Bearer ...</pre><p>to every request.</p><p><strong>3. Secure cookies can be combined with additional protections</strong></p><p>Common cookie attributes include:</p><pre>HttpOnly<br>Secure<br>SameSite</pre><p>Each serves a different purpose.</p><ul><li><strong>HttpOnly</strong> → prevents JavaScript from reading the cookie.</li><li><strong>Secure</strong> → sends the cookie only over HTTPS.</li><li><strong>SameSite</strong> → controls when the browser sends the cookie in cross-site contexts.</li></ul><h4>Disadvantages of HttpOnly Cookies</h4><p>Cookies introduce an important security consideration:</p><p><strong>CSRF</strong></p><p>Because the browser automatically sends applicable cookies with requests, an attacker may try to trick a user’s browser into making an unwanted authenticated request.</p><p>This is where protections such as:</p><pre>SameSite<br>CSRF tokens<br>Origin/Referer validation</pre><p>can become important, depending on the application’s architecture.</p><p>So:</p><blockquote><strong><em>HttpOnly does not mean “completely secure.”</em></strong></blockquote><p>It primarily protects the cookie from being directly read by JavaScript.</p><p>You still need to think about CSRF, XSS, HTTPS, cookie configuration, authorization, and other security controls.</p><h4>When might we use HttpOnly Cookies?</h4><p>This approach is commonly suitable for:</p><ul><li>Traditional web applications</li><li>React/Vue/Angular applications with a backend</li><li>Applications where strong browser-based authentication security is important</li><li>Applications using JWT access/refresh tokens with cookie-based transport</li><li>Applications where you don’t want JavaScript to directly access authentication credentials</li></ul><p>A common modern pattern is:</p><pre>Short-lived access token<br>        +<br>HttpOnly Secure refresh-token cookie</pre><p>Although the exact architecture depends on the application’s requirements.</p><h3>3. Session ID</h3><p>Now let’s look at a different approach.</p><p>With a session-based authentication system, the server doesn’t necessarily send a JWT containing the user’s information.</p><p>Instead, after successful login, the server creates a session.</p><p>For example:</p><pre>Session ID: abc123</pre><p>The server stores something like:</p><pre>abc123 → {<br>    userId: 101,<br>    role: &quot;Admin&quot;<br>}</pre><p>The browser receives the Session ID, usually through a cookie:</p><pre>Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Lax</pre><p>The browser sends that cookie with subsequent requests.</p><h4>How the Flow Works</h4><pre>Login<br>Browser ─────────────────────────→ Backend<br>                                    │<br>                                    │ Validate credentials<br>                                    │<br>                                    │ Create session<br>                                    ↓<br>                              Session Store<br>                              abc123 → user 101<br>                                    │<br>Browser ←───────────────────────────┘<br>        sessionId=abc123<br><br>        ↓<br><br>Next API request<br>Cookie: sessionId=abc123<br><br>        ↓<br><br>Backend<br><br>        ↓<br><br>Find abc123 in session store<br><br>        ↓<br><br>userId = 101<br><br>        ↓<br><br>Authorize request</pre><p>Notice something important:</p><p>The browser doesn’t have the actual user session data.</p><p>It only has:</p><pre>abc123</pre><p>The actual session information lives on the server.</p><h4>Conceptual Backend Code</h4><p>With Express, you might use a session middleware:</p><pre>app.use(<br>  session({<br>    secret: &quot;my-secret&quot;,<br>    resave: false,<br>    saveUninitialized: false<br>  })<br>);</pre><p>After login:</p><pre>req.session.userId = user.id;<br><br>res.json({<br>  message: &quot;Login successful&quot;<br>});</pre><p>Later:</p><pre>app.get(&quot;/profile&quot;, (req, res) =&gt; {<br>  const userId = req.session.userId;<br><br>  // Fetch user and return profile<br>});</pre><p>The session middleware handles the Session ID cookie and session lookup.</p><p>In a production system, the session store would typically be something designed for shared/server-side session storage rather than relying on the default in-memory store.</p><h4>Advantages of Session-Based Authentication</h4><p><strong>1. Server has control over sessions</strong></p><p>The server can invalidate a session immediately.</p><p>For example:</p><pre>Session abc123 → deleted</pre><p>The next request using that Session ID can be rejected.</p><p><strong>2. Sensitive session information stays server-side</strong></p><p>The browser only stores:</p><pre>sessionId=abc123</pre><p>The actual session data remains on the server.</p><p><strong>3. Logout is straightforward</strong></p><p>The backend can destroy the session:</p><pre>req.session.destroy();</pre><p>After that, the Session ID is no longer associated with an authenticated session.</p><h4>Disadvantages of Session-Based Authentication</h4><p><strong>1. Server-side storage is required</strong></p><p>The server needs to maintain session information.</p><p>For example:</p><pre>abc123 → user 101<br>def456 → user 205<br>xyz789 → user 301</pre><p>With a large application, this becomes an infrastructure consideration.</p><p><strong>2. Scaling requires planning</strong></p><p>Suppose you have:</p><pre>Load Balancer<br>      ↓<br> ┌────┴────┐<br>Server A  Server B</pre><p>A user’s session might have been created on Server A.</p><p>If the next request goes to Server B, Server B needs access to the same session data.</p><p>This is why distributed session stores such as Redis are commonly used.</p><p><strong>3. Session management becomes the server’s responsibility</strong></p><p>You need to think about:</p><ul><li>Session expiration</li><li>Idle timeout</li><li>Session invalidation</li><li>Session storage</li><li>Session cleanup</li><li>Scaling</li></ul><h3>Token vs Cookie vs Session — An Important Clarification</h3><p>There is a common misconception that these are three completely independent technologies.</p><p>They aren’t.</p><p><strong>localStorage, cookies, and Session IDs describe different parts of the authentication architecture.</strong></p><p>For example:</p><h3>JWT + localStorage</h3><pre>JWT<br> ↓<br>localStorage<br> ↓<br>Authorization header</pre><h3>JWT + HttpOnly cookie</h3><pre>JWT<br> ↓<br>HttpOnly cookie<br> ↓<br>Cookie header</pre><h3>Session ID + cookie</h3><pre>Session ID<br> ↓<br>HttpOnly cookie<br> ↓<br>Cookie header<br> ↓<br>Server-side session store</pre><p>So:</p><blockquote><strong><em>A cookie is a storage/transport mechanism. A Session ID is an identifier for server-side session state. A JWT is a token format.</em></strong></blockquote><p>This distinction is extremely important in interviews.</p><h3>Side-by-Side Comparison</h3><pre>+----------------------------------+-------------------------+---------------------------+----------------------------+<br>| Feature                          | Token in localStorage   | Token in HttpOnly Cookie | Session ID                 |<br>+----------------------------------+-------------------------+---------------------------+----------------------------+<br>| Authentication model             | Token-based             | Token-based               | Session-based              |<br>| Stored in                        | localStorage            | Cookie                    | Usually cookie             |<br>| JavaScript can read credential?  | Yes                     | No, if HttpOnly            | No, if HttpOnly             |<br>| Automatically sent?              | No                      | Yes, when rules allow      | Yes, when rules allow       |<br>| Server-side session required?    | Usually no              | Usually no for JWT        | Yes                        |<br>| XSS credential exposure          | Higher                  | Reduced                   | Reduced                    |<br>| CSRF consideration               | Less direct             | Important                 | Important                  |<br>| Server can invalidate?           | Not inherently          | Depends on token design   | Yes                        |<br>| Scaling                          | Easier for stateless    | Easier for stateless JWTs | Requires shared storage    |<br>| Typical use                      | SPAs / API applications | Web applications          | Server-backed applications |<br>+----------------------------------+-------------------------+---------------------------+----------------------------+</pre><h3>So Which One Should You Choose?</h3><p>There isn’t a universal answer.</p><p>The right choice depends on the application architecture and its security requirements.</p><h4>Scenario 1: Simple SPA + API</h4><p>You might see:</p><pre>React<br>   ↓<br>REST API<br>   ↓<br>Backend</pre><p>A token-based approach can work well.</p><p>Historically, many applications have stored access tokens in localStorage, but this comes with an increased XSS exposure because JavaScript can read the token.</p><h4>Scenario 2: Production web application</h4><p>For a browser-based application where you want authentication credentials protected from JavaScript access, an HttpOnly, Secure cookie is often a strong choice.</p><p>You still need to handle:</p><pre>XSS<br>CSRF<br>SameSite<br>CORS<br>HTTPS<br>Authorization<br>Token/session expiration</pre><p>Security is never achieved by one setting alone.</p><h4>Scenario 3: Traditional backend application</h4><p>If your application is heavily server-backed and you want the server to maintain authentication state:</p><pre>Browser<br>   ↓<br>Session ID<br>   ↓<br>Server<br>   ↓<br>Session Store</pre><p>Session-based authentication can be a very good fit.</p><h3>What About Refresh Tokens?</h3><p>You may have heard another pattern:</p><pre>Access Token<br>+<br>Refresh Token</pre><p>A common architecture is:</p><pre>Access Token<br>    ↓<br>Short lifetime<br>    ↓<br>Used for API access<br>    ↓<br>Refresh Token<br>    ↓<br>Longer lifetime<br>    ↓<br>Stored in HttpOnly Secure cookie<br>    ↓<br>Used to obtain a new access token</pre><p>For example:</p><pre>Login<br>  ↓<br>Access Token + Refresh Token<br>  ↓<br>Access token used for API calls<br>  ↓<br>Access token expires<br>  ↓<br>Refresh token sent to refresh endpoint<br>  ↓<br>Backend validates refresh token<br>  ↓<br>New access token generated</pre><p>This is another layer of the authentication architecture rather than a completely separate storage method.</p><h3>A Simple Mental Model</h3><p>If you struggle to remember all of this during an interview, remember these three questions:</p><h4>Question 1: Where is the credential stored?</h4><pre>localStorage?<br>Cookie?</pre><h4>Question 2: Can JavaScript access it?</h4><pre>localStorage → Yes<br><br>HttpOnly cookie → No</pre><h4>Question 3: Is authentication state stored on the server?</h4><pre>JWT → Usually noSession ID → Yes</pre><p>That gives you a very simple mental model:</p><pre>Authentication<br>                         │<br>          ┌──────────────┴──────────────┐<br>          │                             │<br>       Token                         Session<br>          │                             │<br>     ┌────┴────┐                        │<br>     │         │                        │<br>localStorage  Cookie              Session ID<br>     │         │                        │<br>     │      HttpOnly                 Cookie<br>     │<br>JavaScript<br>accessible</pre><h3>What I Would Say in an Interview</h3><p>If an interviewer asks:</p><blockquote><strong><em>“Where would you store an authentication token?”</em></strong></blockquote><p>Don’t immediately answer:</p><blockquote><em>“localStorage.”</em></blockquote><p>Instead, explain the trade-off.</p><p>A strong answer would be:</p><blockquote><em>“It depends on the application’s security and architecture requirements. Storing a token in localStorage is simple, but JavaScript can access it, so an XSS vulnerability could expose the token. For browser-based applications, I would generally consider Secure, HttpOnly cookies because JavaScript cannot directly read the authentication credential. However, cookie-based authentication introduces CSRF considerations, so I would configure SameSite appropriately and use additional CSRF protections when required. If the application uses server-side sessions, the browser can store only an opaque Session ID while the actual authentication state remains on the server.”</em></blockquote><p>That answer demonstrates much more understanding than simply saying:</p><blockquote><em>“Use cookies because they’re secure.”</em></blockquote><h3>Final Takeaway</h3><p>There is no magical authentication storage mechanism that makes an application automatically secure.</p><p>Each approach has trade-offs.</p><pre>localStorage<br>    ↓<br>Simple<br>    ↓<br>JavaScript can access credential<br>    ↓<br>Greater XSS exposure<br></pre><pre>HttpOnly Cookie<br>    ↓<br>JavaScript cannot directly access credential<br>    ↓<br>Browser automatically sends cookie<br>    ↓<br>Need to consider CSRF</pre><pre>Session ID<br>    ↓<br>Browser stores only an identifier<br>    ↓<br>Actual session state lives on server<br>    ↓<br>Requires session management and shared storage when scaling</pre><p>The key is to understand <strong>what is being stored, who can access it, how it reaches the server, and what happens if an attacker gets hold of it.</strong></p><p>That’s the real authentication discussion.</p><p>Happy coding! ✌️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=97d073873521" width="1" height="1" alt=""><hr><p><a href="https://javascript.plainenglish.io/where-should-you-store-authentication-credentials-localstorage-vs-httponly-cookies-vs-session-ids-97d073873521">Where Should You Store Authentication Credentials? localStorage vs HttpOnly Cookies vs Session IDs</a> was originally published in <a href="https://javascript.plainenglish.io">JavaScript in Plain English</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AI for Frontend Developers — Day 72]]></title>
            <link>https://medium.com/@rohitkuwar/ai-for-frontend-developers-day-72-fbf3c42fd0b0?source=rss-faedefd908b4------2</link>
            <guid isPermaLink="false">https://medium.com/p/fbf3c42fd0b0</guid>
            <category><![CDATA[llm-temperature]]></category>
            <category><![CDATA[llm-model]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[learning]]></category>
            <category><![CDATA[web-development]]></category>
            <dc:creator><![CDATA[Rohit Kuwar]]></dc:creator>
            <pubDate>Thu, 04 Jun 2026 02:31:00 GMT</pubDate>
            <atom:updated>2026-06-04T02:31:00.775Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-CPUw2o0NCnAc1xz1mDpoA.png" /><figcaption>AI for Frontend Developers — Week 11 Day 2</figcaption></figure><h3>Adding Temperature Controls and Multi-Model Support to My AI Chat Application 🚀</h3><p>Over the past few weeks, my AI Chat application has evolved from a simple chat interface into a more configurable AI platform. After implementing voice preferences and a complete theme system, today’s focus was on giving users more control over how the AI behaves and which model powers their conversations.</p><p>The two major features implemented today were:</p><ol><li>Temperature Settings</li><li>GPT-4o Model Integration</li></ol><p>Both of these features were added through the Settings system that was built during previous weeks.</p><h3>Why Temperature Matters</h3><p>Most users interact with AI without realizing that the same model can behave very differently depending on a parameter called <strong>temperature</strong>.</p><p>Temperature controls how deterministic or creative the model’s responses are.</p><p>Lower values make responses more predictable and consistent, while higher values increase creativity and variation.</p><p>To expose this control to users, I introduced the following options inside the Settings modal:</p><pre>const TEMPERATURE_OPTIONS = [<br>  { value: 0, label: &#39;0.0 — Deterministic&#39; },<br>  { value: 0.3, label: &#39;0.3 — Focused&#39; },<br>  { value: 0.5, label: &#39;0.5 — Balanced&#39; },<br>  { value: 0.7, label: &#39;0.7 — Creative&#39; },<br>  { value: 1, label: &#39;1.0 — Very Creative&#39; },<br>]</pre><h3>Understanding Temperature Levels</h3><pre>+-------------+-------------------------------+<br>| Temperature | Behaviour                     |<br>+-------------+-------------------------------+<br>| 0.0         | Highly predictable            |<br>| 0.3         | Focused and factual           |<br>| 0.5         | Balanced responses            |<br>| 0.7         | More creative                 |<br>| 1.0         | Highly creative and diverse   |<br>+-------------+-------------------------------+</pre><p>For most users, <strong>0.5</strong> provides the best balance between creativity and accuracy.</p><h3>Where Temperature Is Applied</h3><p>One important architectural decision was determining where temperature should actually be used.</p><p>Initially, I considered applying it everywhere, including:</p><ul><li>Chat conversations</li><li>Chat title generation</li><li>Conversation summarization</li></ul><p>However, after analyzing the use cases, I decided against it.</p><h3>Chat Conversations</h3><p>Temperature should affect the main AI conversation because users directly experience those responses.</p><p>Example:</p><pre>User Question<br>      ↓<br>Temperature Applied<br>      ↓<br>AI Response</pre><h3>Chat Title Generation</h3><p>Titles should be short, consistent, and predictable.</p><p>For example:</p><pre>How to Learn React Efficiently?</pre><p>should always become something similar to:</p><pre>React Learning Roadmap</pre><p>and not vary wildly based on creativity settings.</p><h3>Conversation Summaries</h3><p>Summaries should remain factual and deterministic.</p><p>A summary feature exists to preserve context, not generate creative interpretations.</p><p>Applying high temperature here could introduce unnecessary variation or inaccuracies.</p><p>Because of this, temperature is currently applied only to the main chat flow.</p><h3>Adding GPT-4o Support</h3><p>Until now, the application exclusively used:</p><pre>GPT-4o Mini</pre><p>which provides an excellent balance between performance, cost, and response quality.</p><p>Today’s goal was to give users a second option:</p><pre>GPT-4o</pre><h3>Why GPT-4o?</h3><p>Before integrating additional models, I evaluated the complexity involved.</p><p>GPT-4o was selected because:</p><pre>+----------------------+----------------------------+<br>| Reason               | Benefit                    |<br>+----------------------+----------------------------+<br>| Same OpenAI SDK      | Minimal implementation     |<br>| Same Response Format | No architecture changes    |<br>| Same Streaming API   | Existing logic reusable    |<br>| Same Tool Calling    | Existing tools work        |<br>| Same Vision Support  | Existing image flow works  |<br>+----------------------+----------------------------+</pre><p>Since GPT-4o and GPT-4o Mini share the same API structure, the implementation was surprisingly straightforward.</p><p>Previously:</p><pre>model: &quot;gpt-4o-mini&quot;</pre><p>Now:</p><pre>model: settings.model</pre><p>The rest of the backend logic remains unchanged.</p><h3>Model Selection Options</h3><p>Users can now select between:</p><pre>const MODEL_OPTIONS = [<br>  {<br>    value: &quot;gpt-4o-mini&quot;,<br>    label: &quot;⚡ GPT-4o Mini&quot;,<br>  },<br>  {<br>    value: &quot;gpt-4o&quot;,<br>    label: &quot;🧠 GPT-4o&quot;,<br>  },<br>];</pre><p>This selection is stored in the user-specific Settings API and automatically loaded when the user returns to the application.</p><h3>Why We Didn’t Add Gemini or Claude Yet</h3><p>At first glance, adding Gemini or Claude may seem similar to adding GPT-4o.</p><p>However, the reality is much more complicated.</p><p>Different AI providers expose different APIs.</p><p>For example:</p><h4>OpenAI</h4><pre>response.choices[0].message.content</pre><h4>Gemini</h4><pre>response.candidates[0]<br>        .content.parts[0].text</pre><h4>Claude</h4><pre>response.content[0].text</pre><p>Although all three models produce text, the response structures differ significantly.</p><h3>Additional Complexity Introduced by Multiple Providers</h3><p>Integrating models from multiple companies would impact:</p><pre>+----------------------+----------------------------+<br>| System               | Additional Complexity      |<br>+----------------------+----------------------------+<br>| Streaming            | Different APIs             |<br>| Tool Calling         | Different formats          |<br>| Vision               | Different payloads         |<br>| Error Handling       | Different responses        |<br>| Rate Limits          | Different restrictions     |<br>| SDK Management       | Multiple SDKs             |<br>+----------------------+----------------------------+</pre><p>If implemented naively, the chat controller would quickly become difficult to maintain.</p><h3>Future Architecture Plan</h3><p>Instead of rushing into multi-provider support, I want to build a scalable foundation first.</p><p>The long-term architecture will likely look like this:</p><pre>providers/<br>├── openai.js<br>├── gemini.js<br>├── claude.js<br>services/<br>├── llmService.js</pre><p>Each provider will expose a common interface while internally handling provider-specific differences.</p><p>This approach keeps the main chat controller clean and maintainable.</p><h3>Current Model Strategy</h3><p>For now, the application follows this approach:</p><pre>+----------------------+----------------+<br>| Feature              | Model          |<br>+----------------------+----------------+<br>| Main Chat            | User Selected  |<br>| Generate Chat Title  | GPT-4o Mini    |<br>| Chat Summarization   | GPT-4o Mini    |<br>+----------------------+----------------+</pre><p>This ensures that expensive models are only used where users directly benefit from them.</p><h3>Key Takeaways</h3><p>Today’s work was less about adding flashy features and more about improving configurability and future scalability.</p><p>By introducing temperature controls, users can now customize how the AI behaves.</p><p>By integrating GPT-4o support, the application takes its first step toward becoming a true multi-model AI platform.</p><p>Most importantly, instead of immediately integrating models from multiple providers and creating architectural chaos, I chose to first build a foundation that can support such expansion cleanly in the future.</p><h3>🔗 Live Demo &amp; Code</h3><p>👉 <strong>Live App:</strong><br><a href="https://ai-chat-app-learning.netlify.app/">https://ai-chat-app-learning.netlify.app</a></p><p>👉 <strong>GitHub Repo:</strong><br><a href="https://github.com/RohitKuwar/ai-chat-app/tree/feature/settings-temperature-models">https://github.com/RohitKuwar/ai-chat-app/tree/feature/settings-temperature-models</a></p><h3>🚀 What’s Next</h3><p>Today I implemented user-controlled temperature settings and added GPT-4o as a second model option while keeping the architecture simple and maintainable.</p><p>Next, I’ll continue expanding the AI platform by strengthening the model management architecture, improving file handling with cloud storage, and preparing the codebase for future support of providers like Gemini and Claude through a scalable adapter-based design.</p><p>I’ll be posting here daily as I learn. Let’s grow together and stay ahead in the AI era 🚀</p><p>Happy learning ✌️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fbf3c42fd0b0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AI for Frontend Developers — Day 71]]></title>
            <link>https://medium.com/@rohitkuwar/ai-for-frontend-developers-day-71-0a32edad956f?source=rss-faedefd908b4------2</link>
            <guid isPermaLink="false">https://medium.com/p/0a32edad956f</guid>
            <category><![CDATA[user-interface]]></category>
            <category><![CDATA[themes]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[learning]]></category>
            <category><![CDATA[settings]]></category>
            <dc:creator><![CDATA[Rohit Kuwar]]></dc:creator>
            <pubDate>Wed, 03 Jun 2026 09:38:29 GMT</pubDate>
            <atom:updated>2026-06-03T09:38:29.112Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3sStOM6WMc5IRe6K5PGnTg.png" /><figcaption>AI for Frontend Developers — Week 11 Day 1</figcaption></figure><h3>Implementing a Complete Theme System for My AI Chat Application 🎨</h3><p>One of the most noticeable aspects of any application is its visual appearance. Users spend hours interacting with interfaces, and a well-designed theme can significantly improve usability, readability, and overall experience.</p><p>Today I worked on implementing a scalable theme system for my AI Chat application. Instead of hardcoding colors throughout the codebase, I centralized all color definitions using CSS custom properties (variables), making the application easier to maintain and extend.</p><h3>Why Refactor the Existing Styling?</h3><p>Initially, most UI colors were directly hardcoded inside component styles and CSS classes.</p><p>Example:</p><pre>background: #020617;<br>color: #f1f5f9;<br>border: rgba(255, 255, 255, 0.1);</pre><p>While this works for a single theme, it quickly becomes difficult to manage when introducing multiple themes.</p><p>Problems with this approach:</p><ul><li>Color values get repeated throughout the project.</li><li>Updating a color requires searching multiple files.</li><li>Supporting additional themes becomes cumbersome.</li><li>Maintaining visual consistency becomes difficult.</li></ul><p>To solve this, I moved all theme-related colors into centralized CSS variables.</p><h3>Creating a Theme Foundation with CSS Variables</h3><p>I introduced a global theme configuration using CSS custom properties.</p><h4>Default Theme (Dark Navy)</h4><pre>:root {<br>  /* ── Backgrounds ── */<br>  --bg-gradient:        linear-gradient(135deg, #020617, #0f172a);<br>  --bg-deep:            #020617;<br>  --bg-secondary:       #0f172a;<br>  --bg-sidebar:         #171717;<br>  --bg-chat-box:        #111827;<br>  --bg-surface:         #1e293b;<br>  --bg-message-ai:      #1e293b;<br>  --bg-message-ai-alt:  #1f2937;<br>  --bg-message-user:    linear-gradient(135deg, #2563eb, #1d4ed8);<br>  --bg-input:           #020617;<br>  --bg-dropdown:        #1a2035;<br>  --bg-modal:           #1c202f;<br>  --bg-modal-alpha:     rgba(15, 18, 32, 0.95);<br>  --bg-modal-sticky:    rgba(15, 18, 32, 0.98);<br><br>  /* ── Text ── */<br>  --text-primary:       #f1f5f9;<br>  --text-primary-soft:  #e2e8f0;<br>  --text-secondary:     #cbd5e1;<br>  --text-muted:         #9b9b9b;<br>  --text-dim:           #64748b;<br>  --text-faint:         #5e5e5e;<br><br>  /* ── Accent (Indigo) ── */<br>  --accent:             #818cf8;<br>  --accent-hover:       #4f46e5;<br>  --accent-light:       #a5b4fc;<br>  --accent-blue:        #3b82f6;<br>  --accent-soft:        rgba(99, 102, 241, 0.12);<br>  --accent-soft-2:      rgba(99, 102, 241, 0.08);<br>  --accent-soft-3:      rgba(99, 102, 241, 0.15);<br>  --accent-border:      rgba(99, 102, 241, 0.2);<br>  --accent-border-mid:  rgba(99, 102, 241, 0.4);<br><br>  /* ── Borders ── */<br>  --border:             rgba(255, 255, 255, 0.1);<br>  --border-soft:        rgba(255, 255, 255, 0.08);<br>  --border-subtle:      rgba(255, 255, 255, 0.06);<br>  --border-faint:       rgba(255, 255, 255, 0.04);<br>  --border-input:       #334155;<br>  --border-code:        #1f2937;<br><br>  /* ── Semantic ── */<br>  --danger:             #f87171;<br>  --danger-light:       #fca5a5;<br>  --success:            #22c55e;<br>  --success-light:      #4ade80;<br><br>  /* ── Scrollbars ── */<br>  --scrollbar:          #2e2e2e;<br>  --scrollbar-mid:      #374151;<br>  --scrollbar-dark:     #1e2240;<br>}</pre><p>Now UI components no longer depend on hardcoded colors.</p><p>Instead of:</p><pre>background: #020617;</pre><p>I can simply use:</p><pre>background: var(--bg-deep);</pre><p>This creates a single source of truth for all design tokens.</p><h3>Implementing Light Mode</h3><p>Once the foundation was in place, adding new themes became much easier.</p><p>For Light Mode, I only needed to override the variables:</p><pre>[data-theme=&quot;light&quot;] {<br>  /* ── Backgrounds ── */<br>  --bg-gradient:        linear-gradient(135deg, #eef2f7, #e8edf5);<br>  --bg-deep:            #eef2f7;<br>  --bg-secondary:       #e2e8f2;<br>  --bg-sidebar:         #dde3ee;<br>  --bg-chat-box:        #eef2f7;<br>  --bg-surface:         #d6dce8;<br>  --bg-message-ai:      #e8edf5;<br>  --bg-message-ai-alt:  #dde3ee;<br>  --bg-message-user:    linear-gradient(135deg, #2563eb, #1d4ed8);<br>  --bg-input:           #e2e8f2;<br>  --bg-dropdown:        #dde3ee;<br>  --bg-modal:           #eef2f7;<br>  --bg-modal-alpha:     rgba(238, 242, 247, 0.97);<br>  --bg-modal-sticky:    rgba(238, 242, 247, 0.98);<br><br>  /* ── Text ── */<br>  --text-primary:       #1a2035;<br>  --text-primary-soft:  #2d3a52;<br>  --text-secondary:     #3d4f6e;<br>  --text-muted:         #5a6e8c;<br>  --text-dim:           #7a8faa;<br>  --text-faint:         #a0b0c4;<br><br>  /* ── Accent (Indigo) ── */<br>  --accent:             #4f46e5;<br>  --accent-hover:       #3730a3;<br>  --accent-light:       #6366f1;<br>  --accent-blue:        #2563eb;<br>  --accent-soft:        rgba(79, 70, 229, 0.1);<br>  --accent-soft-2:      rgba(79, 70, 229, 0.07);<br>  --accent-soft-3:      rgba(79, 70, 229, 0.13);<br>  --accent-border:      rgba(79, 70, 229, 0.3);<br>  --accent-border-mid:  rgba(79, 70, 229, 0.5);<br><br>  /* ── Borders — visible on light bg ── */<br>  --border:             rgba(100, 116, 139, 0.25);<br>  --border-soft:        rgba(100, 116, 139, 0.18);<br>  --border-subtle:      rgba(100, 116, 139, 0.15);<br>  --border-faint:       rgba(100, 116, 139, 0.1);<br>  --border-input:       rgba(100, 116, 139, 0.35);<br>  --border-code:        rgba(100, 116, 139, 0.2);<br><br>  /* ── Semantic ── */<br>  --danger:             #dc2626;<br>  --danger-light:       #ef4444;<br>  --success:            #16a34a;<br>  --success-light:      #22c55e;<br><br>  /* ── Scrollbars ── */<br>  --scrollbar:          #b0bdd0;<br>  --scrollbar-mid:      #94a3b8;<br>  --scrollbar-dark:     #cbd5e1;<br>}</pre><p>The entire application instantly adapts to the selected theme because every component references variables instead of hardcoded values.</p><h3>Themes Added</h3><p>After implementing the theme architecture, I expanded the application with multiple theme options.</p><h4>Dark Themes</h4><pre>🌑 Custom Dark Navy<br>⚫ Pure Black<br>🩶 Slate Gray<br>🟤 Dark Mocha<br>🔥 Obsidian Warm<br>🌿 Midnight Green</pre><h4>Light Themes</h4><pre>☀️ Light Mode<br>🤍 Soft White<br>📄 Paper<br>🌸 Cherry Blossom<br>🩵 Sky<br>🌿 Fresh Mint<br>🍊 Peach</pre><h4>Special Themes</h4><pre>🟣 Cyberpunk<br>⚡ Hacker</pre><h3>Benefits of This Approach</h3><p>Moving colors into theme variables provided several advantages:</p><pre>+----------------------+--------------------------------------------+<br>| Benefit              | Description                                |<br>+----------------------+--------------------------------------------+<br>| Scalability          | Easy to add new themes                     |<br>| Maintainability      | Colors managed from one location           |<br>| Consistency          | Same design tokens across the app          |<br>| Flexibility          | Instant theme switching                    |<br>| Cleaner CSS          | Less duplication and hardcoded values      |<br>+----------------------+--------------------------------------------+</pre><p>Most importantly, future themes can now be added with minimal effort by simply defining a new theme block and overriding the required variables.</p><h3>Settings Integration</h3><p>Since I already built a dedicated Settings modal during Week 10, theme selection naturally fits into that architecture.</p><p>The selected theme is stored in user settings and automatically applied when the user returns to the application, providing a personalized and consistent experience across sessions.</p><h3>Conclusion</h3><p>Today’s work was less about adding a visible feature and more about improving the application’s architecture. Refactoring hardcoded colors into centralized CSS variables created a scalable foundation that now supports multiple themes with minimal complexity.</p><p>With this system in place, future visual customizations can be implemented much faster while keeping the codebase clean and maintainable.</p><h3>🔗 Live Demo &amp; Code</h3><p>👉 <strong>Live App:</strong><br><a href="https://ai-chat-app-learning.netlify.app/">https://ai-chat-app-learning.netlify.app</a></p><p>👉 <strong>GitHub Repo:</strong><br><a href="https://github.com/RohitKuwar/ai-chat-app/tree/feature/theme">https://github.com/RohitKuwar/ai-chat-app/tree/feature/theme</a></p><h3>🚀 What’s Next</h3><p>Today I implemented a scalable theme architecture by moving hardcoded colors into centralized CSS variables and introducing multiple dark, light, and special themes.</p><p>Next week, I’ll continue expanding the settings system by integrating more LLM model options, temperature controls to make the AI experience even more personalized.</p><p>I’ll be posting here daily as I learn. Let’s grow together and stay ahead in the AI era 🚀</p><p>Happy learning ✌️</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0a32edad956f" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>