<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: AddWeb Solution Pvt Ltd</title>
    <description>The latest articles on DEV Community by AddWeb Solution Pvt Ltd (@addwebsolutionpvtltd).</description>
    <link>https://dev.to/addwebsolutionpvtltd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F11063%2F0b7a4ce4-43ab-4718-abd0-1d314bc88f99.png</url>
      <title>DEV Community: AddWeb Solution Pvt Ltd</title>
      <link>https://dev.to/addwebsolutionpvtltd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/addwebsolutionpvtltd"/>
    <language>en</language>
    <item>
      <title>API Contract-Driven Development (Build Reliable Systems Without Guesswork)</title>
      <dc:creator>Ankit Parmar</dc:creator>
      <pubDate>Wed, 27 May 2026 10:23:48 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/api-contract-driven-development-build-reliable-systems-without-guesswork-lef</link>
      <guid>https://dev.to/addwebsolutionpvtltd/api-contract-driven-development-build-reliable-systems-without-guesswork-lef</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“A big part of the essence of building a program is in fact the debugging of the specification.” - Fred Brooks&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Modern applications often fail not because of bad code-but because of misaligned expectations between frontend and backend. APIs change, fields break, and teams waste time debugging integration issues.&lt;/p&gt;

&lt;p&gt;API Contract-Driven Development fixes this at the root.&lt;/p&gt;

&lt;p&gt;Instead of building first and integrating later, teams define the API contract upfront, align on it, and then implement against that contract independently.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how Contract-Driven Development works, why it matters, and how it enables scalable, predictable systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why API Contracts Matter
&lt;/h2&gt;

&lt;p&gt;Without a defined contract, teams face:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Breaking API changes&lt;/li&gt;
&lt;li&gt;Miscommunication between frontend and backend&lt;/li&gt;
&lt;li&gt;Delayed integrations&lt;/li&gt;
&lt;li&gt;Fragile deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contracts solve this by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defining clear expectations upfront&lt;/li&gt;
&lt;li&gt;Acting as a single source of truth&lt;/li&gt;
&lt;li&gt;Enabling parallel development&lt;/li&gt;
&lt;li&gt;Preventing unexpected breaking changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Is Contract-Driven Development?
&lt;/h2&gt;

&lt;p&gt;Contract-Driven Development (CDD) is an approach where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API structure is defined before implementation&lt;/li&gt;
&lt;li&gt;Both frontend and backend agree on the contract&lt;/li&gt;
&lt;li&gt;Development happens independently but consistently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A contract includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Endpoints&lt;/li&gt;
&lt;li&gt;Request/response structure&lt;/li&gt;
&lt;li&gt;Data types&lt;/li&gt;
&lt;li&gt;Error formats&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Core Philosophy
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“The interface is the system.” - Alan Kay&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The contract defines how systems interact-everything else is implementation detail.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;API contracts eliminate ambiguity between teams&lt;/li&gt;
&lt;li&gt;Enable parallel frontend and backend development&lt;/li&gt;
&lt;li&gt;Reduce integration bugs and rework&lt;/li&gt;
&lt;li&gt;Improve system reliability and predictability&lt;/li&gt;
&lt;li&gt;Work best with tools like OpenAPI and schema validation&lt;/li&gt;
&lt;li&gt;Essential for scaling teams and microservices&lt;/li&gt;
&lt;li&gt;Promote clear ownership and accountability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Index
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;What Is an API Contract?&lt;/li&gt;
&lt;li&gt;Contract-First vs Code-First&lt;/li&gt;
&lt;li&gt;How Contract-Driven Development Works&lt;/li&gt;
&lt;li&gt;Architecture Overview&lt;/li&gt;
&lt;li&gt;Defining API Contracts (Conceptual)&lt;/li&gt;
&lt;li&gt;Development Workflow&lt;/li&gt;
&lt;li&gt;Validation and Testing&lt;/li&gt;
&lt;li&gt;Versioning and Evolution&lt;/li&gt;
&lt;li&gt;Tooling and Ecosystem&lt;/li&gt;
&lt;li&gt;Why This Approach Makes Sense&lt;/li&gt;
&lt;li&gt;Watch Out For&lt;/li&gt;
&lt;li&gt;Next Steps You Can Take&lt;/li&gt;
&lt;li&gt;Interesting Facts&lt;/li&gt;
&lt;li&gt;FAQ&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;As systems grow, frontend and backend teams often move independently. Without a shared agreement, APIs become a moving target-leading to broken integrations and wasted time.&lt;/p&gt;

&lt;p&gt;Contract-Driven Development introduces structure. By defining the API upfront, teams align early and build with confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. What Is an API Contract?
&lt;/h2&gt;

&lt;p&gt;An API contract is a formal agreement that defines how two systems communicate.&lt;br&gt;
It specifies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Endpoint paths&lt;/li&gt;
&lt;li&gt;HTTP methods&lt;/li&gt;
&lt;li&gt;Request payloads&lt;/li&gt;
&lt;li&gt;Response formats&lt;/li&gt;
&lt;li&gt;Status codes&lt;/li&gt;
&lt;li&gt;Error structures&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it as a blueprint for communication.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The most damaging phrase in the language is: ‘It’s always been done this way.’” - Grace Hopper&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Contract-First vs Code-First
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Contract-First (Recommended)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define API using schema (OpenAPI, JSON Schema)&lt;/li&gt;
&lt;li&gt;Generate mocks and clients&lt;/li&gt;
&lt;li&gt;Implement backend afterward&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Code-First&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build backend first&lt;/li&gt;
&lt;li&gt;Document later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation often becomes outdated&lt;/li&gt;
&lt;li&gt;Frontend depends on unstable APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. How Contract-Driven Development Works
&lt;/h2&gt;

&lt;p&gt;High-level flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define API contract&lt;/li&gt;
&lt;li&gt;Share with teams&lt;/li&gt;
&lt;li&gt;Generate mocks&lt;/li&gt;
&lt;li&gt;Frontend builds against mock API&lt;/li&gt;
&lt;li&gt;Backend implements contract&lt;/li&gt;
&lt;li&gt;Validate both sides&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No surprises during integration&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Frontend (React / Mobile)&lt;br&gt;
 ↓&lt;br&gt;
 API Contract (OpenAPI)&lt;br&gt;
 ↓&lt;br&gt;
 Backend Implementation&lt;br&gt;
 ↓&lt;br&gt;
 Validation Layer&lt;br&gt;
Key idea:&lt;br&gt;
 The contract sits in the middle as the source of truth.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Controlling complexity is the essence of computer programming.” - Brian Kernighan&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  6. Defining API Contracts (Conceptual)
&lt;/h2&gt;

&lt;p&gt;A contract is typically written using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAPI (Swagger)&lt;/li&gt;
&lt;li&gt;JSON Schema&lt;/li&gt;
&lt;li&gt;GraphQL schema&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example (simplified):&lt;/p&gt;

&lt;p&gt;paths:&lt;br&gt;
 /users:&lt;br&gt;
   get:&lt;br&gt;
     responses:&lt;br&gt;
       200:&lt;br&gt;
         description: List users&lt;br&gt;
         content:&lt;br&gt;
           application/json:&lt;br&gt;
             schema:&lt;br&gt;
               type: array&lt;br&gt;
               items:&lt;br&gt;
                 type: object&lt;br&gt;
                 properties:&lt;br&gt;
                   id:&lt;br&gt;
                     type: integer&lt;br&gt;
                   name:&lt;br&gt;
                     type: string&lt;/p&gt;

&lt;p&gt;This defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structure&lt;/li&gt;
&lt;li&gt;Types&lt;/li&gt;
&lt;li&gt;Expected response&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Development Workflow
&lt;/h2&gt;

&lt;p&gt;Step 1: Define contract&lt;br&gt;
Step 2: Review with team&lt;br&gt;
Step 3: Generate mock server&lt;br&gt;
Step 4: Frontend development&lt;br&gt;
Step 5: Backend implementation&lt;br&gt;
Step 6: Contract validation&lt;/p&gt;

&lt;p&gt;Parallel development becomes possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Validation and Testing
&lt;/h2&gt;

&lt;p&gt;Contracts are enforced using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Schema validation&lt;/li&gt;
&lt;li&gt;Contract testing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two key approaches:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consumer-Driven Contracts&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend defines expectations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Provider Validation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Backend ensures it matches contract&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pact&lt;/li&gt;
&lt;li&gt;Postman&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  9. Versioning and Evolution
&lt;/h2&gt;

&lt;p&gt;APIs evolve. Contracts help manage change safely.&lt;br&gt;
Best practices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use versioning (/v1, /v2)&lt;/li&gt;
&lt;li&gt;Avoid breaking changes&lt;/li&gt;
&lt;li&gt;Deprecate gradually&lt;/li&gt;
&lt;li&gt;Maintain backward compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“Simplicity is prerequisite for reliability.” - Edsger W. Dijkstra&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  10. Tooling and Ecosystem
&lt;/h2&gt;

&lt;p&gt;Popular tools include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAPI Specification&lt;/li&gt;
&lt;li&gt;Swagger&lt;/li&gt;
&lt;li&gt;Stoplight&lt;/li&gt;
&lt;li&gt;Insomnia&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools help:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design APIs&lt;/li&gt;
&lt;li&gt;Generate docs&lt;/li&gt;
&lt;li&gt;Create mocks&lt;/li&gt;
&lt;li&gt;Validate contracts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  11. Why This Approach Makes Sense
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Eliminates ambiguity&lt;/li&gt;
&lt;li&gt;Reduces integration issues&lt;/li&gt;
&lt;li&gt;Enables parallel development&lt;/li&gt;
&lt;li&gt;Improves developer productivity&lt;/li&gt;
&lt;li&gt;Scales across teams and services&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  12. Watch Out For
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Over-engineering simple APIs&lt;/li&gt;
&lt;li&gt;Poorly defined contracts&lt;/li&gt;
&lt;li&gt;Lack of versioning strategy&lt;/li&gt;
&lt;li&gt;Ignoring backward compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  13. Next Steps You Can Take
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduce OpenAPI in your project&lt;/li&gt;
&lt;li&gt;Add contract validation in CI/CD&lt;/li&gt;
&lt;li&gt;Generate mock APIs for frontend&lt;/li&gt;
&lt;li&gt;Adopt contract testing tools&lt;/li&gt;
&lt;li&gt;Document APIs properly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  14. Interesting Facts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Contract-first development is widely used in microservices architectures. &lt;a href="https://microservices.io/patterns/apigateway.html" rel="noopener noreferrer"&gt;https://microservices.io/patterns/apigateway.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;API contracts act as a single source of truth across teams. &lt;a href="https://stoplight.io/api-design-guide" rel="noopener noreferrer"&gt;https://stoplight.io/api-design-guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Contract testing significantly reduces integration failures in production.&lt;a href="https://martinfowler.com/articles/consumerDrivenContracts.html" rel="noopener noreferrer"&gt;https://martinfowler.com/articles/consumerDrivenContracts.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  15. FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Is contract-driven development only for large systems?&lt;/strong&gt;&lt;br&gt;
No, it’s useful even for small teams to avoid confusion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Is this the same as API documentation?&lt;/strong&gt;&lt;br&gt;
No. Documentation is derived from the contract, not the source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Can I use this with GraphQL?&lt;/strong&gt;&lt;br&gt;
Yes. GraphQL schema acts as a contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Does this slow down development?&lt;/strong&gt;&lt;br&gt;
Initially slightly-but saves massive time later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Is it required for microservices?&lt;/strong&gt;&lt;br&gt;
Highly recommended.&lt;/p&gt;

&lt;h2&gt;
  
  
  16. Conclusion
&lt;/h2&gt;

&lt;p&gt;API Contract-Driven Development brings clarity to one of the most fragile parts of modern systems-communication between services.&lt;/p&gt;

&lt;p&gt;By defining expectations upfront, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Predictable integrations&lt;/li&gt;
&lt;li&gt;Faster development&lt;/li&gt;
&lt;li&gt;Fewer bugs&lt;/li&gt;
&lt;li&gt;Better scalability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your team struggles with broken APIs, miscommunication, or slow integration cycles, adopting a contract-first approach is a practical, high-impact improvement.&lt;/p&gt;

&lt;p&gt;About the Author:&lt;em&gt;Ankit is a full-stack developer at &lt;a href="https://www.addwebsolution.com/" rel="noopener noreferrer"&gt;AddWebSolution&lt;/a&gt; and AI enthusiast who crafts intelligent web solutions with PHP, Laravel, and modern frontend tools.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>backenddevelopment</category>
      <category>contractdrivendevelopment</category>
      <category>openai</category>
    </item>
    <item>
      <title>Designing Permission Systems Beyond RBAC (ABAC)</title>
      <dc:creator>Mayank Goyal</dc:creator>
      <pubDate>Tue, 26 May 2026 08:04:47 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/designing-permission-systems-beyond-rbac-abac-1f44</link>
      <guid>https://dev.to/addwebsolutionpvtltd/designing-permission-systems-beyond-rbac-abac-1f44</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“Simple permissions work for small systems. Context-aware permissions power enterprise systems.” &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;RBAC breaks down at scale&lt;/li&gt;
&lt;li&gt;ABAC enables dynamic, context-aware authorization&lt;/li&gt;
&lt;li&gt;Permissions should depend on attributes, not only roles&lt;/li&gt;
&lt;li&gt;Centralized policy engines improve maintainability&lt;/li&gt;
&lt;li&gt;Fine-grained authorization is essential for modern SaaS&lt;/li&gt;
&lt;li&gt;Performance and caching are critical in permission systems&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Most applications start with simple role-based permissions: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admin - Full access&lt;/li&gt;
&lt;li&gt;Editor - Edit content&lt;/li&gt;
&lt;li&gt;Viewer - Read-only access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This works initially.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;But as systems grow, requirements become more complex:&lt;/li&gt;
&lt;li&gt;Managers can edit only their department’s data&lt;/li&gt;
&lt;li&gt;Support agents can access tickets only during work hours&lt;/li&gt;
&lt;li&gt;Users can download reports only from trusted devices&lt;/li&gt;
&lt;li&gt;Contractors lose access after project expiration
Traditional RBAC (Role-Based Access Control) struggles to handle these dynamic conditions cleanly. This is where ABAC (Attribute-Based Access Control) becomes essential.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;blockquote&gt;
&lt;p&gt;“What role does this user have?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;ABAC asks:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Should this specific user perform this action on this resource under these conditions?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That difference changes everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Index
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Why Traditional RBAC Fails at Scale&lt;/li&gt;
&lt;li&gt;Core Concepts of ABAC&lt;/li&gt;
&lt;li&gt;RBAC vs ABAC (Deep Comparison)&lt;/li&gt;
&lt;li&gt;Core Principles of Modern Permission Architecture&lt;/li&gt;
&lt;li&gt;Designing a Scalable Authorization System&lt;/li&gt;
&lt;li&gt;Policy Engine Architecture&lt;/li&gt;
&lt;li&gt;Attribute Modeling Strategy&lt;/li&gt;
&lt;li&gt;Multi-Tenant Permission Design&lt;/li&gt;
&lt;li&gt;Real-Time Authorization Challenges&lt;/li&gt;
&lt;li&gt;Caching &amp;amp; Performance Optimization&lt;/li&gt;
&lt;li&gt;Frontend Authorization Patterns&lt;/li&gt;
&lt;li&gt;Backend Enforcement Strategy&lt;/li&gt;
&lt;li&gt;Audit Logging &amp;amp; Compliance&lt;/li&gt;
&lt;li&gt;Testing Authorization Systems&lt;/li&gt;
&lt;li&gt;Real-World Example (Enterprise SaaS)&lt;/li&gt;
&lt;li&gt;Interesting Facts&lt;/li&gt;
&lt;li&gt;Stats&lt;/li&gt;
&lt;li&gt;FAQ’s&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why Traditional RBAC Fails at Scale
&lt;/h2&gt;

&lt;p&gt;RBAC becomes difficult in enterprise systems because permissions explode over time.&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Admin
RegionalAdmin
RegionalManager
RegionalManagerReadOnly
FinanceManager
FinanceManagerEU
FinanceManagerUS
SupportLevel1
SupportLevel2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Soon you face: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Role explosion &amp;amp; Duplicate permissions&lt;/li&gt;
&lt;li&gt;Hardcoded business logic&lt;/li&gt;
&lt;li&gt;Complex exception handling&lt;/li&gt;
&lt;li&gt;Difficult audits&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“Every special-case role is usually a hidden architecture problem.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Core Concepts of ABAC
&lt;/h2&gt;

&lt;p&gt;ABAC evaluates permissions using attributes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. User Attributes&lt;/strong&gt;&lt;br&gt;
Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;department = finance
region = EU
employmentType = contractor
clearanceLevel = 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Resource Attributes&lt;/strong&gt;&lt;br&gt;
Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.ownerId
document.region
document.classification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Action Attributes&lt;/strong&gt;&lt;br&gt;
Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read
write
delete
approve
export
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Environment Attributes&lt;/strong&gt;&lt;br&gt;
Examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;currentTime
IP address
device type
geo location
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  RBAC vs ABAC (Deep Comparison)
&lt;/h2&gt;

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

&lt;h2&gt;
  
  
  Core Principles of Modern Permission Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Centralized Authorization&lt;/strong&gt;&lt;br&gt;
Never scatter permission checks everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad&lt;/strong&gt;&lt;br&gt;
if (user.role === 'admin') {}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better&lt;/strong&gt;&lt;br&gt;
authorizationService.can(user, 'delete', project)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Policy-Based Design&lt;/strong&gt;&lt;br&gt;
Permissions should be defined as policies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
Managers can edit invoices in their own department.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOT:&lt;/strong&gt;&lt;br&gt;
if role === manager &amp;amp;&amp;amp; department === ...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Least Privilege Principle&lt;/strong&gt;&lt;br&gt;
Users should receive the minimum access necessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This minimizes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security risks&lt;/li&gt;
&lt;li&gt;Data leaks&lt;/li&gt;
&lt;li&gt;Insider threats&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Deny by Default&lt;/strong&gt;&lt;br&gt;
If no rule explicitly allows access:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ACCESS = DENIED&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Always.&lt;/p&gt;
&lt;h2&gt;
  
  
  Designing a Scalable Authorization System
&lt;/h2&gt;

&lt;p&gt;Recommended High-Level Architecture&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Client
   ↓
  API Gateway
   ↓
   Authorization Layer
   ↓
   Policy Engine
   ↓
   Attribute Store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Folder Structure Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
│
├── auth/
│   ├── policies/
│   ├── guards/
│   ├── attributes/
│   ├── services/
│   ├── engines/
│   └── audit/
│
├── features/
│   ├── billing/
│   ├── analytics/
│   └── users/
│
├── shared/
└── core/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;“Authorization logic is infrastructure, not UI logic.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Policy Engine Architecture
&lt;/h2&gt;

&lt;p&gt;A policy engine evaluates authorization decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Flow&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User requests action
↓
Load user attributes
↓
Load resource attributes
↓
Evaluate policies
↓
Return allow/deny
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example Policy&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const invoicePolicy = {
 action: 'edit',
 resource: 'invoice',
 evaluate: ({ user, resource }) =&amp;gt; {
   return (
     user.department === resource.department &amp;amp;&amp;amp;
     user.clearanceLevel &amp;gt;= 2
   );
 }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Attribute Modeling Strategy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Poor attribute design creates long-term problems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Good Attribute Categories&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Avoid&lt;/strong&gt;&lt;br&gt;
Storing derived permissions directly&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt;&lt;br&gt;
canEditInvoices = true&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better:&lt;/strong&gt;&lt;br&gt;
department = finance&lt;br&gt;
role = manager&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-Tenant Permission Design&lt;/strong&gt;&lt;br&gt;
Enterprise SaaS applications require tenant isolation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;br&gt;
Tenant A users must NEVER access Tenant B data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every authorization check should include:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;resource.tenantId === user.tenantId&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Missing this is one of the most dangerous SaaS security bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-Time Authorization Challenges&lt;/strong&gt;&lt;br&gt;
Permissions can change instantly.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;User suspension&lt;/li&gt;
&lt;li&gt;Role updates&lt;/li&gt;
&lt;li&gt;Subscription expiration&lt;/li&gt;
&lt;li&gt;Emergency revocation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenges:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stale JWT permissions&lt;/li&gt;
&lt;li&gt;Cached authorization data&lt;/li&gt;
&lt;li&gt;Distributed systems consistency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Recommended:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Short-lived tokens&lt;/li&gt;
&lt;li&gt;Server-side validation&lt;/li&gt;
&lt;li&gt;Permission versioning&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Caching &amp;amp; Performance Optimization
&lt;/h2&gt;

&lt;p&gt;Authorization can become expensive at scale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Large systems may process:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Millions of permission checks per minute&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimization Strategies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Policy Caching&lt;/strong&gt;&lt;br&gt;
Cache compiled policies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Attribute Caching&lt;/strong&gt;&lt;br&gt;
Use Redis for frequently accessed attributes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Decision Memoization&lt;/strong&gt;&lt;br&gt;
Cache repeated authorization decisions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Batch Authorization&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Instead of:&lt;/strong&gt;&lt;br&gt;
1000 separate permission checks&lt;br&gt;
&lt;strong&gt;Use:&lt;/strong&gt;&lt;br&gt;
1 bulk evaluation&lt;/p&gt;
&lt;h2&gt;
  
  
  Frontend Authorization Patterns
&lt;/h2&gt;

&lt;p&gt;Frontend authorization is for UX only.&lt;br&gt;
Backend authorization is mandatory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good Frontend Pattern&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Can action="edit" resource="invoice"&amp;gt;
 &amp;lt;EditButton /&amp;gt;
&amp;lt;/Can&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Avoid&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Relying solely on hidden buttons&lt;br&gt;
Attackers can still call APIs directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend Enforcement Strategy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Backend APIs must ALWAYS enforce authorization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app.post('/invoice/:id', async (req, res) =&amp;gt; {
 const allowed = await auth.can(
   req.user,
   'edit',
   invoice
 );

 if (!allowed) {
   return res.status(403).json({
     error: 'Forbidden'
   });
 }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;“UI permissions improve experience. Backend permissions protect systems.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Audit Logging &amp;amp; Compliance
&lt;/h2&gt;

&lt;p&gt;Modern enterprises require full auditability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Track:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who accessed what&lt;/li&gt;
&lt;li&gt;When access occurred&lt;/li&gt;
&lt;li&gt;Why permission was granted&lt;/li&gt;
&lt;li&gt;Policy evaluation result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User 482 edited invoice 882&lt;/li&gt;
&lt;li&gt;Policy: FinanceManagerPolicy&lt;/li&gt;
&lt;li&gt;Timestamp: 2026-05-22T10:14Z&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Critical for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SOC2&lt;/li&gt;
&lt;li&gt;HIPAA&lt;/li&gt;
&lt;li&gt;GDPR&lt;/li&gt;
&lt;li&gt;ISO compliance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Testing Authorization Systems&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Authorization bugs are security bugs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended Testing Levels&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('contractor cannot access finance data', () =&amp;gt; {
 const result = canAccess(contractor, financeReport);
 expect(result).toBe(false);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Real-World Example (Enterprise SaaS)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A project management platform requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admins - Full access&lt;/li&gt;
&lt;li&gt;Managers - Manage own teams&lt;/li&gt;
&lt;li&gt;Contractors - Limited projects only&lt;/li&gt;
&lt;li&gt;Clients - Read-only assigned projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Authorization Service&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const canAccessProject = ({ user,  project,  action }) =&amp;gt; {
 if (user.role === 'admin') {
   return true;
 }
 if (user.teamId === project.teamId &amp;amp;&amp;amp; action !== 'delete' ) {
   return true;
 }
 return false;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Frontend Usage&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Can action="update" resource={project}&amp;gt;
   &amp;lt;ProjectSettings /&amp;gt;
&amp;lt;/Can&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;API Enforcement&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (!canAccessProject({ user, project, action })) {
  throw new ForbiddenError();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interesting Facts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;ABAC originated from military-grade access control systems where contextual authorization was mandatory. &lt;a href="https://www.nist.gov/publications/guide-attribute-based-access-control-abac-definition-and-considerations?" rel="noopener noreferrer"&gt;NIST ABAC Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Google’s BeyondCorp security model heavily relies on context-aware authorization principles. &lt;a href="https://cloud.google.com/beyondcorp?" rel="noopener noreferrer"&gt;Google BeyondCorp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Modern cloud IAM systems from AWS and Azure support ABAC-style policies.&lt;a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction_attribute-based-access-control.html?" rel="noopener noreferrer"&gt;AWS IAM ABAC Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Policy engines like OPA (Open Policy Agent) are increasingly adopted in Kubernetes and cloud-native systems.&lt;a href="https://www.openpolicyagent.org/?" rel="noopener noreferrer"&gt;Open Policy Agent&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fine-grained authorization is becoming a core requirement for enterprise SaaS platforms due to increasing compliance and multi-tenant security demands. Auth Authorization Trends](&lt;a href="https://auth0.com/blog/what-is-abac-and-how-to-implement-it/" rel="noopener noreferrer"&gt;https://auth0.com/blog/what-is-abac-and-how-to-implement-it/&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Open Policy Agent is widely adopted across cloud-native infrastructure for centralized policy enforcement.&lt;a href="https://www.cncf.io/projects/open-policy-agent-opa/" rel="noopener noreferrer"&gt;CNCF OPA Project&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Zero Trust architectures increasingly depend on contextual authorization instead of static role systems. &lt;a href="https://learn.microsoft.com/en-us/security/zero-trust/zero-trust-overview?" rel="noopener noreferrer"&gt;Microsoft Zero Trust Model&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ’s
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. Is RBAC obsolete?&lt;/strong&gt;&lt;br&gt;
No. RBAC is still useful.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RBAC for broad roles&lt;/li&gt;
&lt;li&gt;ABAC for fine-grained rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Q2. Is ABAC harder to implement?&lt;/strong&gt;&lt;br&gt;
Yes, but it scales much better for complex systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. Should authorization live in the frontend?&lt;/strong&gt;&lt;br&gt;
No.Frontend checks are UX enhancements only and Backend enforcement is mandatory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. What is the biggest authorization mistake?&lt;/strong&gt;&lt;br&gt;
Embedding permission logic directly inside business code everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. Which companies benefit most from ABAC?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enterprise SaaS&lt;/li&gt;
&lt;li&gt;FinTech&lt;/li&gt;
&lt;li&gt;Healthcare&lt;/li&gt;
&lt;li&gt;Government systems&lt;/li&gt;
&lt;li&gt;Multi-tenant platforms&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Modern applications require more than static roles.&lt;/p&gt;

&lt;p&gt;As systems scale, authorization becomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context-aware&lt;/li&gt;
&lt;li&gt;Dynamic&lt;/li&gt;
&lt;li&gt;Fine-grained&lt;/li&gt;
&lt;li&gt;Policy-driven
The future of scalable security architecture lies beyond simple RBAC systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By adopting ABAC principles, centralized policy engines, and clean authorization architecture, teams can build systems that are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More secure&lt;/li&gt;
&lt;li&gt;Easier to scale&lt;/li&gt;
&lt;li&gt;Easier to audit&lt;/li&gt;
&lt;li&gt;Easier to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“Authentication identifies users. Authorization defines boundaries.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;About the Author:&lt;em&gt;Mayank is a web developer at &lt;a href="https://www.addwebsolution.com/" rel="noopener noreferrer"&gt;AddWebSolution&lt;/a&gt;, building scalable apps with PHP, Node.js &amp;amp; React. Sharing ideas, code, and creativity.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>backenddevelopment</category>
      <category>systemdesign</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Handling Long-Running Processes in UI</title>
      <dc:creator>Abodh Kumar</dc:creator>
      <pubDate>Fri, 22 May 2026 10:47:24 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/handling-long-running-processes-in-ui-18hm</link>
      <guid>https://dev.to/addwebsolutionpvtltd/handling-long-running-processes-in-ui-18hm</guid>
      <description>&lt;p&gt;In modern web and desktop applications, users routinely trigger operations that cannot complete in a single render cycle - file uploads, video transcoding, AI generation, data exports, batch imports, report generation, and complex calculations. Unlike a simple API call that resolves in milliseconds, these processes can take seconds, minutes, or even hours. How your interface communicates progress, allows cancellation, and survives navigation defines whether users trust your application or abandon it mid-task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Always model four states - queued, running, completed, and failed - for every long-running process, no exceptions.&lt;/li&gt;
&lt;li&gt;Show determinate progress (with a percentage or ETA) whenever the duration is predictable; never leave users guessing how long they have to wait.&lt;/li&gt;
&lt;li&gt;Decouple long-running work from the component lifecycle - the user must be free to navigate without aborting the task.&lt;/li&gt;
&lt;li&gt;Provide cancellation for any process that takes longer than three seconds; users must always feel in control.&lt;/li&gt;
&lt;li&gt;Use Web Workers, background tabs, or server-side jobs to keep the main thread responsive during heavy computation.&lt;/li&gt;
&lt;li&gt;Persist process state across reloads and navigations so users can recover after refresh, network drops, or accidental tab closures.&lt;/li&gt;
&lt;li&gt;Notify users on completion through in-app toasts, browser notifications, or email - never expect them to keep watching the screen.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Understanding the Four Process States&lt;/li&gt;
&lt;li&gt;Communicating Progress - Determinate, Indeterminate &amp;amp; Multi-Stage&lt;/li&gt;
&lt;li&gt;Handling Cancellation, Failure &amp;amp; Recovery&lt;/li&gt;
&lt;li&gt;Building a Custom useLongRunningTask Hook&lt;/li&gt;
&lt;li&gt;Advanced Techniques&lt;/li&gt;
&lt;li&gt;Stats &amp;amp; Interesting Facts&lt;/li&gt;
&lt;li&gt;FAQ&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;A long-running process is any operation whose duration exceeds the user's natural attention span for a single interaction - generally anything beyond two to three seconds. Examples are everywhere in modern software: uploading a 500 MB video, running an AI image generation job, exporting a 100,000-row spreadsheet, transcoding audio, performing a database migration triggered from an admin panel, or compiling a project in a browser-based IDE.&lt;/p&gt;

&lt;p&gt;These processes share a common trait: the user initiates them, then has nothing to do but wait. If your interface handles that wait poorly - by freezing, hiding, or silently failing - users assume your application is broken. Worse, they may close the tab, lose their work, and never return.&lt;/p&gt;

&lt;p&gt;Done well, long-running process UX feels almost magical. The user uploads a file, sees a clear progress indicator, navigates to another page, gets a notification when the upload finishes, and never once feels trapped. Done badly, the same operation produces a frozen browser tab, an indefinite spinner, and a frustrated user.&lt;/p&gt;

&lt;p&gt;This article covers a complete strategy for handling long-running processes in modern web UIs - from foundational state modeling and progress indicators to background workers, cancellation patterns, resumable uploads, and cross-session persistence.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Understanding the Four Process States
&lt;/h2&gt;

&lt;p&gt;Unlike short API calls (which only need three states), long-running processes require a richer state model. Every long-running task should be in exactly one of these four states at any given moment:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.1 The Queued State&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The queued state exists from the moment the user triggers the action until the process actually begins executing. This is non-trivial: many systems queue work behind a rate limiter, a worker pool, or a server-side job queue. The user needs to know their request was accepted, where they are in the queue, and roughly when execution will start. Skipping this state and jumping straight to "running" lies to the user when there is real waiting time before any work happens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.2 The Running State&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The running state covers the actual execution. This is where progress communication matters most. Depending on the nature of the task, you may know the percentage complete (a file upload), the current step out of N (a multi-stage pipeline), or only that work is happening (an open-ended AI generation). Each calls for a different visual treatment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.3 The Completed State&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the process finishes successfully, the UI must transition into a clear success state - ideally with a summary of what was produced, a link to the result, and an action the user can take next. A common mistake is to silently return to the previous screen, which leaves users uncertain whether the task actually succeeded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.4 The Failed or Cancelled State&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Any failure - whether from a server error, a timeout, a quota exceedance, or an explicit user cancellation - must surface to the user with a clear explanation and, where applicable, a way to retry or resume. Cancellation deserves special treatment: it was intentional, so the message should confirm rather than alarm.&lt;/p&gt;

&lt;p&gt;A foundational pattern using a single status enum keeps these states explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useReducer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// idle | queued | running | completed | failed | cancelled&lt;/span&gt;
  &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;processReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;queue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;queued&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancelled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ExportButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;processReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startExport&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;queue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createExportJob&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unsubscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;subscribeToJob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;onProgress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;onComplete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;startExport&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Start&lt;/span&gt; &lt;span class="nx"&gt;Export&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;queued&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;QueuedIndicator&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProgressBar&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DownloadLink&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ErrorPanel&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;onRetry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;startExport&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancelled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CancelledNotice&lt;/span&gt; &lt;span class="nx"&gt;onRestart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;startExport&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Communicating Progress - Determinate, Indeterminate &amp;amp; Multi-Stage
&lt;/h2&gt;

&lt;p&gt;The single biggest factor in how users tolerate waiting is whether they can predict how much longer they have to wait. Research in UX psychology consistently shows that perceived duration is far more important than actual duration - and the right progress indicator can make a 30-second wait feel shorter than a 10-second wait with no feedback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.1 Determinate Progress Bars - When You Know the Total&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A determinate progress bar shows a percentage, an ETA, or both. Use it whenever you can reasonably estimate the total work - file uploads (you know the byte size), batch imports (you know the row count), or paginated data exports (you know the page count). Always pair the visual bar with a numeric percentage and, if possible, an estimated time remaining.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProgressBar&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;etaSeconds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;progress-container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;progressbar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
         &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;valuenow&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;valuemin&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;valuemax&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;progress-track&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;progress-fill&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;progress-meta&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;%&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;etaSeconds&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;formatEta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;etaSeconds&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="nx"&gt;remaining&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;formatEta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;s`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;m`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;seconds&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;h`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.2 Indeterminate Indicators - For Unknown Durations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some tasks have genuinely unknowable duration: an AI text generation, a complex SQL query, or any process behind an opaque external service. In these cases, a determinate bar would be a lie. Use an indeterminate animation - a sweeping bar, a pulsing shape, or a series of step descriptions - to communicate "we are working" without falsely promising a specific finish time. Where possible, replace pure motion with descriptive status text that updates as the task progresses ("Analyzing input...", "Generating response...", "Finalizing output...").&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;IndeterminateProgress&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;stage&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;indeterminate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;indeterminate-bar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stage-label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Working...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// CSS&lt;/span&gt;
&lt;span class="cm"&gt;/*
.indeterminate-bar {
  position: relative;
  height: 4px;
  background: #e0e0e0;
  overflow: hidden;
  border-radius: 2px;
}
.indeterminate-bar::after {
  content: '';
  position: absolute;
  height: 100%;
  width: 30%;
  background: #4a90e2;
  animation: slide 1.4s ease-in-out infinite;
}
@keyframes slide {
  0%   { left: -30%; }
  100% { left: 100%; }
}
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.3 Multi-Stage Progress&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many real-world processes are pipelines: upload → validate → process → export → notify. A single progress bar collapses this into one number, hiding useful information. A stepper or checklist UI exposes each stage individually, so the user sees not only "we are 60% done" but "we just finished processing and are now exporting." This builds confidence that the system is making real progress, especially for long pipelines where any single stage may pause for a while.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MultiStageProgress&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentStage&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ol&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stage-list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;stages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;currentStage&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;done&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;currentStage&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`stage stage-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stage-icon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;done&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✓&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Spinner&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;○&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stage-label&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stage-detail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="p"&gt;)}&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;})}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ol&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The function of design is letting design function. - Micha Commeren&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Handling Cancellation, Failure &amp;amp; Recovery
&lt;/h2&gt;

&lt;p&gt;A process that cannot be cancelled is a trap. A process that fails without explanation is worse. Robust long-running UX treats cancellation, failure, and recovery as first-class concerns rather than afterthoughts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.1 Cancellation - Always Available, Always Clean&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Any process taking longer than three seconds should expose a cancel control. Cancellation must be honest: if the work is already partially complete on the server, the UI must explain what happens to that partial result (kept, discarded, or rolled back). Silent or unreliable cancellation - where the button does nothing or the work continues anyway - destroys user trust faster than an outright crash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CancellableUpload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProgress&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controllerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;controllerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;uploadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;onProgress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;setProgress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AbortError&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancelled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;controllerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProgressBar&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Cancel&lt;/span&gt; &lt;span class="nx"&gt;upload&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancelled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Upload&lt;/span&gt; &lt;span class="nx"&gt;cancelled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;No&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="nx"&gt;was&lt;/span&gt; &lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.2 Distinguishing Failure Modes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Long-running failures are not all the same. Group them into categories your UI can respond to differently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recoverable network failures - The connection dropped mid-upload. Offer automatic retry, ideally resuming from the last successful chunk rather than restarting.&lt;/li&gt;
&lt;li&gt;Server-side processing errors - The server accepted the input but couldn't complete the work (transcoding error, malformed data row, AI safety rejection). Show the specific reason and, where possible, a way to fix and retry.&lt;/li&gt;
&lt;li&gt;Quota or limit errors - The user hit a plan limit, rate limit, or storage cap. Surface the specific limit and a clear path to resolve it (upgrade, free up space, wait).&lt;/li&gt;
&lt;li&gt;Timeouts - The process exceeded a maximum allowed duration. Communicate this honestly rather than letting the spinner run indefinitely.&lt;/li&gt;
&lt;li&gt;User cancellation - Treat differently from failure. Confirm the cancellation succeeded, state what was kept or discarded, and offer a clear restart path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4.3 Recovery and Resumability&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For any process that takes more than a few seconds, design for the assumption that something will go wrong - the network will drop, the tab will close, the laptop will sleep. The most resilient UIs persist process state to durable storage (IndexedDB, localStorage, or a server-side job record) so that on next load, the user can resume from where they left off.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useResumableUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saved&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`upload:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;saved&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;saved&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`upload:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_SIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;uploadChunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;CHUNK_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`upload:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resume&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Building a Custom useLongRunningTask Hook
&lt;/h2&gt;

&lt;p&gt;Repeating queue/run/cancel/recover logic in every component is unmaintainable. A custom hook centralizes the lifecycle, exposes a clean API to components, and ensures every long-running task in your app behaves consistently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.1 The useLongRunningTask Hook&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useLongRunningTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskFn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;processReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controllerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controllerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;controllerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;controllerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;queue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;taskFn&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;onProgress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aborted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AbortError&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;taskFn&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;controllerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;controllerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reset&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;controllerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;reset&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VideoExport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;projectId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useLongRunningTask&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exportVideo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Export&lt;/span&gt; &lt;span class="nx"&gt;Video&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;queued&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProgressBar&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Cancel&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DownloadLink&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;onDismiss&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ErrorPanel&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;onRetry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;projectId&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cancelled&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Export&lt;/span&gt; &lt;span class="nx"&gt;cancelled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Start&lt;/span&gt; &lt;span class="nx"&gt;over&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5.2 Decoupling From the Component Lifecycle&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A subtle but critical point: the hook above ties the task lifetime to the component that started it. If the user navigates away, the task is cancelled. For most long-running processes, that is the wrong default - users expect to start a job, leave the page, and find it still running when they return. The fix is to store the running task in a global state container (Zustand, Redux, or a React context provider above the router) so it survives navigation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zustand&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useTaskStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="c1"&gt;// taskId -&amp;gt; task state&lt;/span&gt;

  &lt;span class="nf"&gt;startTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;taskFn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

    &lt;span class="nf"&gt;taskFn&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;onProgress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="nf"&gt;cancelTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now any component anywhere in the tree can read the task's current state, and the task continues to run regardless of which page is mounted.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Progress is impossible without change, and those who cannot communicate progress will be assumed to have made none. - Adapted from George Bernard Shaw&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  6. Advanced Techniques
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;6.1 Web Workers for CPU-Bound Tasks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your long-running process is heavy computation in the browser - image processing, parsing a large CSV, encrypting a file - running it on the main thread will freeze the UI. A Web Worker moves the work to a background thread, keeping animations smooth and inputs responsive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useWorkerTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workerUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProgress&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setResult&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;workerRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;workerUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;workerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onmessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;setProgress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;done&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;setResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;workerRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;terminate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cancel&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6.2 Server-Sent Events for Real-Time Progress&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For long-running server-side jobs, polling for status is wasteful and laggy. Server-Sent Events (SSE) give you a one-way streaming connection that pushes progress updates as the server produces them - ideal for AI generation, video transcoding, or any workflow where the server is the source of truth.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useJobStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connecting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/jobs/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/stream`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;progress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stage&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Connection lost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6.3 Browser Notifications and Tab-Title Updates&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Users will not stare at a progress bar for ten minutes. They will switch tabs, check email, or walk away. To bring them back when the work is done, use the Notifications API for a native push and update the document title with a status icon so a glance at the tab strip tells them whether to come back.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useCompletionNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✓ Done — &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;originalTitle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;permission&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;granted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Process complete&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✗ Failed — &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;originalTitle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;running&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⏳ Working — &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;originalTitle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;originalTitle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always request notification permission contextually - at the moment the user starts a long task, not on page load. Cold-start permission prompts are denied 90% of the time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.4 Persistent Background Jobs With Service Workers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For uploads, exports, or syncs that should survive even if the user closes the tab, register a Service Worker with the Background Sync API. The browser holds the work in a queue and resumes it when the network is available, even if the originating page is gone. This pattern powers the "your message will be sent when you're back online" experience in modern messaging apps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;queueBackgroundUpload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;serviceWorker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;reg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upload-pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;stashFileInIndexedDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// In the service worker&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upload-pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;processQueuedUploads&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Stats &amp;amp; Interesting Facts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;According to research compiled by the Nielsen Norman Group, the upper limit for keeping a user's attention focused on a dialog without progress feedback is approximately 10 seconds - past this, users start switching context and may abandon the task entirely.
Source: &lt;a href="https://www.nngroup.com/articles/response-times-3-important-limits/" rel="noopener noreferrer"&gt;https://www.nngroup.com/articles/response-times-3-important-limits/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A study by Chris Harrison and colleagues at Carnegie Mellon University found that progress bars with backward-decelerating animation (fast at first, slowing down) are perceived as up to 12% faster than uniform progress bars covering the same actual time.
Source: &lt;a href="https://www.chrisharrison.net/index.php/Research/ProgressBars" rel="noopener noreferrer"&gt;https://www.chrisharrison.net/index.php/Research/ProgressBars&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Akamai's 2017 online retail performance report found that a two-second delay during a checkout-related process more than doubled the bounce rate. Long-running processes without clear progress feedback amplify this effect dramatically.Source:
&lt;a href="https://www.akamai.com/newsroom/press-release/akamai-releases-spring-2017-state-of-online-retail-performance-report" rel="noopener noreferrer"&gt;https://www.akamai.com/newsroom/press-release/akamai-releases-spring-2017-state-of-online-retail-performance-report&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;According to Cloudflare's State of Application Security report, large-file uploads abandoned mid-transfer due to lack of resumability cost e-commerce and SaaS platforms millions of dollars in lost user productivity each year.Source: &lt;a href="https://www.cloudflare.com/lp/state-of-application-security-report/" rel="noopener noreferrer"&gt;https://www.cloudflare.com/lp/state-of-application-security-report/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Chrome's Web Vitals data shows that pages with a Total Blocking Time over 600ms - common when long-running processes run on the main thread - have measurably higher abandonment rates than pages that offload work to Web Workers.Source: &lt;a href="https://web.dev/articles/tbt" rel="noopener noreferrer"&gt;https://web.dev/articles/tbt&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A 2024 study published by the Baymard Institute found that 27% of e-commerce checkout abandonment is attributable to interfaces that appear unresponsive during payment processing - a textbook long-running process UX failure.Source: &lt;a href="https://baymard.com/lists/cart-abandonment-rate" rel="noopener noreferrer"&gt;https://baymard.com/lists/cart-abandonment-rate&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Stripe's developer documentation reports that approximately 8% of payment-confirmation flows experience a network interruption between request and confirmation, making resumable, idempotent process handling essential for financial UX.Source: &lt;a href="https://stripe.com/docs/payments/payment-intents" rel="noopener noreferrer"&gt;https://stripe.com/docs/payments/payment-intents&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. When should a process be treated as "long-running" and given a dedicated UX, rather than just a spinner?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: A useful heuristic is the three-second rule: any process that may take longer than three seconds in the 95th-percentile case deserves dedicated long-running UX. Below that threshold, a simple loading state is sufficient. Above it, you need progress feedback, cancellation, and survivability across navigation. Be honest about your real-world latency distribution, not the best case on a fast network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Should a long-running task be tied to a single component, or should it survive navigation?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: It depends on the user's mental model. If the task is conceptually local to the page (e.g., a search refinement), tying it to the component is fine. If the user thinks of the task as something they "started" - an upload, an export, a video render - it should survive navigation, ideally by living in a global state container or being driven by a server-side job that the UI subscribes to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. How do I show progress when the duration is genuinely unknown?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: Show an indeterminate animation paired with descriptive status text that updates as the process advances ("Connecting...", "Analyzing...", "Generating output..."). This gives the user something to read and watch, signals real progress, and avoids the lie of a fake percentage. Never display a fabricated ETA - users notice when bars stall at 99% and lose trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. What is the best way to cancel a long-running task that is partially complete on the server?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: Always be explicit about what cancellation means in the UI. If the server can roll back the partial work, say so ("Cancel and discard"). If the partial work will be kept, say that instead ("Cancel — your progress so far will be saved"). Ambiguity here causes users to hesitate or, worse, to refresh the page mid-task and lose data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. How should I handle a tab being closed in the middle of a long-running process?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: For client-driven processes, persist progress to IndexedDB or localStorage and offer to resume on next visit. For server-driven jobs, use a server-side job record so the work continues regardless of the client - the user can simply reconnect and the UI subscribes to the existing job. For uploads specifically, use a chunked, resumable protocol (tus.io is the open standard).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Should I use polling or streaming (SSE/WebSockets) for progress updates?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: Streaming is strictly better for user experience: lower latency, less server load, no wasted requests. Use polling only when streaming infrastructure is unavailable, or as a fallback when the stream connection drops. If you do poll, use exponential backoff and a maximum interval to avoid hammering the server for jobs that take hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. How do I keep the UI responsive during a CPU-heavy process in the browser?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: Move the work to a Web Worker. The main thread is responsible for rendering and input handling - any long-running computation there will freeze the UI, drop frames, and make the page feel broken. Workers communicate via postMessage and have access to most APIs you need for compute-heavy tasks. For very large datasets, consider streaming results back in chunks rather than waiting for the entire computation to finish.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. What's the right way to notify a user that a long-running task has finished if they've switched tabs?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: Layer your notifications. First, update the document title with a status icon - users glance at tab strips constantly. Second, fire a Web Notification if permission has been granted. Third, for tasks that may complete after the user has fully closed the app, send an email or push notification from the backend. Never rely on a single channel; users check different channels at different times.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The most precious resource we all have is time. - Steve Jobs&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  9. Conclusion
&lt;/h2&gt;

&lt;p&gt;Long-running processes are not edge cases - they are central to modern application UX. Every minute spent improving how your app handles uploads, exports, generations, and batch jobs returns disproportionate user trust. The strategies described in this article each address a specific dimension of that experience:&lt;/p&gt;

&lt;p&gt;Explicit four-state modeling ensures queued, running, completed, and failed are always represented, never collapsed or implicit.&lt;br&gt;
Progress communication - whether determinate, indeterminate, or multi-stage - turns waiting from a passive frustration into an informed wait.&lt;/p&gt;

&lt;p&gt;Cancellation and recovery put the user in control, ensuring no process can trap them or destroy work silently.&lt;br&gt;
Custom hooks and global stores decouple long-running tasks from the component lifecycle, so navigation and unmounts don't kill in-flight work.&lt;/p&gt;

&lt;p&gt;Web Workers, SSE, and Service Workers keep the UI responsive, pipe real-time updates, and let work survive tab closures.&lt;br&gt;
Notifications and title updates bring users back the moment their work is ready, instead of forcing them to babysit a progress bar.&lt;/p&gt;

&lt;p&gt;The browser and modern frameworks give you every primitive you need. The responsibility lies with developers to compose them with care, treat long-running UX as a design problem rather than an engineering afterthought, and remember that for many users the difference between a finished task and an abandoned one is whether the interface respected their time.&lt;/p&gt;

&lt;p&gt;The best long-running UX feels like a quiet promise kept. The user starts something, the system honors that intent through whatever conditions arise, and at the end - whether seconds or hours later - delivers the result clearly. That invisibility, that reliability, is the mark of mature product engineering.&lt;/p&gt;

&lt;p&gt;About the Author: &lt;em&gt;Abodh is a PHP and Laravel Developer at &lt;a href="https://www.addwebsolution.com/" rel="noopener noreferrer"&gt;AddWeb Solution&lt;/a&gt;, skilled in MySQL, REST APIs, JavaScript, Git, and Docker for building robust web applications.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
      <category>progressbar</category>
    </item>
    <item>
      <title>Accessibility (a11y) in Modern Apps</title>
      <dc:creator>Vatsal Acharya</dc:creator>
      <pubDate>Fri, 15 May 2026 12:17:50 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/accessibility-a11y-in-modern-apps-2oj0</link>
      <guid>https://dev.to/addwebsolutionpvtltd/accessibility-a11y-in-modern-apps-2oj0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect.” - Tim Berners-Lee&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Accessibility (a11y) ensures apps are usable by everyone, including people with disabilities&lt;/li&gt;
&lt;li&gt;It improves UX, SEO, and performance, not just compliance&lt;/li&gt;
&lt;li&gt;Modern apps must support keyboard navigation, screen readers, and visual adjustments&lt;/li&gt;
&lt;li&gt;Ignoring accessibility leads to real business loss + legal risks&lt;/li&gt;
&lt;li&gt;Accessibility is not a feature - it’s a foundation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Index
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is Accessibility (a11y)?&lt;/li&gt;
&lt;li&gt;Why Accessibility Matters&lt;/li&gt;
&lt;li&gt;Types of Disabilities to Consider&lt;/li&gt;
&lt;li&gt;Core Principles (WCAG)&lt;/li&gt;
&lt;li&gt;Common Accessibility Mistakes&lt;/li&gt;
&lt;li&gt;Practical Implementation (Frontend + Backend)&lt;/li&gt;
&lt;li&gt;Tools for Testing Accessibility&lt;/li&gt;
&lt;li&gt;Real-World Examples&lt;/li&gt;
&lt;li&gt;Key Takeaways&lt;/li&gt;
&lt;li&gt;FAQs&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What is Accessibility (a11y)?
&lt;/h2&gt;

&lt;p&gt;Accessibility (often shortened as a11y, because there are 11 letters between “a” and “y”) refers to designing applications that can be used by people with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visual impairments&lt;/li&gt;
&lt;li&gt;Hearing impairments&lt;/li&gt;
&lt;li&gt;Motor disabilities&lt;/li&gt;
&lt;li&gt;Cognitive limitations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It ensures your app works with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Screen readers&lt;/li&gt;
&lt;li&gt;Keyboard-only navigation&lt;/li&gt;
&lt;li&gt;Voice controls&lt;/li&gt;
&lt;li&gt;Assistive technologies&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Accessibility Matters
&lt;/h2&gt;

&lt;p&gt;Most devs think: “My users don’t need this.”&lt;br&gt;
That’s usually wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Real Users Need It&lt;/strong&gt;&lt;br&gt;
Millions rely on assistive tech daily&lt;br&gt;
Temporary disabilities (injuries, fatigue) also matter&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Better UX for Everyone&lt;/strong&gt;&lt;br&gt;
Keyboard shortcuts = power users love it&lt;br&gt;
Proper contrast = better readability for all&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. SEO Benefits&lt;/strong&gt;&lt;br&gt;
Semantic HTML improves indexing&lt;br&gt;
Alt text improves image discoverability&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Legal Compliance&lt;/strong&gt;&lt;br&gt;
Many countries enforce accessibility standards&lt;br&gt;
Non-compliance can lead to lawsuits&lt;/p&gt;
&lt;h2&gt;
  
  
  Types of Disabilities to Consider
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Visual&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blindness&lt;/li&gt;
&lt;li&gt;Low vision&lt;/li&gt;
&lt;li&gt;Color blindness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Hearing&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deafness&lt;/li&gt;
&lt;li&gt;Hard of hearing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Motor&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limited hand movement&lt;/li&gt;
&lt;li&gt;Cannot use a mouse&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cognitive&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dyslexia&lt;/li&gt;
&lt;li&gt;ADHD&lt;/li&gt;
&lt;li&gt;Memory issues&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“The Web is fundamentally designed to work for all people, whatever their hardware, software, language, location, or ability.” - World Wide Web Consortium (W3C)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Core Principles (WCAG)
&lt;/h2&gt;

&lt;p&gt;Accessibility is guided by WCAG (Web Content Accessibility Guidelines):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Perceivable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Users must be able to see/hear content&lt;br&gt;
 → Example: Alt text for images&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Operable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Users must be able to navigate&lt;br&gt;
 → Example: Keyboard navigation&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Understandable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Content must be clear and predictable&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Robust&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Works across devices and assistive tech&lt;/p&gt;
&lt;h2&gt;
  
  
  Common Accessibility Mistakes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Missing alt text on images&lt;/li&gt;
&lt;li&gt; Using div instead of semantic tags (button, nav)&lt;/li&gt;
&lt;li&gt; No keyboard navigation support&lt;/li&gt;
&lt;li&gt; Poor color contrast&lt;/li&gt;
&lt;li&gt; Forms without labels&lt;/li&gt;
&lt;li&gt; Auto-playing videos without controls&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Practical Implementation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Semantic HTML (Most Important)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- Correct --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"submit()"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- Wrong --&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Alt Text for Images&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"product.jpg"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Red running shoes with white sole"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Keyboard Accessibility&lt;/strong&gt;&lt;br&gt;
Ensure all interactive elements are reachable via Tab&lt;br&gt;
Use :focus styles&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. ARIA (Use Carefully)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Close menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;X&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rule: Don’t use ARIA if native HTML can do the job&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Forms Accessibility&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Color Contrast&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimum ratio: 4.5:1&lt;/li&gt;
&lt;li&gt;Avoid light gray text on white background&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7. Screen Reader Support&lt;/strong&gt;&lt;br&gt;
Test using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NVDA (Windows)&lt;/li&gt;
&lt;li&gt;VoiceOver (Mac)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;8. Accessibility in React&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 Submit
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
 Submit
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;9. Accessibility in Laravel (Backend)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validate meaningful error messages&lt;/li&gt;
&lt;li&gt;Return proper status codes&lt;/li&gt;
&lt;li&gt;Support localization (multi-language)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“Accessibility is not a feature, it is a social trend.” - Antonio Santos&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Tools for Testing Accessibility
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Lighthouse (Chrome DevTools)&lt;/li&gt;
&lt;li&gt;axe DevTools&lt;/li&gt;
&lt;li&gt;WAVE&lt;/li&gt;
&lt;li&gt;Screen readers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real-World Example
&lt;/h2&gt;

&lt;p&gt;Imagine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A user cannot use a mouse&lt;/li&gt;
&lt;li&gt;Your app has no keyboard navigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your app is completely unusable&lt;br&gt;
That’s not a “minor bug” - that’s total failure&lt;/p&gt;

&lt;h2&gt;
  
  
  Interesting Facts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Over 1 billion people live with some form of disability. &lt;a href="https://www.who.int/news-room/fact-sheets/detail/disability-and-health" rel="noopener noreferrer"&gt;Source&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Accessibility improvements can increase conversion rates &lt;a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/Accessibility/What_is_accessibility" rel="noopener noreferrer"&gt;Source&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Many accessibility fixes take less than 5 minutes&lt;/li&gt;
&lt;li&gt;Around 40.9% of color combinations on major websites still fail WCAG contrast requirements. &lt;a href="https://arxiv.org/abs/2602.24067" rel="noopener noreferrer"&gt;Source&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1: Is accessibility only for disabled users?&lt;/strong&gt;&lt;br&gt;
No - it improves usability for everyone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2: Is ARIA enough?&lt;/strong&gt;&lt;br&gt;
No - proper HTML matters more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3: Does accessibility slow development?&lt;/strong&gt;&lt;br&gt;
Initially yes, but saves time later.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Designing for accessibility does not only benefit people with disabilities - it benefits everyone.” - Microsoft&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Accessibility is not optional anymore.&lt;br&gt;
Modern apps must be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inclusive&lt;/li&gt;
&lt;li&gt;Usable&lt;/li&gt;
&lt;li&gt;Compliant
If your app isn’t accessible, it’s not truly “modern.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;About the Author:&lt;em&gt;Vatsal is a web developer at &lt;a href="https://www.addwebsolution.com/" rel="noopener noreferrer"&gt;AddWebSolution&lt;/a&gt;. Building web magic with Laravel, PHP, MySQL, Vue.js &amp;amp; more.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>a11y</category>
      <category>frontend</category>
      <category>react</category>
    </item>
    <item>
      <title>Managing Environment Variables in React Properly</title>
      <dc:creator>Lakashya Upadhyay</dc:creator>
      <pubDate>Wed, 29 Apr 2026 07:36:27 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/managing-environment-variables-in-react-properly-2913</link>
      <guid>https://dev.to/addwebsolutionpvtltd/managing-environment-variables-in-react-properly-2913</guid>
      <description>&lt;p&gt;Environment variables in React often look simple at first, but they can quickly become a source of bugs, leaks, and deployment confusion if handled carelessly. In modern React apps, especially with Vite or CRA, the way you name, store, and access environment variables matters just as much as the values themselves.&lt;/p&gt;

&lt;p&gt;This guide walks through the most common mistakes developers make when managing environment variables in React, explains why they happen, and shows how to set up a clean and reliable environment configuration strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use the correct prefix for your build tool: REACT_APP_ for CRA and VITE_ for Vite.youtubevite&lt;/li&gt;
&lt;li&gt;Never store secrets in client-side environment variables, because anything shipped to the browser can be inspected.&lt;/li&gt;
&lt;li&gt;Keep .env.local for local-only values and add env files to .gitignore when appropriate.&lt;/li&gt;
&lt;li&gt;Use import.meta.env in Vite instead of process.env.&lt;/li&gt;
&lt;li&gt;Keep environment variables documented, consistent, and validated before the app uses them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Index
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why This Matters&lt;/li&gt;
&lt;li&gt;Mistake 1: Using the Wrong Environment Variable Prefix&lt;/li&gt;
&lt;li&gt;Mistake 2: Treating Client-Side Env Vars Like Secrets&lt;/li&gt;
&lt;li&gt;Mistake 3: Mixing process.env and import.meta.env&lt;/li&gt;
&lt;li&gt;Mistake 4: Hardcoding URLs Instead of Using Config&lt;/li&gt;
&lt;li&gt;Mistake 5: Not Using .env.local Properly&lt;/li&gt;
&lt;li&gt;Mistake 6: Forgetting to Restart the Dev Server&lt;/li&gt;
&lt;li&gt;Mistake 7: Keeping Too Many Environment Files Without a Clear Rule&lt;/li&gt;
&lt;li&gt;Mistake 8: Not Validating Required Variables&lt;/li&gt;
&lt;li&gt;Mistake 9: Committing Sensitive .env Files to Git&lt;/li&gt;
&lt;li&gt;Mistake 10: Poor Naming and Documentation&lt;/li&gt;
&lt;li&gt;Frequently Asked Questions (FAQs)&lt;/li&gt;
&lt;li&gt;Interesting Facts &amp;amp; Stats&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Why This Matters
&lt;/h2&gt;

&lt;p&gt;React environment variables are usually used for API URLs, feature flags, analytics IDs, and deployment settings. The problem is that frontend apps are bundled for the browser, so misconfigured variables can lead to broken builds, undefined values, or exposed configuration that should never have been public.&lt;/p&gt;

&lt;p&gt;A small mistake like using API_URL instead of VITE_API_URL or REACT_APP_API_URL can make the value unavailable in your app. Another common issue is assuming environment variables are private when they are actually shipped to the client bundle.&lt;/p&gt;

&lt;p&gt;A good environment strategy makes your app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier to deploy across dev, staging, and production.&lt;/li&gt;
&lt;li&gt;Safer to maintain in a team.&lt;/li&gt;
&lt;li&gt;Less likely to break from config mistakes.&lt;/li&gt;
&lt;li&gt;Easier to document and onboard new developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Mistake 1: Using the Wrong Environment Variable Prefix
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Your variable name matters more than you think.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In React, the framework or build tool only exposes specific environment variables to the client. In Create React App, custom variables must begin with REACT_APP_, while Vite exposes variables prefixed with VITE_ through import.meta.env.youtube&lt;br&gt;
Example of the mistake:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;text
API_URL=https://api.example.com
js
console.log(import.meta.env.API_URL);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to fix it:&lt;br&gt;
Use the correct prefix for your tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;text&lt;/span&gt;
&lt;span class="nx"&gt;VITE_API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//api.example.com&lt;/span&gt;
&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_API_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;For&lt;/span&gt; &lt;span class="nx"&gt;CRA&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="nx"&gt;text&lt;/span&gt;
&lt;span class="nx"&gt;REACT_APP_API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//api.example.com&lt;/span&gt;
&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REACT_APP_API_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variables are actually available in the app.&lt;/li&gt;
&lt;li&gt;Builds behave consistently across environments.&lt;/li&gt;
&lt;li&gt;Your team knows which config values are intended for frontend use.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Mistake 2: Treating Client-Side Env Vars Like Secrets
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Anything in a React bundle is public.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A very common misunderstanding is storing passwords, private tokens, or database credentials in frontend env files. React environment variables are bundled into client-side code, which means users can inspect them in the browser.&lt;/p&gt;

&lt;p&gt;Example of the mistake:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;text
VITE_STRIPE_SECRET_KEY=sk_live_12345
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is dangerous because any secret exposed to the front end can be discovered by end users.&lt;/p&gt;

&lt;p&gt;How to fix it:&lt;br&gt;
Use frontend env vars only for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public API base URLs.&lt;/li&gt;
&lt;li&gt;Feature flags.&lt;/li&gt;
&lt;li&gt;Analytics public IDs.&lt;/li&gt;
&lt;li&gt;Non-sensitive deployment settings.
Keep sensitive secrets on the backend, and let your React app communicate with a server API instead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better security.&lt;/li&gt;
&lt;li&gt;Less risk of accidental leaks.&lt;/li&gt;
&lt;li&gt;Cleaner separation between frontend and backend responsibilities.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  4. Mistake 3: Mixing process.env and import.meta.env
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Use the syntax your build tool expects.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;One of the most frequent bugs in Vite apps is using process.env, which is a CRA/Node-style pattern. Vite expects values from import.meta.env.&lt;/p&gt;

&lt;p&gt;Example of the mistake in Vite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;VITE_API_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to fix it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;js
console.log(import.meta.env.VITE_API_URL);
In CRA, the equivalent pattern is usually:
js
console.log(process.env.REACT_APP_API_URL);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Avoids undefined errors.&lt;/li&gt;
&lt;li&gt;Keep your code compatible with the correct build environment.&lt;/li&gt;
&lt;li&gt;Reduces confusion when switching between CRA and Vite.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Mistake 4: Hardcoding URLs Instead of Using Config
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Hardcoding works until deployment changes.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A lot of React apps start with hardcoded endpoints like &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;, then later break when deployed to staging or production. This makes the app harder to move between environments and increases refactoring work later.&lt;/p&gt;

&lt;p&gt;Example of the mistake:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8000/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to fix it:&lt;br&gt;
Move environment-specific values into env files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;text
VITE_API_URL=https://api.example.com
js
const baseUrl = import.meta.env.VITE_API_URL;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One codebase works across multiple environments.&lt;/li&gt;
&lt;li&gt;Easier switching between dev and production.&lt;/li&gt;
&lt;li&gt;Cleaner deployment pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Mistake 5: Not Using .env.local Properly
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Local overrides should stay local.”&lt;br&gt;
A common best practice is to place developer-specific values in .env.local, which is intended for local overrides and usually not committed. This is especially useful when each developer has different local endpoints, test credentials, or personal feature flags.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Example structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;text&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;development&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;production&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to fix it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use .env for shared defaults.&lt;/li&gt;
&lt;li&gt;Use .env.local for machine-specific values.&lt;/li&gt;
&lt;li&gt;Use .env.development and .env.production for environment-specific behavior.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cleaner collaboration.&lt;/li&gt;
&lt;li&gt;Fewer accidental git commits of personal values.&lt;/li&gt;
&lt;li&gt;Easier developer onboarding.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Mistake 6: Forgetting to Restart the Dev Server
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Env changes are not always hot-reloaded.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In many React setups, environment file changes do not always appear immediately in the running dev server. If a variable seems stuck or undefined, the first thing to try is restarting the server.&lt;/p&gt;

&lt;p&gt;How to fix it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stop the dev server.&lt;/li&gt;
&lt;li&gt;Save the .env file.&lt;/li&gt;
&lt;li&gt;Start the server again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Saves debugging time.&lt;/li&gt;
&lt;li&gt;Prevents confusion when values appear stale.&lt;/li&gt;
&lt;li&gt;Ensures the bundler picks up the latest config.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. Mistake 7: Keeping Too Many Environment Files Without a Clear Rule
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Too many env files can become configuration chaos.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s easy to end up with .env, .env.local, .env.development, .env.staging, .env.production, and several team-specific variants without a clear structure. That usually makes it hard to know which value is active in which environment.&lt;/p&gt;

&lt;p&gt;How to fix it:&lt;br&gt;
Define a simple convention:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.env for shared defaults.&lt;/li&gt;
&lt;li&gt;.env.local for developer-specific overrides.&lt;/li&gt;
&lt;li&gt;.env.development for local development.&lt;/li&gt;
&lt;li&gt;.env.production for production build values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier to understand.&lt;/li&gt;
&lt;li&gt;Less confusion during deployment.&lt;/li&gt;
&lt;li&gt;More predictable configuration behavior.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  9. Mistake 8: Not Validating Required Variables
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Missing config should fail early.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If your app depends on a value like VITE_API_URL, it should not silently continue with undefined. A missing environment variable often leads to broken API calls, blank screens, or confusing runtime errors.&lt;/p&gt;

&lt;p&gt;How to fix it:&lt;br&gt;
Check required values at app startup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;js
const apiUrl = import.meta.env.VITE_API_URL;

if (!apiUrl) {
  throw new Error('VITE_API_URL is missing');
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fails fast during development.&lt;/li&gt;
&lt;li&gt;Prevents subtle runtime bugs.&lt;/li&gt;
&lt;li&gt;Makes deployment issues easier to catch.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10. Mistake 9: Committing Sensitive .env Files to Git
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Git remembers everything.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another common mistake is committing real environment files to source control. Even if the repo later becomes private, the secret may already have been exposed in history.&lt;/p&gt;

&lt;p&gt;How to fix it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add .env, .env.local, and secret files to .gitignore.&lt;/li&gt;
&lt;li&gt;Commit only .env.example.&lt;/li&gt;
&lt;li&gt;Put placeholder values in examples.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;text&lt;/span&gt;
&lt;span class="nx"&gt;VITE_API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="nx"&gt;VITE_APP_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Protects secrets.&lt;/li&gt;
&lt;li&gt;Makes setup easier for teammates.&lt;/li&gt;
&lt;li&gt;Keeps the repo safer and cleaner.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  11. Mistake 10: Poor Naming and Documentation
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“Clear names save time later.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Vague variable names like URL1, KEY, or TOKEN make maintenance harder. Good naming conventions help your team understand what belongs where and reduce mistakes when moving between projects.&lt;/p&gt;

&lt;p&gt;Better names:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;VITE_API_URL&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VITE_APP_NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VITE_GOOGLE_MAPS_KEY&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REACT_APP_BACKEND_URL&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How to fix it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use uppercase letters and underscores.&lt;/li&gt;
&lt;li&gt;Prefix only values meant for the frontend.&lt;/li&gt;
&lt;li&gt;Document each variable in your README or .env.example.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easier onboarding.&lt;/li&gt;
&lt;li&gt;Better readability.&lt;/li&gt;
&lt;li&gt;Less confusion during refactoring or deployment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  12. Frequently Asked Questions (FAQs)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q. Can I use environment variables to hide secrets in React?&lt;/strong&gt;&lt;br&gt;
A. No. Anything shipped to the browser is visible to users, so secrets should stay on the backend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q. Why is import.meta.env undefined in my Vite app?&lt;/strong&gt;&lt;br&gt;
A. The variable probably does not use the VITE_ prefix, or the dev server needs a restart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q. What prefix should I use in CRA?&lt;/strong&gt;&lt;br&gt;
A. Use REACT_APP_ for custom variables in Create React App.youtubeconfigu&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q. Should I commit .env files?&lt;/strong&gt;&lt;br&gt;
A. Usually not for real secrets. Commit .env.example instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q. Do I need separate env files for every environment?&lt;/strong&gt;&lt;br&gt;
A. Not always, but a clear convention for local, development, and production makes projects easier to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Interesting Facts &amp;amp; Stats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Vite exposes custom client-side env variables through import.meta.env, and only variables prefixed with VITE_ are exposed by default. Source: Vite Env Variables and Modes&lt;/li&gt;
&lt;li&gt;CRA requires the REACT_APP_ prefix for custom environment variables to be available in React code. Source: React .env File Hierarchy | Master Environment Variables in CRA &amp;amp; Vite&lt;/li&gt;
&lt;li&gt;Many environment variable bugs are not code bugs at all, but naming or build-tool configuration issues. Source: React Environment Variables: Basics, Tutorial, and Best Practices&lt;/li&gt;
&lt;li&gt;Using .env.local for local-only settings is a widely recommended pattern for safer collaboration. Source: React Environment Variables: Basics, Tutorial, and Best Practices&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  14. Conclusion
&lt;/h2&gt;

&lt;p&gt;Managing environment variables in React is less about storing values and more about building a predictable configuration system. Once you follow the right prefixing rules, keep secrets off the client, and separate local, development, and production values properly, environment setup becomes much more reliable.&lt;/p&gt;

&lt;p&gt;A good React environment strategy is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the correct build-tool syntax.&lt;/li&gt;
&lt;li&gt;Keep secrets on the server.&lt;/li&gt;
&lt;li&gt;Document variables clearly.&lt;/li&gt;
&lt;li&gt;Fail fast when config is missing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;About the Author: Lakashya is a full‑stack Laravel developer at &lt;a href="https://www.addwebsolution.com/" rel="noopener noreferrer"&gt;AddWeb Solution &lt;/a&gt;specializing in scalable, real‑time applications with PHP and modern frontends.&lt;/p&gt;

</description>
      <category>reactbestpractices</category>
      <category>environmentvariables</category>
      <category>reactjsdevelopment</category>
      <category>frontendarchitecture</category>
    </item>
    <item>
      <title>Building Reusable UI Components in React (Clean &amp; Scalable Approach)</title>
      <dc:creator>Ankit Parmar</dc:creator>
      <pubDate>Mon, 27 Apr 2026 08:33:36 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/building-reusable-ui-components-in-react-clean-scalable-approach-gp1</link>
      <guid>https://dev.to/addwebsolutionpvtltd/building-reusable-ui-components-in-react-clean-scalable-approach-gp1</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“Programs must be written for people to read, and only incidentally for machines to execute.” - Harold Abelson&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Modern frontend applications grow fast-and without a solid component strategy, they become hard to maintain, inconsistent, and fragile. Reusable UI components solve this by promoting consistency, reducing duplication, and enabling scalable architecture.&lt;/p&gt;

&lt;p&gt;But simply “reusing components” isn’t enough. Poorly designed components can create more problems than they solve.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how to build clean, reusable, and scalable UI components in React, focusing on architecture, patterns, and real-world practices-not just syntax.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Reusable Components Matter
&lt;/h2&gt;

&lt;p&gt;Without reusable components, teams often face:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI inconsistencies across pages&lt;/li&gt;
&lt;li&gt;Duplicate logic and styling&lt;/li&gt;
&lt;li&gt;Difficult debugging and updates&lt;/li&gt;
&lt;li&gt;Slower development velocity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reusable components help you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Standardize design and behavior&lt;/li&gt;
&lt;li&gt;Reduce code duplication&lt;/li&gt;
&lt;li&gt;Improve maintainability&lt;/li&gt;
&lt;li&gt;Speed up development&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Makes a Component Truly Reusable?
&lt;/h2&gt;

&lt;p&gt;A reusable component is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generic → Works in multiple contexts&lt;/li&gt;
&lt;li&gt;Composable → Can be combined with other components&lt;/li&gt;
&lt;li&gt;Configurable → Controlled via props&lt;/li&gt;
&lt;li&gt;Decoupled → Not tied to specific business logic
Bad example:
A UserDashboardCard hardcoded for one screen
Good example:
A Card component configurable via props&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Core Philosophy&lt;br&gt;
“Components should do one thing, and do it well.”&lt;br&gt;
Focus on:&lt;br&gt;
Separation of concerns&lt;br&gt;
Clear interfaces (props)&lt;br&gt;
Minimal assumptions&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reusable components reduce duplication and improve maintainability&lt;/li&gt;
&lt;li&gt;Keep components small, focused, and composable&lt;/li&gt;
&lt;li&gt;Use props for flexibility, not hardcoded values&lt;/li&gt;
&lt;li&gt;Separate UI from business logic&lt;/li&gt;
&lt;li&gt;Follow consistent folder and naming conventions&lt;/li&gt;
&lt;li&gt;Avoid premature abstraction-refactor when patterns emerge&lt;/li&gt;
&lt;li&gt;Design systems and shared libraries scale teams effectively&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Index
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;What Are Reusable Components?&lt;/li&gt;
&lt;li&gt;Component Design Principles&lt;/li&gt;
&lt;li&gt;Types of Reusable Components&lt;/li&gt;
&lt;li&gt;Structuring Your Component Folder&lt;/li&gt;
&lt;li&gt;Props and Configuration&lt;/li&gt;
&lt;li&gt;Composition vs Inheritance&lt;/li&gt;
&lt;li&gt;Styling Strategies&lt;/li&gt;
&lt;li&gt;Handling State and Logic&lt;/li&gt;
&lt;li&gt;Building a Design System&lt;/li&gt;
&lt;li&gt;Common Mistakes to Avoid&lt;/li&gt;
&lt;li&gt;Why This Approach Works&lt;/li&gt;
&lt;li&gt;Watch Out For&lt;/li&gt;
&lt;li&gt;Next Steps You Can Take&lt;/li&gt;
&lt;li&gt;Interesting Facts&lt;/li&gt;
&lt;li&gt;FAQ&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;As React applications grow, managing UI becomes increasingly complex. Without a reusable component strategy, developers often duplicate UI patterns, leading to inconsistent designs and bloated codebases.&lt;br&gt;
Reusable components solve this by encapsulating UI and behavior into modular building blocks. When done correctly, they create a system where features can be built faster and maintained more easily.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Simplicity is prerequisite for reliability.” - Edsger W. Dijkstra&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  2. What Are Reusable Components?
&lt;/h2&gt;

&lt;p&gt;Reusable components are UI elements that can be used across multiple parts of an application without modification.&lt;br&gt;
Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buttons&lt;/li&gt;
&lt;li&gt;Inputs&lt;/li&gt;
&lt;li&gt;Modals&lt;/li&gt;
&lt;li&gt;Cards&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Tables&lt;br&gt;
They are not tied to:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Specific pages&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Business logic&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hardcoded data&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  3. Component Design Principles
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Single Responsibility&lt;/strong&gt;&lt;br&gt;
Each component should handle one concern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Reusability Over Specificity&lt;/strong&gt;&lt;br&gt;
Avoid embedding business logic inside UI components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Predictable API&lt;/strong&gt;&lt;br&gt;
Props should be clear, consistent, and minimal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Composition First&lt;/strong&gt;&lt;br&gt;
Build complex UI by combining smaller components.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Types of Reusable Components
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Presentational Components&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Focus only on UI&lt;/li&gt;
&lt;li&gt;No business logic
Example:
Button, Card, Avatar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Container Components&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handle data and logic&lt;/li&gt;
&lt;li&gt;Pass data to presentational components&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Layout Components&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define page structure
Example:
Grid, Container, Sidebar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Compound Components&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple components working together
Example:
Tabs, Dropdown, Accordion&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“Duplication is the root of all evil in software.” - Don’t Repeat Yourself principle&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  5. Structuring Your Component Folder
&lt;/h2&gt;

&lt;p&gt;A scalable structure looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;components/
  ui/
    Button/
      Button.jsx
      Button.css
      index.js
  forms/
    Input/
    Select/
  layout/
    Container/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key idea:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group by feature or type, not by file type&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Props and Configuration
&lt;/h2&gt;

&lt;p&gt;Props are the backbone of reusability.&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;primary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`btn btn-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"secondary"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Cancel&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Avoid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hardcoding values&lt;/li&gt;
&lt;li&gt;Passing too many unrelated props&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“Any fool can write code that a computer can understand. Good programmers write code that humans can understand.” - Martin Fowler&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  7. Composition vs Inheritance
&lt;/h2&gt;

&lt;p&gt;React favors composition over inheritance.&lt;br&gt;
Bad approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extending components like classes
Good approach:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Title&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Content&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This keeps components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flexible&lt;/li&gt;
&lt;li&gt;Readable&lt;/li&gt;
&lt;li&gt;Easy to extend&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  8. Styling Strategies
&lt;/h2&gt;

&lt;p&gt;Consistency in styling is critical.&lt;br&gt;
Options:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. CSS Modules&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scoped styles&lt;/li&gt;
&lt;li&gt;Prevent conflicts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Tailwind CSS&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Utility-first&lt;/li&gt;
&lt;li&gt;Fast development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Styled Components&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dynamic styling&lt;/li&gt;
&lt;li&gt;Component-level styles&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Choose one approach and stay consistent&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  9. Handling State and Logic
&lt;/h2&gt;

&lt;p&gt;Reusable components should avoid heavy logic.&lt;br&gt;
Good pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep logic outside&lt;/li&gt;
&lt;li&gt;Pass data via props
Bad:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tightly coupled&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserCard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserCard&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  10. Building a Design System
&lt;/h2&gt;

&lt;p&gt;At scale, reusable components evolve into a design system.&lt;br&gt;
A design system includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI components&lt;/li&gt;
&lt;li&gt;Design tokens (colors, spacing, typography)&lt;/li&gt;
&lt;li&gt;Usage guidelines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consistency across teams&lt;/li&gt;
&lt;li&gt;Faster onboarding&lt;/li&gt;
&lt;li&gt;Easier scaling
## 11. Common Mistakes to Avoid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Over-abstracting too early&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Don’t generalize before patterns emerge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tight coupling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Avoid linking components to specific APIs or data sources.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prop explosion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Too many props = hard to use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ignoring accessibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reusable components must support:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keyboard navigation&lt;/li&gt;
&lt;li&gt;ARIA attributes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  12. Why This Approach Works
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reduces duplication&lt;/li&gt;
&lt;li&gt;Improves readability&lt;/li&gt;
&lt;li&gt;Encourages modular thinking&lt;/li&gt;
&lt;li&gt;Makes testing easier&lt;/li&gt;
&lt;li&gt;Scales across teams and projects&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“The best way to build complex systems is to assemble them from simple, well-defined parts.” - David Parnas&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Watch Out For&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Over-engineering simple components&lt;/li&gt;
&lt;li&gt;Creating unnecessary abstractions&lt;/li&gt;
&lt;li&gt;Mixing business logic with UI&lt;/li&gt;
&lt;li&gt;Inconsistent naming conventions&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Next Steps You Can Take&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Refactor duplicated UI into shared components&lt;/li&gt;
&lt;li&gt;Introduce a ui/ component library&lt;/li&gt;
&lt;li&gt;Document component usage&lt;/li&gt;
&lt;li&gt;Add Storybook for visual testing&lt;/li&gt;
&lt;li&gt;Enforce design consistency with linting&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Interesting Facts&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Most large-scale React apps rely heavily on internal component libraries source&lt;/li&gt;
&lt;li&gt;Design systems can reduce development time significantly source&lt;/li&gt;
&lt;li&gt;Component-driven development is a standard in modern frontend engineering source&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  16. FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. How small should a component be?&lt;/strong&gt;&lt;br&gt;
Small enough to do one job, but not so small that it becomes meaningless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Should every component be reusable?&lt;/strong&gt;&lt;br&gt;
No. Only abstract when reuse is needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. How do I manage shared components?&lt;/strong&gt;&lt;br&gt;
Use a centralized components or ui directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Is Tailwind better for reusable components?&lt;/strong&gt;&lt;br&gt;
It depends-great for speed, but consistency depends on discipline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Can this scale for large apps?&lt;/strong&gt;&lt;br&gt;
Yes. This is exactly how large React apps are structured.&lt;/p&gt;

&lt;h2&gt;
  
  
  17. Conclusion
&lt;/h2&gt;

&lt;p&gt;Building reusable UI components in React is not just about avoiding duplication-it’s about creating a scalable, maintainable architecture.&lt;br&gt;
When done right, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cleaner code&lt;/li&gt;
&lt;li&gt;Faster development&lt;/li&gt;
&lt;li&gt;Consistent UI&lt;/li&gt;
&lt;li&gt;Easier scaling
If your goal is to build production-grade applications, investing in a strong component strategy isn’t optional-it’s foundational.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;About the Author:&lt;em&gt;Ankit is a full-stack developer at &lt;a href="https://www.addwebsolution.com/" rel="noopener noreferrer"&gt;AddWebSolution&lt;/a&gt; and AI enthusiast who crafts intelligent web solutions with PHP, Laravel, and modern frontend tools.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>frontend</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Handling API Errors &amp; Loading States in React (Clean UX Approach)</title>
      <dc:creator>Abodh Kumar</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:40:37 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/handling-api-errors-loading-states-in-react-clean-ux-approach-54o7</link>
      <guid>https://dev.to/addwebsolutionpvtltd/handling-api-errors-loading-states-in-react-clean-ux-approach-54o7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A user interface is like a joke. If you have to explain it, it is not that good. - Martin LeBlanc&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In any real-world React application, you will spend far more time handling what goes wrong than celebrating what goes right. Network failures, slow responses, timeouts, and server errors are not edge cases - they are everyday realities. How you communicate these states to your users defines the quality of your application's user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaway
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Always model three states - loading, error, and success - for every API call, no exceptions.&lt;/li&gt;
&lt;li&gt;Provide meaningful, context-specific error messages - never expose raw error objects to users.&lt;/li&gt;
&lt;li&gt;Use skeleton screens instead of spinners where possible to reduce perceived load time.&lt;/li&gt;
&lt;li&gt;Implement retry logic with exponential backoff for transient network failures.&lt;/li&gt;
&lt;li&gt;Centralize error handling with custom hooks to avoid repeating logic across components.&lt;/li&gt;
&lt;li&gt;Distinguish between network errors, HTTP errors, and validation errors - each requires different UX.&lt;/li&gt;
&lt;li&gt;Always cancel in-flight requests on component unmount to prevent memory leaks and ghost state updates&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Understanding the Three API States&lt;/li&gt;
&lt;li&gt;Handling Loading States - Spinners, Skeletons &amp;amp; Beyond&lt;/li&gt;
&lt;li&gt;Handling API Errors - Graceful Degradation&lt;/li&gt;
&lt;li&gt;Building a Custom useFetch Hook&lt;/li&gt;
&lt;li&gt;Advanced Techniques&lt;/li&gt;
&lt;li&gt;Stats &amp;amp; Interesting Facts&lt;/li&gt;
&lt;li&gt;FAQ&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Modern React applications are almost always data-driven, relying on REST APIs, GraphQL endpoints, or third-party services to power their interfaces. Whether you are building a dashboard that fetches analytics data, a shopping app that loads product listings, or a social platform that streams user posts, every API interaction introduces three inevitable possibilities: the data is loading, the data arrived successfully, or something went wrong.&lt;br&gt;
The difference between a great app and a frustrating one often comes down to how gracefully these states are handled. Users who encounter a blank screen during loading, a cryptic "undefined is not an object" message when an error occurs, or an interface that silently fails will lose trust in your product instantly.&lt;br&gt;
This article walks you through a complete, production-ready strategy for handling API errors and loading states in React - covering everything from basic useState patterns and skeleton screens to custom hooks, Axios interceptors, and React Query integration.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Understanding the Three API States
&lt;/h2&gt;

&lt;p&gt;Before writing a single line of code, it is essential to model your data fetching around three distinct states. Every API call in your application should reflect exactly one of these states at any given moment:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.1 The Loading State&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The loading state exists from the moment you initiate a request until a response (successful or otherwise) is received. Failing to model this state means users see stale data, empty screens, or worse - an interface that appears broken. A loading state should always trigger visible feedback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.2 The Success State&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the API returns the expected data with a successful status code (typically 2xx), the UI transitions to the success state. This is where you render the actual content. Even here, you must consider edge cases like empty arrays, null values, or partially missing fields.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.3 The Error State&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Any response outside the 2xx range, a network timeout, or a connection failure triggers the error state. This is the most neglected of the three, yet it is critical. Users need to know what failed and, where possible, what they can do about it.&lt;br&gt;
A foundational pattern using React's useState hook illustrates this model clearly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;
       &lt;span class="p"&gt;});&lt;/span&gt;
       &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server error: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AbortError&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// cleanup on unmount&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LoadingSkeleton&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ErrorMessage&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProfileCard&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Design is not just what it looks like and feels like. Design is how it works. - Steve Jobs&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Handling Loading States - Spinners, Skeletons &amp;amp; Beyond
&lt;/h2&gt;

&lt;p&gt;Not all loading indicators are equal. The choice of loading UI dramatically affects how users perceive your application's speed. Research in UX psychology consistently shows that users tolerate waiting much better when they receive structured, meaningful feedback.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.1 Spinners - When to Use Them&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Spinners (circular progress indicators) are appropriate for short, unpredictable waits - typically actions like form submissions, delete confirmations, or quick one-off requests. They signal activity without implying structure about what is loading. Use them sparingly; overuse of spinners across an interface feels chaotic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Spinner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;spinner-wrapper&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;aria&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Loading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;spinner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sr-only&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.2 Skeleton Screens - The Gold Standard&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Skeleton screens render placeholder shapes that mimic the final layout of your content before data arrives. They dramatically reduce perceived loading time by orienting the user to the page structure immediately. Facebook, LinkedIn, YouTube, and Slack all rely heavily on skeleton screens for their primary content feeds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductCardSkeleton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;card skeleton&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;skeleton-image&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;skeleton-line wide&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;skeleton-line medium&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;skeleton-line narrow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// CSS for pulsing animation&lt;/span&gt;
&lt;span class="cm"&gt;/*
.skeleton-line {
 height: 16px;
 background: linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%);
 background-size: 200% 100%;
 animation: shimmer 1.5s infinite;
 border-radius: 4px;
 margin-bottom: 10px;
}
@keyframes shimmer {
 0% { background-position: 200% 0; }
 100% { background-position: -200% 0; }
}
*/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3.3 Progressive Loading&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For data-heavy pages, consider progressive loading: render critical above-the-fold content first, then fetch secondary sections lazily. This approach, combined with React's Suspense boundaries, delivers a fast initial paint even on slower connections.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lazy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HeavyChartSection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./HeavyChartSection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Dashboard&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HeroMetrics&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Loads immediately */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ChartSkeleton&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HeavyChartSection&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Loads lazily */&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Handling API Errors - Graceful Degradation
&lt;/h2&gt;

&lt;p&gt;Error handling is where most React applications fall short. A robust error handling strategy distinguishes between different failure modes and responds to each appropriately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.1 Classifying Errors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not all errors are the same. Build your error handling logic around these distinct categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network Errors - The request never reached the server (offline, DNS failure, CORS). The user needs to check their connection.&lt;/li&gt;
&lt;li&gt;HTTP 4xx Errors - Client-side errors (401 Unauthorized, 403 Forbidden, 404 Not Found, 422 Validation Failed). Each requires a specific response.&lt;/li&gt;
&lt;li&gt;HTTP 5xx Errors - Server-side errors (500 Internal Server Error, 503 Service Unavailable). The user should be reassured it is not their fault.&lt;/li&gt;
&lt;li&gt;Timeout Errors - The request took too long. Often appropriate to offer a retry.&lt;/li&gt;
&lt;li&gt;Validation Errors - The server rejected the data (422). Field-level feedback is critical here.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;classifyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;network&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No internet connection. Please check your network.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Your session has expired. Please log in again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;forbidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You do not have permission to view this resource.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;notfound&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;The requested resource could not be found.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;422&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;validation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Please check your input and try again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Our servers are having trouble. Please try again shortly.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something went wrong. Please try again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.2 User-Friendly Error Components&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your error component should always communicate what went wrong, ideally offer a recovery action, and never expose raw error objects or stack traces to end users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ErrorMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onRetry&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;icons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;network&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;📡&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🔐&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;forbidden&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🚫&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;notfound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🔍&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;⚠️&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;❓&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error-container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error-icon&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h3&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error-title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Something&lt;/span&gt; &lt;span class="nx"&gt;went&lt;/span&gt; &lt;span class="nx"&gt;wrong&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h3&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;error-message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onRetry&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onRetry&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;retry-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="nx"&gt;Try&lt;/span&gt; &lt;span class="nx"&gt;Again&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="p"&gt;)}&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4.3 React Error Boundaries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For rendering errors (not async errors), React's Error Boundary class component catches exceptions thrown during render and prevents the entire tree from unmounting. Pair them with your async error states for comprehensive coverage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ErrorBoundary&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nf"&gt;getDerivedStateFromError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="nf"&gt;componentDidCatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ErrorBoundary caught:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;info&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="c1"&gt;// Send to error tracking service (e.g., Sentry)&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hasError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fallback&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DefaultErrorFallback&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ErrorBoundary&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Something&lt;/span&gt; &lt;span class="nx"&gt;went&lt;/span&gt; &lt;span class="nx"&gt;wrong&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UserDashboard&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ErrorBoundary&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Building a Custom useFetch Hook
&lt;/h2&gt;

&lt;p&gt;Repeating loading/error/success logic in every component is a maintenance nightmare. A custom hook centralizes this logic, ensures consistency, and makes components dramatically cleaner.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.1 The useFetch Hook&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;abortRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// Abort any in-flight request&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;abortRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;abortRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AbortController&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nx"&gt;abortRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;});&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;errData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({}));&lt;/span&gt;
       &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;errData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;});&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
     &lt;span class="nf"&gt;setData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AbortError&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nf"&gt;setLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

 &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nf"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;abortRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;fetchData&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;refetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fetchData&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// Usage in a component&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refetch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductListSkeleton&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ErrorMessage&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;onRetry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;refetch&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;EmptyState&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductCard&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5.2 Adding Retry Logic with Exponential Backoff&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Transient network errors often resolve themselves within seconds. Implementing automatic retry with exponential backoff dramatically improves resilience in unstable network environments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchWithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// Retry on 5xx with exponential backoff: 1s, 2s, 4s&lt;/span&gt;
       &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
       &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;maxRetries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;There are only two types of API calls: those that have failed, and those that have not failed yet. Plan for both. - Unknown, React Community Proverb&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  6. Advanced Techniques
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;6.1 Using React Query for Robust Data Fetching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For production applications, React Query (now TanStack Query) provides battle-tested data fetching with built-in caching, background refetching, stale-while-revalidate patterns, and automatic retry - all without writing custom hooks from scratch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useQuery&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tanstack/react-query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refetch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
   &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to fetch user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;}),&lt;/span&gt;
   &lt;span class="na"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="na"&gt;retryDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;attemptIndex&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="nx"&gt;attemptIndex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="na"&gt;staleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 5 minutes&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;

 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;LoadingSkeleton&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ErrorMessage&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;onRetry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;refetch&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProfileCard&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6.2 Axios Interceptors for Global Error Handling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When using Axios, interceptors allow you to handle common error scenarios globally - such as redirecting to login on 401, showing a toast notification on 5xx errors, or refreshing tokens transparently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;toast&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-hot-toast&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Server error. Our team has been notified.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Network error. Please check your connection.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6.3 Optimistic Updates&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For actions like liking a post or toggling a bookmark, optimistic updates apply the change to the UI immediately before the server confirms it. If the request fails, the UI rolls back. This technique makes interfaces feel instantaneous.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;instantaneous&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;LikeButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialLiked&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;liked&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLiked&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialLiked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;likeCount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLikeCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleLike&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// Optimistically update UI&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wasLiked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;liked&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nf"&gt;setLiked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;wasLiked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nf"&gt;setLikeCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;wasLiked&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/posts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/like`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;liked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;wasLiked&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nf"&gt;setLiked&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wasLiked&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nf"&gt;setLikeCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;wasLiked&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nx"&gt;toast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Could not update like. Please try again.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleLike&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;liked&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;❤️&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🤍&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;likeCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6.4 Global Loading Indicator&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For applications with many simultaneous API calls, a global loading bar (like the thin progress bar at the top of GitHub's pages) provides ambient feedback without disrupting the UI. Libraries like NProgress integrate cleanly with React.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;NProgress&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nprogress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nprogress/nprogress.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// In your Axios instance&lt;/span&gt;
&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NProgress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;interceptors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NProgress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NProgress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Stats &amp;amp; Interesting Facts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;According to Google research, 53% of mobile users abandon a site that takes longer than 3 seconds to load. Effective loading state UX directly impacts retention.Source: &lt;a href="https://www.thinkwithgoogle.com/marketing-strategies/app-and-mobile/mobile-page-speed-new-industry-benchmarks/" rel="noopener noreferrer"&gt;https://www.thinkwithgoogle.com/marketing-strategies/app-and-mobile/mobile-page-speed-new-industry-benchmarks/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;A study by the Nielsen Norman Group found that users perceive skeleton screens as significantly faster than equivalent spinner-based loading experiences, even when actual load times are identical.
Source: &lt;a href="https://www.nngroup.com/articles/progress-indicators/" rel="noopener noreferrer"&gt;https://www.nngroup.com/articles/progress-indicators/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;According to State of JS surveys, React Query and SWR are now used by over 40% of React developers who fetch remote data, largely because of their built-in loading and error state management.
Source: &lt;a href="https://stateofjs.com" rel="noopener noreferrer"&gt;https://stateofjs.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Research from Amazon found that every 100ms increase in page load time reduced sales by 1%. Loading state UX is not just a UX concern - it is a business metric.Source: &lt;a href="https://www.fastcompany.com/1825005/how-one-second-could-cost-amazon-16-billion-sales" rel="noopener noreferrer"&gt;https://www.fastcompany.com/1825005/how-one-second-could-cost-amazon-16-billion-sales&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The HTTP Archive reports that the median mobile page makes over 70 separate HTTP requests. Without proper loading state management per request, the cumulative UX degradation is significant.
Source: &lt;a href="https://httparchive.org/reports/state-of-the-web" rel="noopener noreferrer"&gt;https://httparchive.org/reports/state-of-the-web&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sentry's 2023 developer survey found that unhandled promise rejections from API calls are among the top three sources of JavaScript errors in production React applications.
Source: &lt;a href="https://sentry.io/resources/developer-survey/" rel="noopener noreferrer"&gt;https://sentry.io/resources/developer-survey/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;According to UX research by Toptal, 88% of online consumers are less likely to return to a site after a bad user experience - silent API failures with no user feedback are a primary driver of this statistic. Source: &lt;a href="https://www.toptal.com/designers/ux/ux-statistics-insights-infographic" rel="noopener noreferrer"&gt;https://www.toptal.com/designers/ux/ux-statistics-insights-infographic&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Good error messages are a form of documentation. They tell the user what went wrong, why it matters, and what to do about it. - Jakob Nielsen, Nielsen Norman Group&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  8. FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Why should I model loading and error states separately rather than using a single "status" string?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: Using separate boolean flags (loading, error) is simpler for most cases and avoids state machine complexity. However, a single status enum ("idle" | "loading" | "success" | "error") is a valid and arguably more explicit pattern, especially as state logic grows. The critical principle is that all three states must always be represented - never leave any one implicit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. What is the difference between a loading skeleton and a placeholder?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: A skeleton screen mimics the actual layout and shape of the content that will appear, giving users a structural preview. A generic placeholder (e.g., a grey box or spinner) gives no structural information. Skeleton screens outperform generic placeholders in perceived performance because they orient the user before the content arrives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Should I use React Query or write custom hooks for data fetching?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: For simple, one-off fetch calls, a custom hook is sufficient and avoids adding a dependency. For production apps with complex caching, pagination, background syncing, or optimistic updates, React Query (TanStack Query) or SWR are strongly recommended. They solve problems that custom hooks inevitably re-implement poorly over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. How should I handle validation errors returned from an API (HTTP 422)?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: Parse the error response body to extract field-level validation messages and display them directly adjacent to the relevant form fields. Never show a generic error message for validation failures - users need to know exactly which field failed and why. React Hook Form and Formik both integrate well with server-side validation error patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. What should I do if a component unmounts before an API call completes?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: Always use an AbortController to cancel in-flight requests in your useEffect cleanup function. Without this, the component will attempt to update state after unmounting, causing the classic React warning about updating state on an unmounted component, and potentially causing memory leaks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Is it ever appropriate to silently swallow API errors without user feedback?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: Only for non-critical background operations where failure has no impact on the user's task - for example, firing an analytics event or pre-fetching secondary content. For any operation the user explicitly triggered or depends on, always provide feedback. Silent failures destroy user trust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. How should I handle errors in React Server Components (Next.js App Router)?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: In Next.js App Router, use the error.tsx file convention to define error boundaries per route segment. For server-side data fetching, use try/catch in async server components and return appropriate fallback UI. The notFound() function handles 404 cases, while the error.tsx boundary handles unexpected errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. How do I test loading and error states in React components?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ans: Use Mock Service Worker (MSW) to intercept API calls in tests without mocking fetch directly. MSW allows you to simulate slow responses (for loading state tests), 4xx/5xx error responses (for error state tests), and successful responses - giving you realistic integration test coverage of all three states.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Conclusion.
&lt;/h2&gt;

&lt;p&gt;Handling API errors and loading states cleanly is not a nice-to-have - it is the baseline of professional React development. Every layer described in this article serves a specific purpose in a complete, user-respecting data fetching strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Explicit state modeling ensures loading, error, and success are always accounted for - never left implicit.&lt;/li&gt;
&lt;li&gt;Skeleton screens reduce perceived loading time by orienting users to the page structure before content arrives.&lt;/li&gt;
&lt;li&gt;Error classification provides users with actionable, context-specific messages rather than generic failure notices.&lt;/li&gt;
&lt;li&gt;Custom hooks centralize data fetching logic and eliminate repetition across components.&lt;/li&gt;
&lt;li&gt;Retry logic and request cancellation add resilience and prevent memory leaks in production environments.&lt;/li&gt;
&lt;li&gt;React Query and Axios interceptors provide production-grade abstractions for complex data fetching scenarios.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;React provides all the primitives you need. The responsibility lies with developers to compose them thoughtfully, test all three states rigorously, and treat error handling as a first-class feature rather than an afterthought.&lt;/p&gt;

&lt;p&gt;The best UX is invisible - a user who never sees a cryptic error message, never stares at a blank screen, and always feels in control is experiencing excellent error handling. That invisibility is the mark of a truly polished React application.&lt;/p&gt;

&lt;p&gt;About the Author:&lt;em&gt;Abodh is a PHP and Laravel Developer at &lt;a href="https://www.addwebsolution.com/our-capabilities/laravel-development-agency" rel="noopener noreferrer"&gt;AddWeb Solution&lt;/a&gt;, skilled in MySQL, REST APIs, JavaScript, Git, and Docker for building robust web applications.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Data Fetching Strategies in Next.js - SSR, SSG, ISR, and RSC</title>
      <dc:creator>Vatsal Acharya</dc:creator>
      <pubDate>Mon, 20 Apr 2026 10:46:48 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/data-fetching-strategies-in-nextjs-ssr-ssg-isr-and-rsc-5a2p</link>
      <guid>https://dev.to/addwebsolutionpvtltd/data-fetching-strategies-in-nextjs-ssr-ssg-isr-and-rsc-5a2p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“Performance is not a feature - it’s the foundation. Next.js gives you the tools to build it right.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js provides multiple data fetching strategies depending on performance and freshness needs.&lt;/li&gt;
&lt;li&gt;SSR fetches data on every request and is best for dynamic content.&lt;/li&gt;
&lt;li&gt;SSG generates pages at build time, making it extremely fast.&lt;/li&gt;
&lt;li&gt;ISR updates static pages after deployment without full rebuilds.&lt;/li&gt;
&lt;li&gt;RSC (React Server Components) is the modern default in App Router.&lt;/li&gt;
&lt;li&gt;Caching in Next.js is controlled using fetch options like cache and revalidate.&lt;/li&gt;
&lt;li&gt;Real-world apps use a combination of SSR, SSG, ISR, and RSC - not just one.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Index
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why data fetching strategy matters&lt;/li&gt;
&lt;li&gt;Quick comparison table&lt;/li&gt;
&lt;li&gt;Server-Side Rendering (SSR)&lt;/li&gt;
&lt;li&gt;Static Site Generation (SSG)

&lt;ul&gt;
&lt;li&gt;4b Incremental Static Regeneration (ISR)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;React Server Components (RSC)&lt;/li&gt;
&lt;li&gt;App Router vs Pages Router&lt;/li&gt;
&lt;li&gt;Caching in App Router&lt;/li&gt;
&lt;li&gt;Real-world app architecture&lt;/li&gt;
&lt;li&gt;Common mistakes &lt;/li&gt;
&lt;li&gt;Stats&lt;/li&gt;
&lt;li&gt;Interesting Facts&lt;/li&gt;
&lt;li&gt;FAQs&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why does data fetching strategy actually matter?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a classic React app, data fetching is simple: useEffect fires after the component mounts, you hit an API, and update state.&lt;/p&gt;

&lt;p&gt;Easy to reason about - but there’s a problem:&lt;br&gt;
The user sees a blank or loading screen until JavaScript executes and the fetch completes.&lt;/p&gt;

&lt;p&gt;For small apps, this is fine.&lt;br&gt;
At scale, it becomes a serious UX and SEO issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How Next.js Solves This&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Modern web development is not about fetching data - it’s about fetching it intelligently.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next.js lets you decide:&lt;br&gt;
Where and when data should be fetched&lt;/p&gt;

&lt;p&gt;Each strategy is a tradeoff between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Freshness&lt;/li&gt;
&lt;li&gt;Performance&lt;/li&gt;
&lt;li&gt;Server cost&lt;/li&gt;
&lt;li&gt;Complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mental Model&lt;/strong&gt;&lt;br&gt;
Think of it as a spectrum:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fully Dynamic → SSR&lt;/li&gt;
&lt;li&gt;Fully Static → SSG&lt;/li&gt;
&lt;li&gt;In Between → ISR + RSC&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Quick Reference - All Strategies at a Glance
&lt;/h2&gt;

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

&lt;p&gt;&lt;strong&gt;Important Note&lt;/strong&gt;&lt;br&gt;
RSC is not a replacement for SSR, SSG, or ISR.&lt;br&gt;
It’s a new rendering model that can behave like any of them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Strategy 01
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Server-Side Rendering (SSR)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a request comes in:&lt;br&gt;
Next.js fetches data on the server before sending HTML&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pages Router Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getServerSideProps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;
 &lt;span class="nx"&gt;oop&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;permanent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.example.com/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Behavior&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs on every request&lt;/li&gt;
&lt;li&gt;Full HTML is returned&lt;/li&gt;
&lt;li&gt;No loading state on client&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to Use SSR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User-specific content&lt;/li&gt;
&lt;li&gt;Authentication logic&lt;/li&gt;
&lt;li&gt;Real-time data&lt;/li&gt;
&lt;li&gt;Accessing cookies/headers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When NOT to Use SSR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same data for all users&lt;/li&gt;
&lt;li&gt;Rarely changing content&lt;/li&gt;
&lt;li&gt;Performance-sensitive pages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;&lt;br&gt;
SSR adds latency - your API speed = your page speed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Strategy 02
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Static Site Generation (SSG)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SSG generates pages at build time and serves static HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dynamic Routes Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getStaticPaths&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
     &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;})),&lt;/span&gt;
   &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to Use SSG&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blogs&lt;/li&gt;
&lt;li&gt;Docs&lt;/li&gt;
&lt;li&gt;Landing pages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tradeoff&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Very fast&lt;/li&gt;
&lt;li&gt;Not real-time&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Strategy 03
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Incremental Static Regeneration (ISR)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ISR = SSG + automatic updates&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How It Works&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serve static page&lt;/li&gt;
&lt;li&gt;After interval → regenerate in background&lt;/li&gt;
&lt;li&gt;Next request gets fresh page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getStaticProps&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="na"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Insight&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Revalidation only happens when a request comes&lt;br&gt;
 No traffic = no update&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On-Demand ISR (Better Approach)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-webhook-secret&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WEBHOOK_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;

 &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/products/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;revalidated&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to Use ISR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product pages&lt;/li&gt;
&lt;li&gt;News&lt;/li&gt;
&lt;li&gt;CMS-driven content&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Strategy 04 (Modern Default)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;React Server Components (RSC)&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“RSC changes the game: less JavaScript, better performance, cleaner architecture.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In App Router:&lt;br&gt;
 Components run on the server by default&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.example.com/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;})&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

 &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="p"&gt;))}&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Controlling Behavior&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;fetch(url, { cache: 'force-cache' })   // SSG&lt;br&gt;
fetch(url, { cache: 'no-store' })      // SSR&lt;br&gt;
fetch(url, { next: { revalidate: 60 } }) // ISR&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Benefits&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No useEffect&lt;/li&gt;
&lt;li&gt;No client-side fetching&lt;/li&gt;
&lt;li&gt;Smaller JS bundle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important Rule&lt;/strong&gt;&lt;br&gt;
 Client components cannot import server components&lt;br&gt;
 Data flows server → client&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;App Router vs Pages Router&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pages Router&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;getServerSideProps / getStaticProps&lt;/li&gt;
&lt;li&gt;Page-level fetching&lt;/li&gt;
&lt;li&gt;Older system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;App Router&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Async components&lt;/li&gt;
&lt;li&gt;Component-level fetching&lt;/li&gt;
&lt;li&gt;Server-first approach&lt;/li&gt;
&lt;li&gt;Supports streaming
App Router is the future&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deep Dive
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Caching in App Router&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are 4 cache layers:&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Common Issue&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“Why is my data not updating?”&lt;br&gt;
 Usually due to Router cache&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;router.refresh()&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Revalidation Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;import { revalidateTag } from 'next/cache'&lt;br&gt;
revalidateTag('products')&lt;/p&gt;

&lt;h2&gt;
  
  
  Production Patterns
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Real-World Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E-commerce&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Homepage → SSG&lt;/li&gt;
&lt;li&gt;Product Page → ISR&lt;/li&gt;
&lt;li&gt;Cart → SSR&lt;/li&gt;
&lt;li&gt;Layout → RSC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;SaaS&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Landing → SSG&lt;/li&gt;
&lt;li&gt;Dashboard → SSR&lt;/li&gt;
&lt;li&gt;Docs → ISR&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real apps use combination of strategies&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The best data fetching strategy is not SSR or SSG - it’s choosing the right one at the right time.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Anti-Patterns
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Common Mistakes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Using SSR everywhere&lt;/strong&gt;&lt;br&gt;
Expensive + slow&lt;br&gt;
Use ISR instead&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Ignoring server/client boundary&lt;/strong&gt;&lt;br&gt;
Causes runtime errors&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. No error handling&lt;/strong&gt;&lt;br&gt;
Can crash entire page&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Fetching in client unnecessarily&lt;/strong&gt;&lt;br&gt;
Adds JS + loading delay&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Misunderstanding ISR timing&lt;/strong&gt;&lt;br&gt;
Low traffic = stale data&lt;/p&gt;

&lt;h2&gt;
  
  
  Stats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;According to Vercel, static generation can significantly improve performance by serving pre-rendered pages from a CDN, reducing server computation.Source: &lt;a href="https://nextjs.org/docs/pages/building-your-application/rendering/static-site-generation" rel="noopener noreferrer"&gt;https://nextjs.org/docs/pages/building-your-application/rendering/static-site-generation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Next.js has millions of weekly downloads and is one of the most widely used React frameworks.Source: &lt;a href="https://www.npmjs.com/package/next" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/next&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Next.js is used by companies like Netflix, TikTok, and Hulu for high-performance applications.Source: &lt;a href="https://nextjs.org/showcase" rel="noopener noreferrer"&gt;https://nextjs.org/showcase&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Interesting Facts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js was created and is maintained by Vercel.Source: &lt;a href="https://nextjs.org" rel="noopener noreferrer"&gt;https://nextjs.org&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Incremental Static Regeneration (ISR) was introduced in Next.js 9.5.Source: &lt;a href="https://nextjs.org/blog/next-9-5" rel="noopener noreferrer"&gt;https://nextjs.org/blog/next-9-5&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;React Server Components (RSC) became stable with the App Router in Next.js 13.Source: &lt;a href="https://nextjs.org/blog/next-13" rel="noopener noreferrer"&gt;https://nextjs.org/blog/next-13&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Next.js enables full-stack development by combining frontend and backend capabilities in a single framework.Source: &lt;a href="https://nextjs.org/docs" rel="noopener noreferrer"&gt;https://nextjs.org/docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Is SSR outdated?&lt;/strong&gt;&lt;br&gt;
No - it’s still essential for dynamic and user-specific data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Is RSC replacing SSR/SSG?&lt;/strong&gt;&lt;br&gt;
No - it enhances them and gives more flexibility.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Can we mix all strategies?&lt;/strong&gt;&lt;br&gt;
Yes - and that’s the recommended approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Which is best for SEO?&lt;/strong&gt;&lt;br&gt;
SSG and SSR both are great for SEO.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Can ISR work with dynamic routes?&lt;/strong&gt;&lt;br&gt;
Yes&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Where to store API keys?&lt;/strong&gt;&lt;br&gt;
Server-side only&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. App Router or Pages Router?&lt;/strong&gt;&lt;br&gt;
New projects → App Router&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. How to avoid waterfall fetches?&lt;/strong&gt;&lt;br&gt;
Use RSC&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The Decision Tree&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ask:&lt;br&gt;
Does data change per user?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yes → SSR / RSC (no-store)
Does data rarely change?&lt;/li&gt;
&lt;li&gt;Yes → SSG
Does it update periodically?&lt;/li&gt;
&lt;li&gt;Yes → ISR
Using App Router?&lt;/li&gt;
&lt;li&gt;Use RSC + fetch control&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Insight
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use Server Components for data&lt;/li&gt;
&lt;li&gt;Use Client Components for interactivity&lt;/li&gt;
&lt;li&gt;Use ISR for scalability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Data fetching in Next.js is not about memorizing functions…&lt;br&gt;
It’s about choosing the right strategy at the right time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use SSR for dynamic data&lt;/li&gt;
&lt;li&gt;Use SSG for static content&lt;/li&gt;
&lt;li&gt;Use ISR for balance&lt;/li&gt;
&lt;li&gt;Use RSC for modern optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you understand this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your apps become faster&lt;/li&gt;
&lt;li&gt;Your architecture becomes scalable&lt;/li&gt;
&lt;li&gt;Your performance improves significantly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;About the Author:&lt;em&gt;Vatsal is a web developer at &lt;a href="https://www.addwebsolution.com/" rel="noopener noreferrer"&gt;AddWebSolution&lt;/a&gt;. Building web magic with Laravel, PHP, MySQL, Vue.js &amp;amp; more.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Docker Image with Multi-Stage Builds &amp; Docker Images Optimization</title>
      <dc:creator>Rajan Vavadia</dc:creator>
      <pubDate>Wed, 15 Apr 2026 08:24:49 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/docker-image-with-multi-stage-builds-docker-images-optimization-4379</link>
      <guid>https://dev.to/addwebsolutionpvtltd/docker-image-with-multi-stage-builds-docker-images-optimization-4379</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“Docker is not a virtualization technology. It is an application delivery technology.” - Mike Coleman (Docker)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Overview: Multi-Stage Build Strategy (Builder + Production)&lt;/li&gt;
&lt;li&gt;Stage 1: Builder - Compile Extensions, Install Tools &amp;amp; Dependencies&lt;/li&gt;
&lt;li&gt;Stage 2: Production - Minimal Runtime Image&lt;/li&gt;
&lt;li&gt;The .dockerignore File&lt;/li&gt;
&lt;li&gt;Practical Setup (Step-by-step)&lt;/li&gt;
&lt;li&gt;Quotes&lt;/li&gt;
&lt;li&gt;FAQs&lt;/li&gt;
&lt;li&gt;Key Takeaways&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;This document explains how to build a production-ready PHP 8.4-FPM Docker image using a multi-stage Dockerfile. The image ships with all the PHP extensions, CLI tools (Composer, WP-CLI, Drush, Drupal Console), and Node.js needed for WordPress and Drupal projects - while keeping the final image lean and secure.&lt;/p&gt;

&lt;p&gt;The approach uses a two-stage build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stage 1 (Builder): Compiles PHP extensions, installs development tools, and downloads CLI utilities.&lt;/li&gt;
&lt;li&gt;Stage 2 (Production): Starts from a clean php:8.4-fpm base, copies only the compiled artifacts and runtime libraries, applies security hardening, and runs as a non-root user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why multi-stage?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A single-stage Dockerfile that compiles extensions and installs build tools leaves behind compilers, header files, and package caches that bloat the image and increase the attack surface. Multi-stage builds solve this by discarding everything that is not needed at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Overview: Multi-Stage Build Strategy (Builder + Production)
&lt;/h2&gt;

&lt;p&gt;Stage 1 (Builder) starts from php:8.4-fpm and installs: - 20+ PHP extensions using install-php-extensions (handles build dependencies and cleanup internally). - Composer, WP-CLI, Drush, and Drupal Console. - Node.js 20.x for front-end tooling.&lt;/p&gt;

&lt;p&gt;Stage 2 (Production) starts from a fresh php:8.4-fpm and: - Copies compiled extension .so files and their .ini configs from the builder. - Copies CLI binaries (Composer, WP-CLI, Drush, Drupal Console, Node.js). - Installs only the runtime shared libraries needed by the extensions. - Applies OPcache tuning and PHP security settings. - Creates a non-root user (appuser) for PHP-FPM. - Add a health check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why separate stages?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller image: Build tools (gcc, make, autoconf, header files) are discarded. Only runtime libraries remain.&lt;/li&gt;
&lt;li&gt;Faster pulls and deploys: Less data to transfer across registries and hosts.&lt;/li&gt;
&lt;li&gt;Reduced attack surface: No compilers or dev packages in production.&lt;/li&gt;
&lt;li&gt;Clearer troubleshooting: Build failures are isolated to Stage 1; runtime issues are isolated to Stage 2.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Stage 1: Builder - Compile Extensions, Install Tools &amp;amp; Dependencies
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3.1 Base Image&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;FROM php:8.4-fpm AS builder&lt;br&gt;
We use the official php:8.4-fpm image (based on Debian 13 Trixie) as the builder base. This gives us the PHP source, phpize, and docker-php-ext-install out of the box.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.2 PHP Extension Installation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Single tool for all PHP extensions (handles build deps + cleanup internally)&lt;br&gt;
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/&lt;/p&gt;

&lt;p&gt;Install all PHP extensions in one layer&lt;br&gt;
RUN install-php-extensions \&lt;br&gt;
        bcmath \&lt;br&gt;
        bz2 \&lt;br&gt;
        calendar \&lt;br&gt;
        exif \&lt;br&gt;
        gd \&lt;br&gt;
        gmp \&lt;br&gt;
        imagick \&lt;br&gt;
        intl \&lt;br&gt;
        mailparse \&lt;br&gt;
        mongodb \&lt;br&gt;
        mysqli \&lt;br&gt;
        opcache \&lt;br&gt;
        pcntl \&lt;br&gt;
        pdo \&lt;br&gt;
        pdo_mysql \&lt;br&gt;
        pdo_pgsql \&lt;br&gt;
        soap \&lt;br&gt;
        sockets \&lt;br&gt;
        sodium \&lt;br&gt;
        xsl \&lt;br&gt;
        zip&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why install-php-extensions?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It automatically installs the OS-level build dependencies (e.g., libpng-dev, libicu-dev), compiles the extension, and removes the build dependencies - all in one step.&lt;/li&gt;
&lt;li&gt;It replaces the manual apt-get install  &amp;amp;&amp;amp; docker-php-ext-configure &amp;amp;&amp;amp; docker-php-ext-install &amp;amp;&amp;amp; apt-get purge pattern.&lt;/li&gt;
&lt;li&gt;It supports PECL extensions (like imagick, mongodb, mailparse) with the same syntax.&lt;/li&gt;
&lt;li&gt;We use COPY --from=mlocati/php-extension-installer to pull the binary directly from the tool’s official image, without adding another FROM stage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Extensions included and why:&lt;/strong&gt;&lt;/p&gt;

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

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

&lt;p&gt;&lt;strong&gt;3.3 CLI Tools Installation&lt;/strong&gt;&lt;br&gt;
Install Composer, WP-CLI, Drush, and Drupal Console in one layer&lt;br&gt;
RUN apt-get update &amp;amp;&amp;amp; apt-get install -y --no-install-recommends \&lt;br&gt;
        git \&lt;br&gt;
        unzip \&lt;br&gt;
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/* \&lt;br&gt;
      Composer&lt;br&gt;
    &amp;amp;&amp;amp; curl -sSL &lt;a href="https://getcomposer.org/installer" rel="noopener noreferrer"&gt;https://getcomposer.org/installer&lt;/a&gt; | php -- --install-dir=/usr/local/bin --filename=composer \&lt;br&gt;
      WP-CLI&lt;br&gt;
    &amp;amp;&amp;amp; curl -sSL &lt;a href="https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar" rel="noopener noreferrer"&gt;https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar&lt;/a&gt; -o /usr/local/bin/wp \&lt;br&gt;
    &amp;amp;&amp;amp; chmod +x /usr/local/bin/wp \&lt;br&gt;
      Drupal Console&lt;br&gt;
    &amp;amp;&amp;amp; curl -sSL &lt;a href="https://drupalconsole.com/installer" rel="noopener noreferrer"&gt;https://drupalconsole.com/installer&lt;/a&gt; -o /usr/local/bin/drupal \&lt;br&gt;
    &amp;amp;&amp;amp; chmod +x /usr/local/bin/drupal&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Composer: PHP dependency manager. Installed globally at /usr/local/bin/composer.&lt;/li&gt;
&lt;li&gt;WP-CLI: WordPress command-line interface for managing WordPress installations.&lt;/li&gt;
&lt;li&gt;Drupal Console: CLI tool for generating boilerplate code and interacting with Drupal.&lt;/li&gt;
&lt;li&gt;git and unzip are needed by Composer to fetch packages; they are installed here but not carried over to the production stage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3.4 Drush Installation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ENV COMPOSER_HOME=/usr/local/share/composer&lt;br&gt;
RUN composer global require drush/drush:8.* --no-interaction --prefer-dist&lt;br&gt;
Drush (Drupal Shell) is installed globally via Composer. COMPOSER_HOME is set explicitly so the entire vendor directory can be copied to the production stage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.5 Node.js Installation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RUN curl -fsSL &lt;a href="https://deb.nodesource.com/setup_20.x" rel="noopener noreferrer"&gt;https://deb.nodesource.com/setup_20.x&lt;/a&gt; | bash - \&lt;br&gt;
    &amp;amp;&amp;amp; apt-get install -y --no-install-recommends nodejs \&lt;br&gt;
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/*&lt;br&gt;
Node.js 20.x (LTS) is installed for front-end build tools (npm scripts, asset compilation). NVM is intentionally avoided - a single global Node.js installation is simpler and more predictable in a container.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Build once, run anywhere - but make sure what you build is only what you need.” - Container best practice principle&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Stage 2: Production - Minimal Runtime Image
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;4.1 Fresh Base and Labels&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;FROM php:8.4-fpm AS production&lt;/p&gt;

&lt;p&gt;LABEL maintainer="AddWeb Solutions" \&lt;br&gt;
      description="PHP 8.4-FPM production image with WordPress/Drupal tooling"&lt;br&gt;
A fresh php:8.4-fpm base - none of the builder’s build tools, caches, or temp files exist here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.2 Copying Artifacts from Builder&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Copy PHP extensions and their configs from builder&lt;br&gt;
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/&lt;br&gt;
COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/&lt;/p&gt;

&lt;p&gt;Copy CLI tools from builder&lt;br&gt;
COPY --from=builder /usr/local/bin/composer /usr/local/bin/composer&lt;br&gt;
COPY --from=builder /usr/local/bin/wp /usr/local/bin/wp&lt;br&gt;
COPY --from=builder /usr/local/bin/drupal /usr/local/bin/drupal&lt;br&gt;
COPY --from=builder /usr/local/share/composer /usr/local/share/composer&lt;/p&gt;

&lt;p&gt;Copy Node.js from builder&lt;br&gt;
COPY --from=builder /usr/bin/node /usr/bin/node&lt;br&gt;
COPY --from=builder /usr/bin/npm /usr/bin/npm&lt;br&gt;
COPY --from=builder /usr/bin/npx /usr/bin/npx&lt;br&gt;
COPY --from=builder /usr/lib/node_modules /usr/lib/node_modules&lt;br&gt;
COPY --from=builder selectively pulls only the compiled .so files, .ini configs, and CLI binaries. Everything else (compilers, header files, build caches) is left behind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.3 Runtime Libraries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RUN apt-get update &amp;amp;&amp;amp; apt-get install -y --no-install-recommends \&lt;br&gt;
        # Runtime libs required by PHP extensions&lt;br&gt;
        libbz2-1.0 \&lt;br&gt;
        libfreetype6 \&lt;br&gt;
        libgmp10 \&lt;br&gt;
        libicu76 \&lt;br&gt;
        libjpeg62-turbo \&lt;br&gt;
        libmagickwand-7.q16-10 \&lt;br&gt;
        libavif16 \&lt;br&gt;
        libpng16-16 \&lt;br&gt;
        libpq5 \&lt;br&gt;
        libxslt1.1 \&lt;br&gt;
        libzip5 \&lt;br&gt;
        # Essential runtime utilities only&lt;br&gt;
        cron \&lt;br&gt;
        curl \&lt;br&gt;
        default-mysql-client \&lt;br&gt;
        ffmpeg \&lt;br&gt;
        sendmail \&lt;br&gt;
        supervisor \&lt;br&gt;
        unzip \&lt;br&gt;
        sqlite3 \&lt;br&gt;
    &amp;amp;&amp;amp; apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \&lt;br&gt;
    &amp;amp;&amp;amp; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runtime libs only: We install the shared libraries (.so files) that the compiled PHP extensions link against. These are the -dev-less counterparts (e.g., libpng16-16 not libpng-dev).&lt;/li&gt;
&lt;li&gt;Debian Trixie package names: Since php:8.4-fpm is based on Debian 13 (Trixie), some package names differ from Bookworm (e.g., libicu76 instead of libicu72, libmagickwand-7.q16-10 instead of libmagickwand-6.q16-6, libzip5 instead of libzip4).&lt;/li&gt;
&lt;li&gt;Essential utilities: cron (scheduled tasks), curl (health checks, API calls), default-mysql-client (database operations), ffmpeg (media processing), sendmail (email delivery), supervisor (process management), unzip, sqlite3.&lt;/li&gt;
&lt;li&gt;Cleanup: apt-get purge --auto-remove removes packages that were pulled in only as install dependencies, and rm -rf /var/lib/apt/lists/* clears the package cache.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Runtime libraries mapped to extensions:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;4.4 Drush Symlink&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ENV COMPOSER_HOME=/usr/local/share/composer&lt;br&gt;
RUN ln -s /usr/local/share/composer/vendor/bin/drush /usr/local/bin/drush&lt;br&gt;
Makes drush available in $PATH without modifying the PATH variable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.5 OPcache Tuning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RUN { \&lt;br&gt;
        echo 'opcache.memory_consumption=128'; \&lt;br&gt;
        echo 'opcache.interned_strings_buffer=8'; \&lt;br&gt;
        echo 'opcache.max_accelerated_files=4000'; \&lt;br&gt;
        echo 'opcache.revalidate_freq=60'; \&lt;br&gt;
        echo 'opcache.fast_shutdown=1'; \&lt;br&gt;
        echo 'opcache.enable_cli=1'; \&lt;br&gt;
        echo 'opcache.validate_timestamps=0'; \&lt;br&gt;
    } &amp;gt; /usr/local/etc/php/conf.d/opcache-recommended.ini&lt;/p&gt;

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

&lt;p&gt;validate_timestamps=0 is a production optimization - PHP never starts the filesystem to check if files have changed. When you deploy new code, restart PHP-FPM to pick up the changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.6 Security Hardening&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RUN { \&lt;br&gt;
        echo 'expose_php=Off'; \&lt;br&gt;
        echo 'display_errors=Off'; \&lt;br&gt;
        echo 'log_errors=On'; \&lt;br&gt;
        echo 'error_log=/dev/stderr'; \&lt;br&gt;
        echo 'allow_url_fopen=Off'; \&lt;br&gt;
    } &amp;gt; /usr/local/etc/php/conf.d/security.ini&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;4.7 Non-Root User&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RUN groupadd -g 1000 appuser &amp;amp;&amp;amp; useradd -u 1000 -g appuser -m appuser&lt;/p&gt;

&lt;p&gt;WORKDIR /var/www/html&lt;br&gt;
RUN chown -R appuser:appuser /var/www/html&lt;br&gt;
Running PHP-FPM as a non-root user reduces the blast radius if the application is compromised. The appuser (UID 1000) owns the web root.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.8 Health Check&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;HEALTHCHECK --interval=30s --timeout=5s --retries=3 \&lt;br&gt;
    CMD php-fpm-healthcheck || kill -0 $(cat /var/run/php-fpm.pid 2&amp;gt;/dev/null) || exit 1&lt;br&gt;
Docker checks every 30 seconds whether PHP-FPM is responding. After 3 consecutive failures, the container is marked unhealthy, and orchestrators (Docker Compose, Swarm, Kubernetes) can restart it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.9  Expose and CMD&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;EXPOSE 9000&lt;/p&gt;

&lt;p&gt;CMD ["php-fpm"]&lt;br&gt;
PHP-FPM listens on port 9000 (FastCGI). A reverse proxy (Nginx, Apache, Caddy) forwards requests to this port.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The .dockerignore File
&lt;/h2&gt;

&lt;p&gt;.git&lt;br&gt;
.github&lt;br&gt;
.gitignore&lt;br&gt;
&lt;em&gt;.md&lt;br&gt;
LICENSE&lt;br&gt;
docker-compose&lt;/em&gt;.yml&lt;br&gt;
.env*&lt;br&gt;
.vscode&lt;br&gt;
.idea&lt;br&gt;
node_modules&lt;br&gt;
vendor&lt;br&gt;
.docker&lt;/p&gt;

&lt;p&gt;The .dockerignore prevents unnecessary files from being sent to the Docker build context. This speeds up builds and avoids leaking secrets (.env files) or bloating the image with node_modules or vendor directories.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Practical Setup (Step-by-step)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;6.1 Build the Image&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;docker build -t php8.4-multistage --target production .&lt;br&gt;
The --target production flag tells Docker to stop at the production stage and discard the builder. If --target is omitted, Docker builds up to the last stage (which is already in production in this Dockerfile).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.2 Run the Container&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;docker run -d \&lt;br&gt;
    --name php-app \&lt;br&gt;
    -p 9000:9000 \&lt;br&gt;
    -v ./src:/var/www/html \&lt;br&gt;
    php8.4-multistage&lt;br&gt;
Mount your application source code at /var/www/html. PHP-FPM will serve it on port 9000.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.3 Pair with Nginx (docker-compose example)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;version: '3.8'&lt;/p&gt;

&lt;p&gt;services:&lt;br&gt;
  php:&lt;br&gt;
    build:&lt;br&gt;
      context: .&lt;br&gt;
      target: production&lt;br&gt;
    volumes:&lt;br&gt;
      - ./src:/var/www/html&lt;br&gt;
    networks:&lt;br&gt;
      - app-network&lt;/p&gt;

&lt;p&gt;nginx:&lt;br&gt;
    image: nginx:alpine&lt;br&gt;
    ports:&lt;br&gt;
      - "80:80"&lt;br&gt;
    volumes:&lt;br&gt;
      - ./src:/var/www/html&lt;br&gt;
      - ./nginx.conf:/etc/nginx/conf.d/default.conf&lt;br&gt;
    depends_on:&lt;br&gt;
      - php&lt;br&gt;
    networks:&lt;br&gt;
      - app-network&lt;/p&gt;

&lt;p&gt;networks:&lt;br&gt;
  app-network:&lt;br&gt;
    driver: bridge&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.4 Verify Extensions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;docker exec php-app php -m&lt;br&gt;
This lists all loaded PHP modules. Confirm that gd, imagick, intl, opcache, pdo_mysql, etc., are all present.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.5 Verify CLI Tools&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;docker exec php-app composer --version&lt;br&gt;
docker exec php-app wp --version&lt;br&gt;
docker exec php-app drush --version&lt;br&gt;
docker exec php-app node --version&lt;br&gt;
docker exec php-app npm --version&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6.6 Check phpinfo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create a phpinfo file&lt;br&gt;
docker exec php-app bash -c "echo '&amp;lt;?php phpinfo();' &amp;gt; /var/www/html/info.php"&lt;/p&gt;

&lt;p&gt;Quick test with built-in server&lt;br&gt;
docker exec -d php-app php -S 0.0.0.0:8085 -t /var/www/html&lt;br&gt;
Open &lt;a href="http://localhost:8085/info.php" rel="noopener noreferrer"&gt;http://localhost:8085/info.php&lt;/a&gt; in your browser to see the full PHP configuration. Remove info.php after testing - never leave it exposed in production.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;6.7 Common Improvements (optional)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use BuildKit cache mounts to speed up repeated builds: RUN --mount=type=cache,target=/var/cache/apt apt-get update &amp;amp;&amp;amp; ...&lt;/li&gt;
&lt;li&gt;Pin the PHP version (e.g., php:8.4.18-fpm) for reproducible builds instead of using the floating 8.4-fpm tag.&lt;/li&gt;
&lt;li&gt;Add a .env-based PHP config layer for settings that vary between environments (memory_limit, upload_max_filesize).&lt;/li&gt;
&lt;li&gt;Use Docker Compose profiles to add Xdebug only in development (avoid Xdebug in production).&lt;/li&gt;
&lt;li&gt;Scan the image with Trivy or Docker Scout for vulnerability reporting before pushing to a registry.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;“The art of simplicity is a puzzle of complexity.” - Douglas Horton&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  8. FAQs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. Why use multi-stage builds instead of a single Dockerfile?&lt;/strong&gt; &lt;br&gt;
A. A single-stage build carries compilers, dev headers, and package caches into the final image. Multi-stage builds discard all of that, resulting in a smaller, more secure image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. Why install-php-extensions instead of docker-php-ext-install?&lt;/strong&gt; A. install-php-extensions handles OS dependency installation, extension compilation, and cleanup automatically. It also supports PECL extensions (imagick, mongodb, mailparse) with the same syntax, eliminating the need for separate pecl install commands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. Why is the base image Debian Trixie? Can I use Bookworm or Alpine?&lt;/strong&gt; &lt;br&gt;
A. The official php:8.4-fpm tag is built on Debian 13 (Trixie) as of 2025. If you need Bookworm, use php:8.4-fpm-bookworm. Alpine (php:8.4-fpm-alpine) is smaller but uses musl libc, which can cause compatibility issues with some extensions (notably ImageMagick).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. Why are the runtime library package names different from older guides?&lt;/strong&gt; &lt;br&gt;
A. Debian Trixie renamed several packages as part of the 64-bit time_t transition and library version bumps. For example: libicu72 became libicu76, libmagickwand-6.q16-6 became libmagickwand-7.q16-10, and libzip4 became libzip5. Always verify package names against the base image’s Debian version.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. How do I find the correct runtime library names if I change the base image?&lt;/strong&gt; &lt;br&gt;
A. Run a temporary container and use ldd to check for missing libraries:&lt;br&gt;
docker run --rm your-image bash -c \&lt;br&gt;
  "for so in /usr/local/lib/php/extensions/&lt;em&gt;/&lt;/em&gt;.so; do \&lt;br&gt;
     ldd \$so 2&amp;gt;/dev/null | grep 'not found'; \&lt;br&gt;
   done"&lt;br&gt;
Then search for the correct package with apt-cache search .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. What does validate_timestamps=0 mean for deployments?&lt;/strong&gt; &lt;br&gt;
A. OPcache will never check if PHP files on disk have changed. This avoids filesystem stat calls on every request (faster). The tradeoff: after deploying new code, you must restart PHP-FPM (docker restart ) to pick up changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. Why create a non-root user?&lt;/strong&gt; &lt;br&gt;
A. Running as root inside a container means a compromised application has full control over the container filesystem. A non-root user limits the damage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q8. Can I add Xdebug for local development?&lt;/strong&gt; &lt;br&gt;
A. Yes, but do not include it in the production image. Use a separate development stage or a Docker Compose override file that installs Xdebug on top of the production image.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Multi-stage builds separate build-time complexity from runtime simplicity.&lt;/li&gt;
&lt;li&gt;install-php-extensions eliminates the manual dance of installing dev packages, compiling, and cleaning up.&lt;/li&gt;
&lt;li&gt;Always match runtime library package names to the base image’s Debian version (Trixie uses libicu76, libzip5, libmagickwand-7.q16-10, libavif16).&lt;/li&gt;
&lt;li&gt;OPcache with validate_timestamps=0 is a significant performance win for production.&lt;/li&gt;
&lt;li&gt;Security hardening (expose_php=Off, display_errors=Off, allow_url_fopen=Off) should be the default, not an afterthought.&lt;/li&gt;
&lt;li&gt;Non-root user + health check + .dockerignore round out a production-ready setup.&lt;/li&gt;
&lt;li&gt;Use ldd to diagnose missing shared libraries when extensions fail to load.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10. Conclusion
&lt;/h2&gt;

&lt;p&gt;This multi-stage Dockerfile provides a clean, repeatable way to build a PHP 8.4-FPM production image with everything needed for WordPress and Drupal projects. The builder stage handles all the heavy lifting - compiling extensions, installing Composer, WP-CLI, Drush, Drupal Console, and Node.js - while the production stage starts fresh and copies only what is needed at runtime. Combined with OPcache tuning, security hardening, a non-root user, and a health check, the result is an image that is smaller, faster, and more secure than a traditional single-stage build. When paired with Nginx via Docker Compose, it forms a solid foundation for deploying PHP applications in any environment.&lt;/p&gt;

&lt;p&gt;About the Author:&lt;em&gt;Rajan is a DevOps Engineer at &lt;a href="https://www.addwebsolution.com/" rel="noopener noreferrer"&gt;AddWebSolution&lt;/a&gt;, specializing in automation infrastructure, Optimize the CI/CD Pipelines and ensuring seamless deployments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>dockerfile</category>
      <category>dockerignore</category>
      <category>multistagebuilds</category>
    </item>
    <item>
      <title>Architecting Large-Scale Next.js Applications (Folder Structure, Patterns, Best Practices)</title>
      <dc:creator>Mayank Goyal</dc:creator>
      <pubDate>Mon, 13 Apr 2026 10:25:46 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/architecting-large-scale-nextjs-applications-folder-structure-patterns-best-practices-2dpj</link>
      <guid>https://dev.to/addwebsolutionpvtltd/architecting-large-scale-nextjs-applications-folder-structure-patterns-best-practices-2dpj</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“Good architecture makes the system easy to understand; great architecture makes it hard to break.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Feature-based architecture is critical&lt;/li&gt;
&lt;li&gt;Separate UI, logic, and data layers&lt;/li&gt;
&lt;li&gt;Prefer server components for performance&lt;/li&gt;
&lt;li&gt;Centralize API logic&lt;/li&gt;
&lt;li&gt;Use scalable state management&lt;/li&gt;
&lt;li&gt;Optimize early, not later&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Index
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Why Architecture Matters&lt;/li&gt;
&lt;li&gt;Core Principles of Scalable Architecture&lt;/li&gt;
&lt;li&gt;Advanced Folder Structure (Enterprise-Level)&lt;/li&gt;
&lt;li&gt;Architectural Patterns (Deep Dive)&lt;/li&gt;
&lt;li&gt;State Management at Scale&lt;/li&gt;
&lt;li&gt;Data Fetching &amp;amp; API Layer Design&lt;/li&gt;
&lt;li&gt;Authentication &amp;amp; Authorization Architecture&lt;/li&gt;
&lt;li&gt;Performance Optimization (Advanced)&lt;/li&gt;
&lt;li&gt;Error Handling &amp;amp; Logging&lt;/li&gt;
&lt;li&gt;Testing Strategy (Production-Ready)&lt;/li&gt;
&lt;li&gt;Dev Experience &amp;amp; Code Quality&lt;/li&gt;
&lt;li&gt;Deployment &amp;amp; Infrastructure Strategy&lt;/li&gt;
&lt;li&gt;Real-World Example (Enterprise Dashboard)&lt;/li&gt;
&lt;li&gt;Interesting Facts&lt;/li&gt;
&lt;li&gt;Stats&lt;/li&gt;
&lt;li&gt;FAQ’s&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Building small Next.js apps is easy. Scaling them to support millions of users, multiple developers, and complex business logic is not.&lt;br&gt;
Large-scale applications require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean architecture&lt;/li&gt;
&lt;li&gt;Predictable structure&lt;/li&gt;
&lt;li&gt;Separation of concerns&lt;/li&gt;
&lt;li&gt;Strong conventions
Next.js (especially App Router) provides powerful primitives, but it does NOT enforce architecture, that’s your responsibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This guide gives you a production-grade blueprint.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Architecture Matters
&lt;/h2&gt;

&lt;p&gt;At scale, poor architecture leads to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tight coupling between components&lt;/li&gt;
&lt;li&gt;Duplicate logic across features&lt;/li&gt;
&lt;li&gt;Slow builds &amp;amp; performance bottlenecks&lt;/li&gt;
&lt;li&gt;Difficult onboarding for new developers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Good architecture enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Independent feature development&lt;/li&gt;
&lt;li&gt;Faster debugging&lt;/li&gt;
&lt;li&gt;Better scalability&lt;/li&gt;
&lt;li&gt;Easier refactoring&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Core Principles of Scalable Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Separation of Concerns&lt;/strong&gt;&lt;br&gt;
Divide responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI → components&lt;/li&gt;
&lt;li&gt;Logic → hooks/services&lt;/li&gt;
&lt;li&gt;Data → API layer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Feature Isolation&lt;/strong&gt;&lt;br&gt;
Each feature should be self-contained.&lt;br&gt;
Think like mini-apps inside your app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Single Responsibility Principle&lt;/strong&gt;&lt;br&gt;
Each file/module should do one thing well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Dependency Direction&lt;/strong&gt;&lt;br&gt;
Components depend on hooks&lt;br&gt;
Hooks depend on services&lt;br&gt;
Services depend on APIs&lt;br&gt;
NOT the other way around.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Scalability First Mindset&lt;/strong&gt;&lt;br&gt;
Design for scale even if you’re small today.&lt;/p&gt;
&lt;h2&gt;
  
  
  Advanced Folder Structure (Enterprise-Level)
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
│
├── app/                         # Next.js App Router
│   ├── (public)/
│   ├── (auth)/
│   ├── dashboard/
│   │   ├── layout.tsx
│   │   ├── page.tsx
│
├── features/                    # Feature modules (CORE)
│   ├── auth/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── services/
│   │   ├── api/
│   │   ├── store/
│   │   └── types.ts
│   │
│   ├── products/
│   ├── orders/
│   └── users/
│
├── shared/                      # Cross-feature reusable code
│   ├── components/
│   ├── hooks/
│   ├── utils/
│   └── constants/
│
├── core/                        # App-level logic
│   ├── config/
│   ├── providers/
│   ├── middleware/
│   └── guards/
│
├── services/                    # Global services (rare)
├── lib/                         # Low-level utilities
├── types/                       # Global types
├── styles/
└── tests/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;“Fetching data is easy. Fetching it efficiently is architecture.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Key Insight&lt;/strong&gt;&lt;br&gt;
Avoid “global chaos” folders like components/ for everything&lt;br&gt;
Prefer feature-based grouping&lt;/p&gt;
&lt;h2&gt;
  
  
  Architectural Patterns (Deep Dive)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Feature-Based Architecture (MOST IMPORTANT)&lt;/strong&gt;&lt;br&gt;
Each feature owns:&lt;br&gt;
UI&lt;br&gt;
logic&lt;br&gt;
API calls&lt;br&gt;
state&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;features/products/
    components/
    hooks/
    services/
    store/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Layered Architecture&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UI Layer (Components)
↓
Hooks Layer (Business Logic)
↓
Service Layer (API Calls)
↓
API Layer (External systems)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Server vs Client Component Strategy&lt;/strong&gt;&lt;/p&gt;

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

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Server Component&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getProducts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ProductsClient&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Smart vs Dumb Components&lt;/strong&gt;&lt;br&gt;
Smart → fetch + logic&lt;br&gt;
Dumb → UI only&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Composition Pattern&lt;/strong&gt;&lt;br&gt;
Avoid inheritance. Use composition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductInfo&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  State Management at Scale
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When NOT to use global state&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static data&lt;/li&gt;
&lt;li&gt;Server-fetched data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Recommended Strategy&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Example (Zustand Advanced)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zustand&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useCartStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="na"&gt;clearCart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Data Fetching &amp;amp; API Layer Design
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bad Practice&lt;/strong&gt;&lt;br&gt;
Calling fetch directly in components everywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good Practice&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;features&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;products&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
  &lt;span class="nx"&gt;services&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;productService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getProducts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;“Every unnecessary render is a tax on your use.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Caching Strategy&lt;/strong&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Authentication &amp;amp; Authorization Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Recommended Setup&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Middleware for route protection&lt;/li&gt;
&lt;li&gt;Server-side session validation&lt;/li&gt;
&lt;li&gt;Role-based access
Example:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// middleware.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Performance Optimization (Advanced)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Techniques&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code splitting (dynamic imports)&lt;/li&gt;
&lt;li&gt;Partial hydration&lt;/li&gt;
&lt;li&gt;Edge rendering&lt;/li&gt;
&lt;li&gt;Image optimization&lt;/li&gt;
&lt;li&gt;Memoization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Chart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./Chart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;ssr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Error Handling &amp;amp; Logging
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Centralized Error Handling&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Logging Tools&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sentry&lt;/li&gt;
&lt;li&gt;LogRocket&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Testing Strategy (Production-Ready)
&lt;/h2&gt;

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

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;adds item to cart&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCartStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addItem&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dev Experience &amp;amp; Code Quality
&lt;/h2&gt;

&lt;p&gt;ESLint + Prettier&lt;br&gt;
Husky (pre-commit hooks)&lt;br&gt;
Strict TypeScript&lt;br&gt;
Absolute imports (@/features/...)&lt;/p&gt;
&lt;h2&gt;
  
  
  Deployment &amp;amp; Infrastructure Strategy
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Recommended Stack&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosting → Vercel&lt;/li&gt;
&lt;li&gt;DB → PostgreSQL&lt;/li&gt;
&lt;li&gt;CDN → Cloudflare&lt;/li&gt;
&lt;li&gt;Monitoring → Sentry&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scaling Tips&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Edge Functions&lt;/li&gt;
&lt;li&gt;Optimize bundle size&lt;/li&gt;
&lt;li&gt;Enable caching&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Real-World Example (Enterprise Dashboard)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Structure&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;features/dashboard/
   components/
      StatsCard.tsx
   hooks/
      useStats.ts
   services/
      dashboardService.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Service&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/stats&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Hook&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setStats&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;fetchStats&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setStats&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Component&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;StatsCard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;totalUsers&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Page&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;DashboardPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useStats&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;StatsCard&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Interesting Facts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js App Router defaults to Server Components.Source: &lt;a href="https://nextjs.org/docs/app/building-your-application/rendering/server-components" rel="noopener noreferrer"&gt;https://nextjs.org/docs/app/building-your-application/rendering/server-components&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Middleware runs at the Edge.Source: &lt;a href="https://nextjs.org/docs/pages/api-reference/file-conventions/middleware" rel="noopener noreferrer"&gt;https://nextjs.org/docs/pages/api-reference/file-conventions/middleware&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;File-based routing reduces ~40% boilerplate.Source: &lt;a href="https://nextjs.org/docs/app/building-your-application/routing" rel="noopener noreferrer"&gt;https://nextjs.org/docs/app/building-your-application/routing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Built-in optimizations replace many libraries.Source: &lt;a href="https://nextjs.org/docs/architecture" rel="noopener noreferrer"&gt;https://nextjs.org/docs/architecture&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;ISR allows hybrid static + dynamic pages.Source: &lt;a href="https://nextjs.org/docs/pages/building-your-application/rendering/incremental-static-regeneration" rel="noopener noreferrer"&gt;https://nextjs.org/docs/pages/building-your-application/rendering/incremental-static-regeneration&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stats
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js is one of the fastest-growing React frameworks and is widely adopted for production-grade applications.
Source: &lt;a href="https://nextjs.org/showcase" rel="noopener noreferrer"&gt;https://nextjs.org/showcase&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Next.js has 120,000+ stars on GitHub, reflecting its strong developer adoption and community support.
Source: &lt;a href="https://github.com/vercel/next.js" rel="noopener noreferrer"&gt;https://github.com/vercel/next.js&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;According to the State of JS survey, Next.js consistently ranks among the top frameworks in developer satisfaction and usage.
Source: &lt;a href="https://stateofjs.com/" rel="noopener noreferrer"&gt;https://stateofjs.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Vercel, the company behind Next.js, serves billions of requests per week across applications deployed on its platform.
Source: &lt;a href="https://vercel.com/customers" rel="noopener noreferrer"&gt;https://vercel.com/customers&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Next.js enables hybrid rendering (SSR, SSG, ISR), which improves performance and scalability for modern web applications.
Source: &lt;a href="https://nextjs.org/docs/pages/building-your-application/rendering" rel="noopener noreferrer"&gt;https://nextjs.org/docs/pages/building-your-application/rendering&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;File-based routing in Next.js simplifies development by automatically mapping files to routes, reducing manual configuration.
Source: &lt;a href="https://nextjs.org/docs/app/building-your-application/routing" rel="noopener noreferrer"&gt;https://nextjs.org/docs/app/building-your-application/routing&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ’s
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. Is Redux necessary?&lt;/strong&gt;&lt;br&gt;
No. Use Zustand unless you need complex workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. How to organize large teams?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature ownership&lt;/li&gt;
&lt;li&gt;Code reviews&lt;/li&gt;
&lt;li&gt;Clear folder structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Q3. Should I use monorepo?&lt;/strong&gt;&lt;br&gt;
Yes, for multi-app systems (Nx / Turborepo).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. Where to keep reusable components?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;shared/components&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. What is the biggest mistake?&lt;/strong&gt;&lt;br&gt;
Mixing everything in global folders.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Scaling a Next.js application is more about architecture than code. The difference between a messy app and a scalable system lies in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Structure&lt;/li&gt;
&lt;li&gt;Discipline&lt;/li&gt;
&lt;li&gt;Consistency
By following feature-based design, layered architecture, and modern Next.js patterns, you can build applications that scale effortlessly with both users and teams.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;About the Author:&lt;em&gt;Mayank is a web developer at &lt;a href="https://www.addwebsolution.com/" rel="noopener noreferrer"&gt;AddWebSolution&lt;/a&gt;, building scalable apps with PHP, Node.js &amp;amp; React. Sharing ideas, code, and creativity.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>ELK Stack Setup for Centralized Log Management &amp; Monitoring</title>
      <dc:creator>Rajan Vavadia</dc:creator>
      <pubDate>Wed, 08 Apr 2026 11:42:01 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/elk-stack-setup-for-centralized-log-management-monitoring-11l0</link>
      <guid>https://dev.to/addwebsolutionpvtltd/elk-stack-setup-for-centralized-log-management-monitoring-11l0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“The goal is to turn data into information, and information into insight.” - Carly Fiorina&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Stage 1: Elasticsearch - The Search &amp;amp; Storage Engine&lt;/li&gt;
&lt;li&gt;Stage 2: Logstash - The Data Processing Pipeline&lt;/li&gt;
&lt;li&gt;Stage 3: Kibana - The Visualization Layer&lt;/li&gt;
&lt;li&gt;Stage 4: Filebeat - The Lightweight Log Shipper&lt;/li&gt;
&lt;li&gt;Connecting the Pieces - End-to-End Data Flow&lt;/li&gt;
&lt;li&gt;Practical Setup (Step-by-step)&lt;/li&gt;
&lt;li&gt;Troubleshooting Common Issues&lt;/li&gt;
&lt;li&gt;Quotes&lt;/li&gt;
&lt;li&gt;FAQs&lt;/li&gt;
&lt;li&gt;Key Takeaways&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;When your application runs on a single server, tailing a log file is enough. When it runs across multiple servers, containers, or microservices - you need centralized logging. Scattered logs across dozens of servers make debugging slow, error correlation impossible, and incident response reactive instead of proactive.&lt;/p&gt;

&lt;p&gt;This guide walks through setting up a production-ready ELK stack (Elasticsearch, Logstash, Kibana) with Filebeat for centralized log collection, processing, and visualization. The setup covers a real-world scenario: a Java Spring Boot application running on one server, with the ELK stack on a separate server.&lt;/p&gt;

&lt;p&gt;The approach uses a &lt;strong&gt;four-component architecture:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filebeat (Application Server): Lightweight agent that tails log files and ships them to Logstash.&lt;/li&gt;
&lt;li&gt;Logstash (ELK Server): Receives raw logs, parses and transforms them, and forwards structured data to Elasticsearch.&lt;/li&gt;
&lt;li&gt;Elasticsearch (ELK Server): Stores, indexes, and makes logs searchable in near real-time.&lt;/li&gt;
&lt;li&gt;Kibana (ELK Server): Web UI for searching, visualizing, and building dashboards from log data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why centralized logging?&lt;/strong&gt;&lt;br&gt;
Manually SSH-ing into each server and grepping through log files does not scale. Centralized logging solves this by aggregating all logs into a single searchable location. &lt;br&gt;
You get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single pane of glass for all application and infrastructure logs.&lt;/li&gt;
&lt;li&gt;Real-time search across millions of log entries in milliseconds.&lt;/li&gt;
&lt;li&gt;Correlation of events across services and servers by timestamp.&lt;/li&gt;
&lt;li&gt;Alerting on error patterns before users report issues.&lt;/li&gt;
&lt;li&gt;Retention and compliance with configurable index lifecycle policies.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Component Responsibilities&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Why separate servers?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;- Resource isolation:&lt;/strong&gt; Elasticsearch is memory-hungry. Running it on the application server competes with your app for RAM and CPU.&lt;br&gt;
&lt;strong&gt;- Independent scaling:&lt;/strong&gt; You can scale the ELK server (more RAM, bigger disk) without touching production application servers.&lt;br&gt;
&lt;strong&gt;- Security boundary:&lt;/strong&gt; The ELK server can sit in a private subnet, accessible only to internal services and authorized users.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Stage 1: Elasticsearch - The Search &amp;amp; Storage Engine
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;2.1 What is Elasticsearch?&lt;/strong&gt;&lt;br&gt;
Elasticsearch is a distributed search and analytics engine built on Apache Lucene. In the ELK stack, it serves as the storage and search backend - every log line that Logstash processes ends up as a document in an Elasticsearch index.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.2 Installation&lt;/strong&gt;&lt;br&gt;
Import the Elasticsearch GPG key&lt;br&gt;
wget -qO - &lt;a href="https://artifacts.elastic.co/GPG-KEY-elasticsearch" rel="noopener noreferrer"&gt;https://artifacts.elastic.co/GPG-KEY-elasticsearch&lt;/a&gt; | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg&lt;/p&gt;

&lt;p&gt;Add the APT repository&lt;br&gt;
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] &lt;a href="https://artifacts.elastic.co/packages/8.x/apt" rel="noopener noreferrer"&gt;https://artifacts.elastic.co/packages/8.x/apt&lt;/a&gt; stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list&lt;/p&gt;

&lt;p&gt;Install Elasticsearch&lt;br&gt;
sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y elasticsearch&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.3 Configuration&lt;/strong&gt;&lt;br&gt;
The main configuration file is /etc/elasticsearch/elasticsearch.yml:&lt;br&gt;
Cluster and node identity&lt;br&gt;
cluster.name: my-cluster&lt;br&gt;
node.name: node-1&lt;/p&gt;

&lt;p&gt;Data and log paths&lt;br&gt;
path.data: /var/lib/elasticsearch&lt;br&gt;
path.logs: /var/log/elasticsearch&lt;/p&gt;

&lt;p&gt;Network - bind to all interfaces for external access&lt;br&gt;
network.host: 0.0.0.0&lt;br&gt;
http.host: 0.0.0.0&lt;/p&gt;

&lt;p&gt;Discovery - single node (no cluster formation)&lt;br&gt;
discovery.type: single-node&lt;/p&gt;

&lt;p&gt;Security - disable for internal/dev setups&lt;br&gt;
xpack.security.enabled: false&lt;br&gt;
xpack.security.enrollment.enabled: true&lt;br&gt;
xpack.security.http.ssl:&lt;br&gt;
  enabled: false&lt;br&gt;
xpack.security.transport.ssl:&lt;br&gt;
  enabled: false&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuration settings explained:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Security note:&lt;/strong&gt; xpack.security.enabled: false is acceptable for internal/development setups behind a firewall. For production environments exposed to the internet, always enable security with TLS certificates and user authentication.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.4 Start and Enable&lt;/strong&gt;&lt;br&gt;
sudo systemctl daemon-reload&lt;br&gt;
sudo systemctl enable elasticsearch&lt;br&gt;
sudo systemctl start elasticsearch&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.5 Verify&lt;/strong&gt;&lt;br&gt;
curl &lt;a href="http://localhost:9200" rel="noopener noreferrer"&gt;http://localhost:9200&lt;/a&gt;&lt;br&gt;
Expected response:&lt;br&gt;
{&lt;br&gt;
  "name": "node-1",&lt;br&gt;
  "cluster_name": "my-cluster",&lt;br&gt;
  "version": {&lt;br&gt;
    "number": "8.x.x"&lt;br&gt;
  },&lt;br&gt;
  "tagline": "You Know, for Search"&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.6 Memory Considerations&lt;/strong&gt;&lt;br&gt;
Elasticsearch defaults to a 1GB heap (-Xms1g -Xmx1g). For production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set heap to 50% of available RAM, but never more than 31GB (to stay within compressed OOPs).&lt;/li&gt;
&lt;li&gt;Edit /etc/elasticsearch/jvm.options.d/heap.options:
-Xms2g
-Xmx2g&lt;/li&gt;
&lt;li&gt;Ensure the system has enough RAM for both the JVM heap and filesystem cache (Lucene relies heavily on OS page cache).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;h2&gt;
  
  
  3. Stage 2: Logstash - The Data Processing Pipeline
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;3.1 What is Logstash?&lt;/strong&gt;&lt;br&gt;
Logstash is a server-side data processing pipeline that ingests data from multiple sources, transforms it, and sends it to Elasticsearch. It sits between Filebeat and Elasticsearch, adding structure to raw log lines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.2 Installation&lt;/strong&gt;&lt;br&gt;
sudo apt-get install -y logstash&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.3 Pipeline Configuration&lt;/strong&gt;&lt;br&gt;
Logstash pipelines are defined in /etc/logstash/conf.d/. Each pipeline has three sections: input, filter, and output.&lt;br&gt;
Create /etc/logstash/conf.d/boardgame.conf:&lt;br&gt;
input {&lt;br&gt;
  beats {&lt;br&gt;
    port =&amp;gt; 5044&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;filter {&lt;br&gt;
  #Parse Spring Boot log format&lt;br&gt;
  grok {&lt;br&gt;
    match =&amp;gt; { "message" =&amp;gt; "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:loglevel} %{GREEDYDATA:logmessage}" }&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;output {&lt;br&gt;
  elasticsearch {&lt;br&gt;
    hosts =&amp;gt; ["localhost:9200"]&lt;br&gt;
    index =&amp;gt; "boardgame-logs-%{+YYYY.MM.dd}"&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
Pipeline sections explained:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input - Where data comes from:&lt;/strong&gt;&lt;br&gt;
input {&lt;br&gt;
  beats {&lt;br&gt;
    port =&amp;gt; 5044&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Filter - How data is transformed:&lt;/strong&gt;&lt;br&gt;
filter {&lt;br&gt;
  grok {&lt;br&gt;
    match =&amp;gt; { "message" =&amp;gt; "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:loglevel} %{GREEDYDATA:logmessage}" }&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

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

&lt;p&gt;The grok filter parses unstructured log lines into structured fields (timestamp, loglevel, logmessage). This enables filtering by log level in Kibana (e.g., show only ERROR logs).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output - Where data goes:&lt;/strong&gt;&lt;br&gt;
output {&lt;br&gt;
  elasticsearch {&lt;br&gt;
    hosts =&amp;gt; ["localhost:9200"]&lt;br&gt;
    index =&amp;gt; "boardgame-logs-%{+YYYY.MM.dd}"&lt;br&gt;
  }&lt;br&gt;
}&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Why daily indices?&lt;/strong&gt; Daily indices make retention management simple - delete old indices by date. They also improve search performance because Elasticsearch can skip entire indices when querying a specific time range.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.4 Start and Enable&lt;/strong&gt;&lt;br&gt;
sudo systemctl daemon-reload&lt;br&gt;
sudo systemctl enable logstash&lt;br&gt;
sudo systemctl start logstash&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.5 Verify&lt;/strong&gt;&lt;br&gt;
Check if Logstash is listening on port 5044&lt;br&gt;
sudo ss -tlnp | grep 5044&lt;/p&gt;

&lt;p&gt;Check Logstash logs for pipeline startup&lt;br&gt;
sudo journalctl -u logstash --no-pager -n 20&lt;br&gt;
Look for: Pipeline started {"pipeline.id"=&amp;gt;"main"} in the logs.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Stage 3: Kibana - The Visualization Layer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;4.1 What is Kibana?&lt;/strong&gt;&lt;br&gt;
Kibana is the web interface for the ELK stack. It connects to Elasticsearch and provides tools for searching logs (Discover), building visualizations (charts, graphs, maps), and creating dashboards for real-time monitoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.2 Installation&lt;/strong&gt;&lt;br&gt;
sudo apt-get install -y kibana&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.3 Configuration&lt;/strong&gt;&lt;br&gt;
Edit /etc/kibana/kibana.yml:&lt;br&gt;
Bind to all interfaces for external access&lt;br&gt;
server.host: "0.0.0.0"&lt;/p&gt;

&lt;p&gt;Connect to Elasticsearch over plain HTTP&lt;br&gt;
elasticsearch.hosts: ["&lt;a href="http://localhost:9200%22" rel="noopener noreferrer"&gt;http://localhost:9200"&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;Critical configuration points:&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Common pitfall:&lt;/strong&gt; If Elasticsearch has xpack.security.http.ssl.enabled: false but Kibana is configured with https:// in elasticsearch.hosts, Kibana will fail to connect with Unable to retrieve version information from Elasticsearch. Always match the protocol.&lt;/p&gt;

&lt;p&gt;Settings to remove or comment out when SSL is disabled:&lt;br&gt;
Comment out or remove these lines&lt;br&gt;
elasticsearch.ssl.certificateAuthorities: [/path/to/ca.crt]&lt;br&gt;
elasticsearch.username: "kibana_system"&lt;br&gt;
elasticsearch.password: "pass"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.4 Start and Enable&lt;/strong&gt;&lt;br&gt;
sudo systemctl daemon-reload&lt;br&gt;
sudo systemctl enable kibana&lt;br&gt;
sudo systemctl start kibana&lt;/p&gt;

&lt;p&gt;Kibana takes 30–60 seconds to fully initialize.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.5 Verify&lt;/strong&gt;&lt;br&gt;
curl &lt;a href="http://localhost:5601/api/status" rel="noopener noreferrer"&gt;http://localhost:5601/api/status&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Expected: {"status":{"overall":{"level":"available"}}} - the level should be available, not unavailable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4.6 Create a Data View&lt;/strong&gt;&lt;br&gt;
Once data is flowing, create a data view so Kibana knows which indices to query:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open http://:5601 in a browser.&lt;/li&gt;
&lt;li&gt;Navigate to Stack Management &amp;gt; Data Views (under Kibana section).&lt;/li&gt;
&lt;li&gt;Click Create data view.&lt;/li&gt;
&lt;li&gt;Fill in the fields:&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Now go to Discover (under Analytics) to search and explore your logs.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;4.7 AWS Security Group&lt;/strong&gt;&lt;br&gt;
If running on AWS EC2, ensure the security group allows inbound traffic:&lt;/p&gt;

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

&lt;p&gt;Never expose port 9200 to the public internet unless Elasticsearch security is enabled with TLS and authentication.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Logs are the immune system of your infrastructure - they tell you when something is wrong before it becomes a crisis.” - DevOps principle&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  5. Stage 4: Filebeat - The Lightweight Log Shipper
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;5.1 What is Filebeat?&lt;/strong&gt;&lt;br&gt;
Filebeat is a lightweight log shipper that runs on the application server. It tails log files, handles log rotation, tracks read positions (so it never sends duplicate lines), and ships logs to Logstash or Elasticsearch with minimal resource overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.2 Why Filebeat instead of sending directly to Logstash?&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Filebeat decouples your application from the logging pipeline. If Logstash or Elasticsearch goes down, Filebeat queues events and retries automatically. Your application keeps writing to its log file without interruption.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.3 Installation (on the Application Server)&lt;/strong&gt;&lt;br&gt;
Use the same Elastic repository&lt;br&gt;
sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install -y filebeat&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.4 Configuration&lt;/strong&gt;&lt;br&gt;
Edit /etc/filebeat/filebeat.yml:&lt;br&gt;
filebeat.inputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;type: log
enabled: true
paths:

&lt;ul&gt;
&lt;li&gt;/home/ubuntu/Boardgame/target/app.log
multiline.pattern: '^\d{4}-\d{2}-\d{2}'
multiline.negate: true
multiline.match: after&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;output.logstash:&lt;br&gt;
  hosts: [":5044"]&lt;/p&gt;

&lt;p&gt;processors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add_host_metadata:
  when.not.contains.tags: forwarded&lt;/li&gt;
&lt;li&gt;add_cloud_metadata: ~&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;logging.level: info&lt;/p&gt;

&lt;p&gt;Configuration explained:&lt;br&gt;
Input section:&lt;/p&gt;

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

&lt;p&gt;Multiline settings (critical for Java/Spring Boot stack traces):&lt;/p&gt;

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

&lt;p&gt;This ensures that a multi-line Java stack trace is treated as a single log event, not dozens of separate lines:&lt;br&gt;
2026-03-11 06:37:55 ERROR Something went wrong&lt;br&gt;
java.lang.NullPointerException&lt;br&gt;
    at com.example.Service.process(Service.java:42)&lt;br&gt;
    at com.example.Controller.handle(Controller.java:15)&lt;br&gt;
Without multiline config, each line of the stack trace becomes a separate Elasticsearch document - making it impossible to correlate errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output section:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Processors:&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;YAML formatting rules (common source of errors):&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;5.5 Start and Enable&lt;/strong&gt;&lt;br&gt;
Clear old registry to re-read files from the beginning&lt;br&gt;
sudo rm -rf /var/lib/filebeat/registry&lt;/p&gt;

&lt;p&gt;sudo systemctl daemon-reload&lt;br&gt;
sudo systemctl enable filebeat&lt;br&gt;
sudo systemctl start filebeat&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.6 Verify&lt;/strong&gt;&lt;br&gt;
Check Filebeat status&lt;br&gt;
sudo systemctl status filebeat&lt;/p&gt;

&lt;p&gt;Check logs - look for "Harvester started"&lt;br&gt;
sudo journalctl -u filebeat --no-pager -n 20&lt;br&gt;
Key indicators in the logs:&lt;/p&gt;

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

&lt;h2&gt;
  
  
  6. Connecting the Pieces - End-to-End Data Flow
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;6.1 The Complete Pipeline&lt;/strong&gt;&lt;br&gt;
[Spring Boot App]&lt;br&gt;
       │&lt;br&gt;
       │ writes logs to disk&lt;br&gt;
       ▼&lt;br&gt;
[/home/ubuntu/Boardgame/target/app.log]&lt;br&gt;
       │&lt;br&gt;
       │ Filebeat tails the file&lt;br&gt;
       ▼&lt;br&gt;
[Filebeat] ──── port 5044 ────► [Logstash]&lt;br&gt;
                                     │&lt;br&gt;
                                     │ grok filter parses log lines&lt;br&gt;
                                     ▼&lt;br&gt;
                              [Elasticsearch]&lt;br&gt;
                              index: boardgame-logs-2026.03.11&lt;br&gt;
                                     │&lt;br&gt;
                                     │ Kibana queries the index&lt;br&gt;
                                     ▼&lt;br&gt;
                                 [Kibana]&lt;br&gt;
                              http://:5601&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;6.2 Verifying Each Link&lt;/strong&gt;&lt;br&gt;
Test each component in order, from bottom to top:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Elasticsearch is running and accessible&lt;br&gt;
curl &lt;a href="http://localhost:9200" rel="noopener noreferrer"&gt;http://localhost:9200&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Logstash is listening for Beats input&lt;br&gt;
sudo ss -tlnp | grep 5044&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kibana can reach Elasticsearch&lt;br&gt;
curl &lt;a href="http://localhost:5601/api/status" rel="noopener noreferrer"&gt;http://localhost:5601/api/status&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Filebeat can reach Logstash (from application server)&lt;br&gt;
telnet  5044&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Data is actually in Elasticsearch&lt;br&gt;
curl &lt;a href="http://localhost:9200/_cat/indices?v" rel="noopener noreferrer"&gt;http://localhost:9200/_cat/indices?v&lt;/a&gt; | grep boardgame&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check document count&lt;br&gt;
curl &lt;a href="http://localhost:9200/boardgame-logs-*/_count" rel="noopener noreferrer"&gt;http://localhost:9200/boardgame-logs-*/_count&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  7. Practical Setup (Step-by-step)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;7.1 Server Requirements&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;7.2 ELK Server Setup (in order)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Step 1: Add Elastic repository&lt;br&gt;
wget -qO - &lt;a href="https://artifacts.elastic.co/GPG-KEY-elasticsearch" rel="noopener noreferrer"&gt;https://artifacts.elastic.co/GPG-KEY-elasticsearch&lt;/a&gt; | sudo gpg --dearmor -o /usr/share/keyrings/elasticsearch-keyring.gpg&lt;br&gt;
echo "deb [signed-by=/usr/share/keyrings/elasticsearch-keyring.gpg] &lt;a href="https://artifacts.elastic.co/packages/8.x/apt" rel="noopener noreferrer"&gt;https://artifacts.elastic.co/packages/8.x/apt&lt;/a&gt; stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list&lt;br&gt;
sudo apt-get update&lt;/p&gt;

&lt;p&gt;Step 2: Install all three components&lt;br&gt;
sudo apt-get install -y elasticsearch logstash kibana&lt;/p&gt;

&lt;p&gt;Step 3: Configure Elasticsearch&lt;br&gt;
sudo nano /etc/elasticsearch/elasticsearch.yml&lt;br&gt;
Set: network.host: 0.0.0.0&lt;br&gt;
Set: discovery.type: single-node&lt;br&gt;
Set: xpack.security.enabled: false&lt;br&gt;
Set: xpack.security.http.ssl.enabled: false&lt;br&gt;
Set: xpack.security.transport.ssl.enabled: false&lt;/p&gt;

&lt;p&gt;Step 4: Configure Logstash pipeline&lt;br&gt;
sudo nano /etc/logstash/conf.d/boardgame.conf&lt;br&gt;
Add input (beats, port 5044), filter (grok), output (elasticsearch)&lt;/p&gt;

&lt;p&gt;Step 5: Configure Kibana&lt;br&gt;
sudo nano /etc/kibana/kibana.yml&lt;br&gt;
Set: server.host: "0.0.0.0"&lt;br&gt;
Set: elasticsearch.hosts: ["&lt;a href="http://localhost:9200%22" rel="noopener noreferrer"&gt;http://localhost:9200"&lt;/a&gt;]&lt;br&gt;
Remove/comment any SSL certificate lines&lt;/p&gt;

&lt;p&gt;Step 6: Start services&lt;br&gt;
sudo systemctl enable elasticsearch logstash kibana&lt;br&gt;
sudo systemctl start elasticsearch logstash kibana&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7.3 Application Server Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Step 1: Install Filebeat&lt;br&gt;
sudo apt-get install -y filebeat&lt;/p&gt;

&lt;p&gt;Step 2: Configure Filebeat&lt;br&gt;
sudo nano /etc/filebeat/filebeat.yml&lt;br&gt;
Replace entire file with clean config (see Section 6.4)&lt;/p&gt;

&lt;p&gt;Step 3: Clear registry and start&lt;br&gt;
sudo rm -rf /var/lib/filebeat/registry&lt;br&gt;
sudo systemctl enable filebeat&lt;br&gt;
sudo systemctl start filebeat&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7.4 Kibana Data View Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Verify data arrived in Elasticsearch&lt;br&gt;
curl http://:9200/_cat/indices?v | grep boardgame&lt;br&gt;
Then in the Kibana UI: 1. Stack Management &amp;gt; Data Views &amp;gt; Create data view 2. Name: Boardgame Logs, Index pattern: boardgame-logs-*, Timestamp: @timestamp 3. Save data view to Kibana 4. Go to Discover to explore your logs&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7.5 Optional: Index Lifecycle Management&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For production, configure automatic index cleanup to prevent disk from filling up:&lt;br&gt;
Create an ILM policy that deletes indices older than 30 days&lt;br&gt;
curl -X PUT "&lt;a href="http://localhost:9200/_ilm/policy/boardgame-logs-policy" rel="noopener noreferrer"&gt;http://localhost:9200/_ilm/policy/boardgame-logs-policy&lt;/a&gt;" -H 'Content-Type: application/json' -d'&lt;br&gt;
{&lt;br&gt;
  "policy": {&lt;br&gt;
    "phases": {&lt;br&gt;
      "hot": {&lt;br&gt;
        "actions": {&lt;br&gt;
          "rollover": {&lt;br&gt;
            "max_size": "5gb",&lt;br&gt;
            "max_age": "1d"&lt;br&gt;
          }&lt;br&gt;
        }&lt;br&gt;
      },&lt;br&gt;
      "delete": {&lt;br&gt;
        "min_age": "30d",&lt;br&gt;
        "actions": {&lt;br&gt;
          "delete": {}&lt;br&gt;
        }&lt;br&gt;
      }&lt;br&gt;
    }&lt;br&gt;
  }&lt;br&gt;
}'&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Troubleshooting Common Issues
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;8.1 Kibana shows “Unable to retrieve version information from Elasticsearch”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cause: Protocol mismatch - Kibana is using https:// but Elasticsearch has SSL disabled.&lt;br&gt;
Fix:&lt;br&gt;
Check current Kibana config&lt;br&gt;
sudo grep "elasticsearch.hosts" /etc/kibana/kibana.yml&lt;/p&gt;

&lt;p&gt;Fix: change https to http&lt;br&gt;
elasticsearch.hosts: ["&lt;a href="http://localhost:9200%22" rel="noopener noreferrer"&gt;http://localhost:9200"&lt;/a&gt;]&lt;/p&gt;

&lt;p&gt;Comment out any SSL certificate lines&lt;br&gt;
elasticsearch.ssl.certificateAuthorities: [...]&lt;/p&gt;

&lt;p&gt;sudo systemctl restart kibana&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8.2 Filebeat shows harvester.open_files: 0&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cause: Filebeat config is malformed (duplicate sections, wrong indentation, or double-quoted regex).&lt;br&gt;
Fix: - Ensure filebeat.yml has no duplicate top-level keys. - Use single quotes for multiline.pattern (YAML treats \d in double quotes as an escape sequence). - Top-level keys (filebeat.inputs:, output.logstash:, processors:) must start at column 0.&lt;br&gt;
Validate the config&lt;br&gt;
sudo filebeat test config -c /etc/filebeat/filebeat.yml&lt;/p&gt;

&lt;p&gt;Clear registry and restart&lt;br&gt;
sudo rm -rf /var/lib/filebeat/registry&lt;br&gt;
sudo systemctl restart filebeat&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8.3 No boardgame-logs-* indices in Elasticsearch&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cause: Filebeat cannot reach Logstash on port 5044.&lt;br&gt;
Fix:&lt;br&gt;
From the application server, test connectivity&lt;br&gt;
telnet  5044&lt;/p&gt;

&lt;p&gt;If connection refused - open port 5044 in the ELK server's security group&lt;br&gt;
If connection times out - check if Logstash is running&lt;br&gt;
sudo systemctl status logstash&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/..." class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/..." alt="Uploading image" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8.4 Kibana Data View shows “No data streams, indices, or index aliases match”&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cause: No data has been ingested yet, or the index pattern is wrong.&lt;br&gt;
Fix:&lt;br&gt;
List all indices&lt;br&gt;
curl &lt;a href="http://localhost:9200/_cat/indices?v" rel="noopener noreferrer"&gt;http://localhost:9200/_cat/indices?v&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check the exact index name and match your pattern accordingly&lt;br&gt;
If index is "boardgame-logs-2026.03.11", pattern should be "boardgame-logs-*"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8.5 Logstash is running but not receiving data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cause: Logstash pipeline failed to start, or the config file has syntax errors.&lt;br&gt;
Fix:&lt;br&gt;
Test the Logstash config&lt;br&gt;
sudo /usr/share/logstash/bin/logstash --config.test_and_exit -f /etc/logstash/conf.d/&lt;/p&gt;

&lt;p&gt;Check Logstash logs&lt;br&gt;
sudo journalctl -u logstash --no-pager -n 30&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“You can’t manage what you can’t measure.” - Peter Drucker&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  9. FAQs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. Why use the ELK stack instead of cloud-native logging (CloudWatch, Stackdriver)?&lt;/strong&gt; &lt;br&gt;
ELK gives you full control over data retention, parsing rules, and costs. Cloud logging services charge per GB ingested, which becomes expensive at scale. ELK is free (open-source) - you only pay for the infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. Why Filebeat instead of sending logs directly from the application?&lt;/strong&gt; &lt;br&gt;
Filebeat decouples your application from the logging pipeline. If Logstash or Elasticsearch goes down, Filebeat queues events and retries. Your application keeps running without blocking on log delivery. Filebeat also uses ~10–30 MB RAM versus Logstash’s 500 MB+, making it ideal for application servers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. Can I skip Logstash and send Filebeat directly to Elasticsearch?&lt;/strong&gt; &lt;br&gt;
Yes. Set output.elasticsearch instead of output.logstash in Filebeat. However, you lose the ability to parse and transform logs with grok filters. For simple use cases (no parsing needed), direct shipping is fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. Why daily indices (boardgame-logs-2026.03.11) instead of a single index?&lt;/strong&gt; &lt;br&gt;
Daily indices enable simple retention management (delete indices older than N days), improve search performance (Elasticsearch skips irrelevant time ranges), and make index management operations (backup, restore) more granular.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q5. How much disk space does the ELK stack need?&lt;/strong&gt; &lt;br&gt;
Rough estimate: 1 GB of raw logs produces ~1.5–2 GB of Elasticsearch data (due to indexing overhead). For 100 MB/day of logs with 30-day retention, budget ~6 GB for Elasticsearch data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q6. Why must multiline.pattern use single quotes in YAML?&lt;/strong&gt; &lt;br&gt;
YAML interprets backslash sequences in double-quoted strings (\d becomes an invalid escape). Single quotes treat the content literally, preserving the regex pattern for Filebeat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q7. How do I add more application servers?&lt;/strong&gt; &lt;br&gt;
Install Filebeat on each server, point it to the same Logstash endpoint. Add a fields section to distinguish servers:&lt;br&gt;
filebeat.inputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;type: log
enabled: true
paths:

&lt;ul&gt;
&lt;li&gt;/var/log/myapp/*.log
fields:
server_name: web-02
environment: production&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Q8. Is this setup production-ready as described?&lt;/strong&gt; &lt;br&gt;
For internal use, yes. For public-facing production, enable Elasticsearch security (xpack.security.enabled: true), use TLS certificates for all inter-component communication, put Kibana behind a reverse proxy with authentication, and configure Index Lifecycle Management for automatic cleanup.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Centralized logging transforms debugging from “SSH into each server and grep” to “search once, find everywhere.”&lt;/li&gt;
&lt;li&gt;Filebeat belongs on the application server, not the ELK server. It is lightweight (~10–30 MB RAM) and handles backpressure gracefully.&lt;/li&gt;
&lt;li&gt;Logstash’s grok filter turns unstructured log lines into structured, searchable fields (timestamp, loglevel, logmessage).&lt;/li&gt;
&lt;li&gt;Protocol mismatch (https:// vs http://) between Kibana and Elasticsearch is the most common setup failure. Always match the protocol to Elasticsearch’s actual SSL configuration.&lt;/li&gt;
&lt;li&gt;YAML formatting causes most Filebeat config errors - use single quotes for regex, no duplicate keys, no leading spaces on top-level keys.&lt;/li&gt;
&lt;li&gt;Daily indices (boardgame-logs-%{+YYYY.MM.dd}) simplify retention management and improve query performance.&lt;/li&gt;
&lt;li&gt;Security is not optional in production - enable xpack.security, use TLS, and restrict port access via security groups.&lt;/li&gt;
&lt;li&gt;Test connectivity bottom-up: Elasticsearch first, then Logstash, then Kibana, then Filebeat.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  11. Conclusion
&lt;/h2&gt;

&lt;p&gt;Setting up the ELK stack is not just about installing four packages - it is about building a reliable data pipeline that turns scattered log files into searchable, visualizable insights. Each component has a clear role: Filebeat ships, Logstash transforms, Elasticsearch stores, and Kibana visualizes.&lt;/p&gt;

&lt;p&gt;The most common failures are not in the software itself, but in the configuration glue between components: protocol mismatches between Kibana and Elasticsearch, YAML formatting errors in Filebeat, unopened firewall ports between servers, and missing runtime dependencies.&lt;/p&gt;

&lt;p&gt;By following the step-by-step approach in this guide - installing bottom-up (Elasticsearch → Logstash → Kibana → Filebeat), verifying each component before moving to the next, and understanding why each configuration setting exists - you can set up a production-grade centralized logging system that scales from a single application to dozens of services.&lt;/p&gt;

&lt;p&gt;Once the pipeline is flowing, the real value begins: building dashboards for error rates, setting up alerts for anomalies, and turning your logs from an afterthought into your first line of defense.&lt;/p&gt;

&lt;p&gt;About the Author:&lt;em&gt;Rajan is a DevOps Engineer at &lt;a href="https://www.addwebsolution.com/" rel="noopener noreferrer"&gt;AddWebSolution&lt;/a&gt;, specializing in automation infrastructure, Optimize the CI/CD Pipelines and ensuring seamless deployments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>elk</category>
      <category>elasticsearch</category>
      <category>logstash</category>
      <category>kibana</category>
    </item>
    <item>
      <title>Nginx + PHP + MySQL Optimisations and Parameter Calculations</title>
      <dc:creator>Narendra Chauhan</dc:creator>
      <pubDate>Fri, 03 Apr 2026 06:38:00 +0000</pubDate>
      <link>https://dev.to/addwebsolutionpvtltd/nginx-php-mysql-optimisations-and-parameter-calculations-3min</link>
      <guid>https://dev.to/addwebsolutionpvtltd/nginx-php-mysql-optimisations-and-parameter-calculations-3min</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“Premature optimization is the root of all evil but lack of optimisation is the root of outages.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Architecture Overview&lt;/li&gt;
&lt;li&gt;Why Optimisation Is Required&lt;/li&gt;
&lt;li&gt;Nginx Optimisations &amp;amp; Parameter Calculations&lt;/li&gt;
&lt;li&gt;PHP-FPM Optimisations &amp;amp; Parameter Calculations&lt;/li&gt;
&lt;li&gt;MySQL Optimisations &amp;amp; Parameter Calculations&lt;/li&gt;
&lt;li&gt;System-Level Optimisations (Linux)&lt;/li&gt;
&lt;li&gt;Practical Example: Small vs Medium vs Large Server&lt;/li&gt;
&lt;li&gt;Interesting Facts &amp;amp; Statistics&lt;/li&gt;
&lt;li&gt;FAQs&lt;/li&gt;
&lt;li&gt;Key Takeaways&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Modern web applications rely heavily on the Nginx + PHP + MySQL (LEMP) stack. While default configurations work for testing, they are not suitable for production traffic.&lt;/p&gt;

&lt;p&gt;Optimisation ensures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster page load time&lt;/li&gt;
&lt;li&gt;Better concurrency handling&lt;/li&gt;
&lt;li&gt;Lower memory and CPU usage&lt;/li&gt;
&lt;li&gt;Higher stability under load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This document explains what to optimise, why to optimise, and how to calculate parameters practically.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Architecture Overview
&lt;/h2&gt;

&lt;p&gt;A typical request flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client sends HTTP request&lt;/li&gt;
&lt;li&gt;Nginx handles connection &amp;amp; static content&lt;/li&gt;
&lt;li&gt;PHP-FPM processes dynamic PHP requests&lt;/li&gt;
&lt;li&gt;MySQL serves data from database&lt;/li&gt;
&lt;li&gt;Response sent back to client&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each layer must be tuned together, not individually.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Why Optimisation Is Required
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Default settings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are conservative&lt;/li&gt;
&lt;li&gt;Waste available RAM&lt;/li&gt;
&lt;li&gt;Limit concurrency&lt;/li&gt;
&lt;li&gt;Cause slow response under load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Common Problems Without Optimisation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;502 / 504 Gateway errors&lt;/li&gt;
&lt;li&gt;High CPU load&lt;/li&gt;
&lt;li&gt;PHP-FPM “server reached max_children”&lt;/li&gt;
&lt;li&gt;MySQL “Too many connections”&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Nginx Optimisations &amp;amp; Parameter Calculations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Key Nginx Parameters&lt;/strong&gt;&lt;br&gt;
worker_processes auto;&lt;br&gt;
worker_connections 4096;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is worker_processes&lt;/strong&gt;&lt;br&gt;
Number of worker processes&lt;br&gt;
Best practice: match CPU cores&lt;/p&gt;

&lt;p&gt;&lt;code&gt;nproc&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
4 CPU cores → worker_processes 4;&lt;br&gt;
What is worker_connections&lt;br&gt;
Maximum connections per worker&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Total Max Connections&lt;/strong&gt;&lt;br&gt;
worker_processes × worker_connections&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;4 × 4096 = 16,384 concurrent connections&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended Extra Optimisations&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use epoll;
multi_accept on;

sendfile on;
tcp_nopush on;
tcp_nodelay on;

keepalive_timeout 65;
keepalive_requests 1000;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. PHP-FPM Optimisations &amp;amp; Parameter Calculations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Key PHP-FPM Settings&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pm = dynamic
pm.max_children = 20
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How to Calculate pm.max_children&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find average PHP process memory:
ps -ylC php-fpm --sort:rss&lt;/li&gt;
&lt;li&gt;Formula:
Available RAM for PHP / Avg PHP process size&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Available RAM: 2 GB&lt;/li&gt;
&lt;li&gt;Avg PHP process: 100 MB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2048 / 100 ≈ 20&lt;br&gt;
pm.max_children = 20&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other Important PHP Optimisations&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;request_terminate_timeout = 60
max_execution_time = 60
memory_limit = 256M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Enable OPcache&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;opcache.enable=1
opcache.memory_consumption=256
opcache.max_accelerated_files=20000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. MySQL Optimisations &amp;amp; Parameter Calculations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Key MySQL Parameters&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;innodb_buffer_pool_size = 2G
innodb_buffer_pool_instances = 2
max_connections = 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How to Calculate innodb_buffer_pool_size&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allocate 60–70% of total RAM (dedicated DB server)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server RAM: 4 GB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4 × 70% ≈ 2.8 GB&lt;br&gt;
Use 2G or 3G&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connection Calculation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional Recommended Settings&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;query_cache_type = 0
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. System-Level Optimisations (Linux)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;File Descriptors&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;ulimit -n 100000&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kernel Tuning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;net.core.somaxconn = 65535&lt;br&gt;
net.ipv4.tcp_max_syn_backlog = 65535&lt;br&gt;
vm.swappiness = 10&lt;/p&gt;
&lt;h2&gt;
  
  
  8. Practical Server Size Examples
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Small Server (2 CPU / 2 GB RAM)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nginx workers: 2&lt;/li&gt;
&lt;li&gt;worker_connections: 2048&lt;/li&gt;
&lt;li&gt;PHP max_children: 10&lt;/li&gt;
&lt;li&gt;MySQL buffer pool: 1G&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Medium Server (4 CPU / 8 GB RAM)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nginx workers: 4&lt;/li&gt;
&lt;li&gt;worker_connections: 4096&lt;/li&gt;
&lt;li&gt;PHP max_children: 30–40&lt;/li&gt;
&lt;li&gt;MySQL buffer pool: 4–5G&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Large Server (8 CPU / 16 GB RAM)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nginx workers: 8&lt;/li&gt;
&lt;li&gt;worker_connections: 8192&lt;/li&gt;
&lt;li&gt;PHP max_children: 60–80&lt;/li&gt;
&lt;li&gt;MySQL buffer pool: 10–12G&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Practical Demonstration (Images Explained)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1. NGINX OPTIMISATION&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parameter Calculations&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Step 1.1  Backup current nginx.conf&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 1.2  Check current config&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  cat /etc/nginx/nginx.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Screenshot: Current state before changes&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 1.3  Edit nginx.conf&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/nginx/nginx.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Replace/update with this optimized config:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user www-data;
  worker_processes 2;                    # = nproc (2 cores)
  worker_rlimit_nofile 65535;
  pid /run/nginx.pid;
  include /etc/nginx/modules-enabled/*.conf;

  events {
      worker_connections 1024;           # 2 x 1024 = 2048 total connections
      use epoll;                         # Linux best event model
      multi_accept on;                   # Accept multiple connections at once
  }

  http {

      # Basic Settings
      sendfile on;
      tcp_nopush on;
      tcp_nodelay on;
      keepalive_timeout 30;              # Reduced from default 75s
      keepalive_requests 100;
      types_hash_max_size 2048;
      server_tokens off;                 # Hide nginx version

      client_max_body_size 20m;
      client_body_buffer_size 128k;
      client_header_buffer_size 1k;
      large_client_header_buffers 4 8k;
      client_body_timeout 12;
      client_header_timeout 12;
      send_timeout 10;

      include /etc/nginx/mime.types;
      default_type application/octet-stream;

      # Logging Settings
      access_log /var/log/nginx/access.log;
      error_log /var/log/nginx/error.log warn;   # Only warn+ to reduce I/O

      # Gzip Settings
      gzip on;
      gzip_vary on;
      gzip_proxied any;
      gzip_comp_level 3;                 # Level 3 = good ratio, low CPU
      gzip_min_length 1024;              # Don't compress tiny files
      gzip_buffers 16 8k;
      gzip_http_version 1.1;
      gzip_types
          text/plain
          text/css
          text/javascript
          application/javascript
          application/json
          application/xml
          image/svg+xml
          font/woff2;


      # Open File Cache
      open_file_cache max=1000 inactive=20s;
      open_file_cache_valid 30s;
      open_file_cache_min_uses 2;
      open_file_cache_errors on;

      # FastCGI Cache (optional - enable per site)
      fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=PHPCACHE:10m
                         max_size=100m inactive=60m use_temp_path=off;

      include /etc/nginx/conf.d/*.conf;
      include /etc/nginx/sites-enabled/*;
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 1.4  Create FastCGI cache directory&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; sudo mkdir -p /var/cache/nginx
  sudo chown www-data:www-data /var/cache/nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 1.5  Test and reload Nginx&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nginx -t
  sudo systemctl reload nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Screenshot: nginx -t showing syntax is ok and test is successful&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 1.6  Verify Nginx is running with new settings&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nginx -T | grep -E "worker_processes|worker_connections|gzip|keepalive_timeout"
systemctl status nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Screenshot: Running status + key parameters&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 2. PHP-FPM OPTIMISATION&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parameter Calculations&lt;/strong&gt;&lt;br&gt;
Available RAM for PHP-FPM: ~150MB (conservative, leaving room for MySQL + Nginx)&lt;br&gt;
  Average PHP-FPM process size: ~30-40MB&lt;br&gt;
  Formula: pm.max_children = 150 / 35 ≈ 4&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PHP-FPM Parameters Calculation&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;pm&lt;/strong&gt;&lt;br&gt;
 Formula: Dynamic (best for variable traffic)&lt;br&gt;
 Value: dynamic&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pm.max_children&lt;/strong&gt;&lt;br&gt;
 Formula: 150MB ÷ 35MB/process&lt;br&gt;
 Value: 4&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pm.start_servers&lt;/strong&gt;&lt;br&gt;
 Formula: pm.max_children / 2&lt;br&gt;
 Value: 2&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pm.min_spare_servers&lt;/strong&gt;&lt;br&gt;
 Formula: pm.start_servers / 2&lt;br&gt;
 Value: 1&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pm.max_spare_servers&lt;/strong&gt;&lt;br&gt;
 Formula: pm.start_servers&lt;br&gt;
 Value: 2&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pm.max_requests&lt;/strong&gt;&lt;br&gt;
 Formula: Prevent memory leaks&lt;br&gt;
 Value: 500&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2.1  Check PHP-FPM version path&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  php -v
  ls /etc/php/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2.2  Backup pool config&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo cp/etc/php/8.4/fpm/pool.d/www.conf/etc /php/8.4/fpm/pool.d/www.conf.bak
sudo cp /etc/php/8.4/fpm/php.ini /etc/php/8.4/fpm/php.ini.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2.3  Check current PHP process memory usage&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run this to see actual PHP-FPM process sizes
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ps aux | grep php-fpm | grep -v grep | awk '{sum += $6} END {print "Total RSS:", sum/1024, "MB"; print "Count:", NR; print "Avg per process:", sum/NR/1024, "MB"}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Screenshot:&lt;/strong&gt; Current process sizes.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 2.4  Edit PHP-FPM pool config&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/php/8.4/fpm/pool.d/www.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find and update these values (search with Ctrl+W in nano):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[www]
user = www-data
group = www-data

listen = /run/php/php8.4-fpm.sock
listen.owner = www-data
listen.group = www-data

pm = dynamic
pm.max_children = 4
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 2
pm.max_requests = 500     ;Restart workers after 500 requests (prevents memory leaks)
pm.process_idle_timeout = 10s

pm.status_path = /status  ; Enable FPM status page
ping.path = /ping

slowlog = /var/log/php8.4-fpm-slow.log
request_slowlog_timeout = 5s     ; Log requests taking &amp;gt; 5 seconds
security.limit_extensions = .php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2.5  Tune PHP OPcache&lt;/strong&gt;&lt;br&gt;
  &lt;strong&gt;sudo nano /etc/php/8.4/mods-available/opcache.ini&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;zend_extension=opcache

  ; Enable OPcache
  opcache.enable=1
  opcache.enable_cli=0

  ; Memory: 64MB for low-RAM server
  opcache.memory_consumption=64
  opcache.interned_strings_buffer=8
  opcache.max_accelerated_files=10000

  ; Production settings (set validate_timestamps=0 in prod)
  opcache.validate_timestamps=1
  opcache.revalidate_freq=60

  opcache.save_comments=1
  opcache.max_wasted_percentage=10
  opcache.use_cwd=1

  ; JIT (PHP 8.x feature)
  opcache.jit_buffer_size=32M
  opcache.jit=1255
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2.6  Tune PHP.ini key values&lt;/strong&gt;&lt;br&gt;
  &lt;strong&gt;sudo nano /etc/php/8.4/fpm/php.ini&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Find and update:
  ; Memory limit per PHP process
  memory_limit = 128M

  ; Upload/POST limits
  upload_max_filesize = 20M
  post_max_size = 25M
  max_execution_time = 60
  max_input_time = 60

  ; Error handling (production)
  display_errors = Off
  log_errors = On
  error_log = /var/log/php_errors.log

  ; Session handling
  session.gc_maxlifetime = 1440
  session.cookie_httponly = 1
  session.cookie_secure = 1

  ; Disable dangerous functions
  disable_functions = exec,passthru,shell_exec,system,proc_open,popen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2.7  Restart PHP-FPM and verify&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo php-fpm8.4 -t
  sudo systemctl restart php8.4-fpm
  sudo systemctl status php8.4-fpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Screenshot: PHP-FPM status showing active (running)&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 2.8  Verify OPcache is active&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php -r "var_dump(opcache_get_status());" | head -30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;- or check via CLI&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php -i | grep -E "opcache|OPcache"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Screenshot: OPcache enabled status&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 2.9  Check PHP-FPM processes after restart&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ps aux | grep php-fpm | grep -v grep
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;- Should show master + 2 worker processes (pm.start_servers=2)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Screenshot:&lt;/strong&gt; FPM process list&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 3.  MYSQL OPTIMISATION&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parameter Calculations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RAM budget for MySQL: ~200MB (out of 914MB total)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;innodb_buffer_pool_size&lt;/strong&gt;&lt;br&gt;
Formula: ~20% of RAM (shared server)&lt;br&gt;
Value: 192M&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;innodb_buffer_pool_instances&lt;/strong&gt;&lt;br&gt;
Formula: buffer_pool ÷ 128M&lt;br&gt;
Value: 1&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;max_connections&lt;/strong&gt;&lt;br&gt;
Formula: Low RAM = conservative&lt;br&gt;
Value: 50&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;innodb_log_file_size&lt;/strong&gt;&lt;br&gt;
Formula: 25% of buffer pool&lt;br&gt;
Value: 48M&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;tmp_table_size&lt;/strong&gt;&lt;br&gt;
Formula: Memory temporary tables&lt;br&gt;
Value: 16M&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;max_heap_table_size&lt;/strong&gt;&lt;br&gt;
 Formula: Same as tmp_table_size&lt;br&gt;
 Value: 16M&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;thread_cache_size&lt;/strong&gt;&lt;br&gt;
 Formula: Reuse threads&lt;br&gt;
 Value: 8&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;table_open_cache&lt;/strong&gt;&lt;br&gt;
 Formula: Open tables cache&lt;br&gt;
 Value: 400&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3.1  Check current MySQL config and status&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo cp /etc/mysql/mysql.conf.d/mysqld.cnf /etc/mysql/mysql.conf.d/mysqld.cnf.bak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;- Check current variables&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_buffer_pool%';"
  mysql -u root -p -e "SHOW VARIABLES LIKE 'max_connections';"
  mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Screenshot:&lt;/strong&gt; Current MySQL variables&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 3.2  Check actual MySQL memory usage&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysql -u root -p -e "
  SELECT
    ROUND(@@innodb_buffer_pool_size/1024/1024, 0) AS 'Buffer Pool MB',
    ROUND(@@key_buffer_size/1024/1024, 0) AS 'Key Buffer MB',
    @@max_connections AS 'Max Connections',
    @@thread_stack/1024 AS 'Thread Stack KB';
  "
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Screenshot:&lt;/strong&gt; Current MySQL memory config&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 3.3  Edit MySQL config&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add/update under [mysqld]:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[mysqld]
  pid-file        = /var/run/mysqld/mysqld.pid
  socket          = /var/run/mysqld/mysqld.sock
  datadir         = /var/lib/mysql
  log-error       = /var/log/mysql/error.log

  #
  # ===== MEMORY SETTINGS =====
  # Server has 914MB RAM - allocate ~200MB for MySQL
  #
  innodb_buffer_pool_size         = 192M    # Main InnoDB cache (most important!)
  innodb_buffer_pool_instances    = 1       # 1 instance (&amp;lt; 1GB pool)
  innodb_log_file_size            = 48M     # ~25% of buffer pool
  innodb_log_buffer_size          = 8M
  innodb_flush_log_at_trx_commit  = 2       # Slight risk, big perf gain (use 1 for strict ACID)

  #
  # ===== CONNECTION SETTINGS =====
  #
  max_connections         = 50             # Low RAM = keep connections limited
  thread_cache_size       = 8             # Reuse threads, avoid creation overhead
  wait_timeout            = 120           # Kill idle connections after 2 min
  interactive_timeout     = 120

  #
  # ===== QUERY CACHE (removed in MySQL 8.0, skip) =====
  # MySQL 8.0 removed query_cache - use ProxySQL or app-level cache

  #
  # ===== TABLE CACHE =====
  #
  table_open_cache        = 400
  table_definition_cache  = 400

  #
  # ===== TEMP TABLES =====
  #
  tmp_table_size          = 16M
  max_heap_table_size     = 16M

  #
  # ===== InnoDB I/O =====
  #
  innodb_file_per_table           = 1
  innodb_flush_method             = O_DIRECT  # Avoid double buffering with OS cache
  innodb_read_io_threads          = 2         # = CPU cores
  innodb_write_io_threads         = 2         # = CPU cores

  #
  # ===== SLOW QUERY LOG =====
  #
  slow_query_log          = 1
  slow_query_log_file     = /var/log/mysql/slow.log
  long_query_time         = 2             # Log queries &amp;gt; 2 seconds
  log_queries_not_using_indexes = 1

  #
  # ===== BINARY LOG (disable if not using replication) =====
  #
  # skip-log-bin                           # Uncomment if no replication needed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3.4  Validate and restart MySQL&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mysqld --validate-config
  sudo systemctl restart mysql
  sudo systemctl status mysql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Screenshot:&lt;/strong&gt; MySQL status active&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 3.5  Verify new MySQL variables&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysql -u root -p -e "
  SELECT 'innodb_buffer_pool_size' AS Variable,
         ROUND(@@innodb_buffer_pool_size/1024/1024,0) AS 'Value (MB)'
  UNION SELECT 'max_connections', @@max_connections
  UNION SELECT 'innodb_log_file_size MB', ROUND(@@innodb_log_file_size/1024/1024,0)
  UNION SELECT 'tmp_table_size MB', ROUND(@@tmp_table_size/1024/1024,0)
  UNION SELECT 'thread_cache_size', @@thread_cache_size;
  "
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Screenshot:&lt;/strong&gt; New values confirmed&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Step 3.6  Check MySQL memory usage post-restart&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  free -h
  ps aux | sort -k6 -rn | head -10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Screenshot:&lt;/strong&gt; Overall memory after all services tuned&lt;/p&gt;

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

&lt;blockquote&gt;
&lt;p&gt;“Measure first, tune second, and monitor always.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Step 4. FINAL VERIFICATION&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4.1  Check all services running&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  systemctl status nginx php8.4-fpm mysql --no-pager
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4.2  Full memory picture&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;free -h
  ps aux --sort=-%mem | head -15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4.3  Check nginx + PHP working together&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Test nginx config is still valid
  sudo nginx -t

 Test PHP-FPM socket exists
  ls -la /run/php/php8.4-fpm.sock

 Check FPM status (if you added status page to your site config)
  curl http://localhost/status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4.4  Check MySQL slow log is active&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mysql -u root -p -e "SHOW VARIABLES LIKE 'slow_query%';"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4.5  Final health summary&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;echo "=== NGINX ===" &amp;amp;&amp;amp; nginx -v &amp;amp;&amp;amp; systemctl is-active nginx
  echo "=== PHP-FPM ===" &amp;amp;&amp;amp; php -v | head -1 &amp;amp;&amp;amp; systemctl is-active php8.4-fpm
  echo "=== MYSQL ===" &amp;amp;&amp;amp; mysql --version &amp;amp;&amp;amp; systemctl is-active mysql
  echo "=== MEMORY ===" &amp;amp;&amp;amp; free -h
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Screenshot:&lt;/strong&gt; Final health check&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Your server is fully optimised. All phases verified:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Phase 0  Swap 2GB active&lt;/li&gt;
&lt;li&gt;  Phase 1  Nginx tuned&lt;/li&gt;
&lt;li&gt;  Phase 2  PHP-FPM + OPcache + JIT enabled&lt;/li&gt;
&lt;li&gt;  Phase 3  MySQL InnoDB / connections / slow query log active&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  9. Interesting Facts &amp;amp; Statistics
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1 second delay can reduce conversions by 7%&lt;/li&gt;
&lt;li&gt;OPcache can improve PHP performance by 2–3×&lt;/li&gt;
&lt;li&gt;MySQL buffer pool cache hit ratio above 99% is ideal&lt;/li&gt;
&lt;li&gt;Nginx handles 10× more concurrent connections than traditional servers&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  11. FAQs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q1. Should I optimise Nginx, PHP, or MySQL first?&lt;/strong&gt;&lt;br&gt;
Start with PHP-FPM and MySQL, then tune Nginx.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q2. Can wrong tuning crash the server?&lt;/strong&gt;&lt;br&gt;
Yes. Over-allocating RAM causes OOM kills.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q3. Are these values fixed forever?&lt;/strong&gt;&lt;br&gt;
No. Recalculate after traffic growth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q4. Do I need load testing?&lt;/strong&gt;&lt;br&gt;
Yes. Use tools like ab, wrk, or k6.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Optimisation is calculation-based, not guesswork&lt;/li&gt;
&lt;li&gt;PHP-FPM memory calculation is critical&lt;/li&gt;
&lt;li&gt;MySQL buffer pool has the biggest performance impact&lt;/li&gt;
&lt;li&gt;Nginx handles concurrency, not application logic&lt;/li&gt;
&lt;li&gt;Monitoring is mandatory after tuning&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  13. Conclusion
&lt;/h2&gt;

&lt;p&gt;Optimising Nginx + PHP + MySQL is not about copying configs from the internet—it is about understanding server resources, calculating limits, and balancing load across layers.&lt;br&gt;
&lt;strong&gt;A well-optimised stack:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles higher traffic&lt;/li&gt;
&lt;li&gt;Reduces downtime&lt;/li&gt;
&lt;li&gt;Improves user experience&lt;/li&gt;
&lt;li&gt;Saves infrastructure cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;About the Author:&lt;em&gt;Narendra is a DevOps Engineer at &lt;a href="https://www.addwebsolution.com/" rel="noopener noreferrer"&gt;AddWebSolution&lt;/a&gt;, specializing in automating infrastructure to improve efficiency and reliability.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>serveroptimization</category>
      <category>lempstack</category>
      <category>webperf</category>
      <category>nginxphpmysql</category>
    </item>
  </channel>
</rss>
