<?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: Samuel Owolabi</title>
    <description>The latest articles on DEV Community by Samuel Owolabi (@samowolabi).</description>
    <link>https://dev.to/samowolabi</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%2Fuser%2Fprofile_image%2F1420717%2Fba6747d0-f567-447d-be08-3f65aca8623a.webp</url>
      <title>DEV Community: Samuel Owolabi</title>
      <link>https://dev.to/samowolabi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/samowolabi"/>
    <language>en</language>
    <item>
      <title>Designing for Failure: Chaos Engineering Principles in System Design</title>
      <dc:creator>Samuel Owolabi</dc:creator>
      <pubDate>Sat, 02 May 2026 15:52:03 +0000</pubDate>
      <link>https://dev.to/samowolabi/designing-for-failure-chaos-engineering-principles-in-system-design-3jj1</link>
      <guid>https://dev.to/samowolabi/designing-for-failure-chaos-engineering-principles-in-system-design-3jj1</guid>
      <description>&lt;p&gt;A practical guide to chaos engineering principles that transform fragile architectures into resilient, self-healing systems.&lt;/p&gt;

&lt;p&gt;Recently, I wrote an article titled &lt;a href="https://dev.to/samowolabi/what-if-you-are-to-build-for-a-1000000-daily-active-users-jm"&gt;&lt;em&gt;“What if you are to build for one million daily active users?”&lt;/em&gt;&lt;/a&gt;. In that article, we explored a point where a monolithic system could no longer scale and began to break. We discussed scalability, availability, and observability, and why they become critical as systems grow. This article builds directly on that discussion.&lt;br&gt;
Here, the focus is designing for failure, what exactly is Chaos Engineering, how can we simulate chaos on our system, measure the impacts, and how to handle and mitigate failures on our system.&lt;/p&gt;

&lt;p&gt;The reality is that 100% uptime is not something you can realistically promise. What you &lt;em&gt;can&lt;/em&gt; design for is fault tolerance and resilient infrastructure. That difference matters.&lt;/p&gt;

&lt;p&gt;A simple way to understand this is the idea of &lt;strong&gt;a spare tire in your car&lt;/strong&gt;. You do not expect to have a flat tire every day, but you still keep a spare. You might even check occasionally to make sure it is still inflated. The reason is straightforward. The cost of being unprepared when failure happens, especially in a bad situation like a breakdown on a highway at night, is very high. I have experienced this before on Lekki, Lagos highway, and that night wasn’t funny (LOL).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Netflix&lt;/strong&gt; famously talked about building &lt;a href="https://netflixtechblog.com/the-netflix-simian-army-16e57fbab116" rel="noopener noreferrer"&gt;&lt;strong&gt;Chaos Monkey&lt;/strong&gt;&lt;/a&gt;, a tool that randomly terminates production instances. The goal was to force their systems to survive common types of failures before those failures happened unexpectedly. This is the core mindset behind Chaos Engineering.&lt;/p&gt;

&lt;p&gt;To design for failure, we must understand how the system behaves when failure inevitably happens. What is the cost? What is the impact? How do we mitigate it? How do we still maintain over 99% uptime? This requires treating failure as a default state, not an exception.&lt;/p&gt;


&lt;h2&gt;
  
  
  Core Principles of Chaos Engineering
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Define What “Normal” Looks Like
&lt;/h3&gt;

&lt;p&gt;The first step is defining steady-state behavior. Without this, there is no baseline to measure against.&lt;br&gt;
Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;P95 latency less than 300 milliseconds&lt;/li&gt;
&lt;li&gt;Error rate below 0.5%&lt;/li&gt;
&lt;li&gt;Successful payment completion rate above 99.9%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These metrics represent what healthy behavior means for your system.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Form a Hypothesis
&lt;/h3&gt;

&lt;p&gt;Once normal behavior is defined, you form a hypothesis.&lt;br&gt;
The idea is simple: if a specific failure happens, the system should still be able to perform a critical function.&lt;br&gt;
For example, if Service X fails, users should still be able to complete checkout within acceptable latency. This keeps chaos experiments intentional and measurable.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Introduce Realistic Failures
&lt;/h3&gt;

&lt;p&gt;Next, you introduce failures that closely resemble real-world incidents.&lt;br&gt;
Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Killing instances or Kubernetes pods&lt;/li&gt;
&lt;li&gt;Introducing network latency or packet loss&lt;/li&gt;
&lt;li&gt;Brute-forcing APIs to test rate limits&lt;/li&gt;
&lt;li&gt;Simulating DDoS-like traffic spikes&lt;/li&gt;
&lt;li&gt;Simulating DNS failures or dependency outages&lt;/li&gt;
&lt;li&gt;Testing third-party data mismatches or breaking changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are ensuring realism, not randomness.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Run Experiments in Production
&lt;/h3&gt;

&lt;p&gt;Chaos experiments are most valuable in production. This is where real traffic patterns, real user behavior, and real data shapes exist.&lt;br&gt;
That said, experiments must be controlled.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start small to limit blast radius.&lt;/li&gt;
&lt;li&gt;Run during business hours when teams are available.&lt;/li&gt;
&lt;li&gt;Have clear rollback strategies such as feature flags, traffic shifting, or instance replacement.&lt;/li&gt;
&lt;li&gt;Ensure strong monitoring and observability before running experiments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I want you to know that running chaos experiments &lt;strong&gt;without observability leads to outages, not learning.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Automate and Continuously Improve
&lt;/h3&gt;

&lt;p&gt;Chaos Engineering is not a one-off exercise.&lt;br&gt;
Systems evolve. Dependencies change. Teams rotate.&lt;br&gt;
Experiments should be automated, repeatable, and run continuously, either as scheduled jobs or integrated into CI/CD pipelines. Over time, experiments can be expanded to test higher-impact scenarios.&lt;br&gt;
The feedback loop remains consistent:&lt;br&gt;
Design → Build → Chaos Test → Iterate&lt;/p&gt;


&lt;h2&gt;
  
  
  Types of Failures to Design For and How to Address Them
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Section 1: Single Points of Failure
&lt;/h2&gt;

&lt;p&gt;A single point of failure is any component whose failure can bring down the entire system.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Database Failures
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; The primary database goes down.&lt;br&gt;
&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Primary–standby setup with synchronous replication and automatic failover&lt;/li&gt;
&lt;li&gt;Read replicas for distributing read queries (asynchronous replication)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional considerations include health checks, failover timing, and data consistency. Strong consistency simplifies reasoning but reduces availability. Eventual consistency improves availability but introduces complexity and potential inconsistency windows.&lt;br&gt;
&lt;strong&gt;Trade-offs:&lt;/strong&gt; Increased cost, operational complexity, and consistency challenges.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Cloud Server Outages
&lt;/h3&gt;

&lt;p&gt;Using a single availability zone creates unnecessary risk.&lt;br&gt;
&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multi-AZ architecture for high availability&lt;/li&gt;
&lt;li&gt;Multi-region architecture for disaster recovery&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; Higher latency, data replication complexity, and increased cost.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Application and Web Server Failures
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; API servers crash or become unhealthy.&lt;br&gt;
&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Horizontal scaling using Auto Scaling Groups&lt;/li&gt;
&lt;li&gt;Load balancer health checks and automatic deregistration&lt;/li&gt;
&lt;li&gt;Stateless application design&lt;/li&gt;
&lt;li&gt;External session storage or token-based session management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; More infrastructure and stricter architectural discipline.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Message Queue Failures
&lt;/h3&gt;

&lt;p&gt;Common failure modes include broker unavailability and consumer processing timeouts.&lt;br&gt;
&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dead-letter queues for failed messages&lt;/li&gt;
&lt;li&gt;Temporary fallback storage such as NoSQL databases&lt;/li&gt;
&lt;li&gt;Clustered queue setups with replication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; Increased operational overhead and reprocessing complexity.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. DNS Failures
&lt;/h3&gt;

&lt;p&gt;Relying on a single DNS provider or registrar is a common oversight.&lt;br&gt;
&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple DNS providers&lt;/li&gt;
&lt;li&gt;Secondary DNS configurations&lt;/li&gt;
&lt;li&gt;Anycast DNS&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  6. Third-Party API Failures
&lt;/h3&gt;

&lt;p&gt;Depending on a single third-party API for critical functionality is risky, especially in domains like fintech.&lt;br&gt;
&lt;strong&gt;Solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple providers&lt;/li&gt;
&lt;li&gt;Cached responses&lt;/li&gt;
&lt;li&gt;Graceful fallback data or degraded flows&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; Higher integration complexity and reconciliation challenges.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every solution discussed here introduces trade-offs. Designing for failure is not about eliminating risk entirely. It is about understanding where risk exists and making deliberate decisions about how much of it you are willing to accept.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Section 2: Network Failures
&lt;/h2&gt;

&lt;p&gt;Network failures are unavoidable in distributed systems. Latency spikes, packets get dropped, DNS fails, and sometimes the network splits entirely. Many system outages are not caused by servers crashing, but by slow or unreliable communication between otherwise healthy components.&lt;/p&gt;

&lt;p&gt;This is where several of the classic fallacies of distributed computing show up, especially the assumption that the network is reliable and has zero latency.&lt;/p&gt;
&lt;h3&gt;
  
  
  Common Network Behavior and Risk Pattern we often ignore
&lt;/h3&gt;
&lt;h3&gt;
  
  
  1. Are All Network Calls Protected with Timeouts?
&lt;/h3&gt;

&lt;p&gt;Timeouts are one of the most important and most frequently overlooked safeguards in distributed systems.&lt;/p&gt;

&lt;p&gt;A slow dependency is often worse than a failed one. Without timeouts, requests pile up, threads get exhausted, and failures spread to otherwise healthy services.&lt;br&gt;
Timeouts should exist at every layer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client-side requests&lt;/li&gt;
&lt;li&gt;API gateway timeouts&lt;/li&gt;
&lt;li&gt;Service-to-service calls&lt;/li&gt;
&lt;li&gt;Database query execution&lt;/li&gt;
&lt;li&gt;External API requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A practical rule of thumb is that timeouts should get shorter as requests move deeper into the system.&lt;br&gt;
Example timeout chain:&lt;br&gt;
&lt;strong&gt;Client (10s) → Gateway (8s) → Service (6s) → Database (4s)&lt;/strong&gt;&lt;br&gt;
Timeouts should also be monitored. A sudden increase often indicates upstream degradation long before a full outage occurs.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Do We Have Retry Logic with Exponential Backoff?
&lt;/h3&gt;

&lt;p&gt;Retries exist to handle transient failures such as brief network interruptions or temporary service unavailability.&lt;br&gt;
However, retries without limits or delays can make outages worse.&lt;br&gt;
It is important to distinguish between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transient failures, which may succeed on retry&lt;/li&gt;
&lt;li&gt;Permanent failures, which will not&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exponential backoff helps control retry behavior by increasing the wait time between attempts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Basic formula:
wait_time = base_delay × (2 ^ attempt_number)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This reduces pressure on failing services and prevents retry storms. Retry counts should always be capped, and retries should be combined with timeouts and circuit breakers.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Are Operations Idempotent?
&lt;/h3&gt;

&lt;p&gt;Idempotency means that performing the same operation multiple times produces the same result.&lt;br&gt;
This becomes critical when retries are involved. If a request is retried due to a timeout, the system must not process it twice in a harmful way.&lt;/p&gt;

&lt;p&gt;Common implementation strategies include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unique transaction or request IDs&lt;/li&gt;
&lt;li&gt;Idempotency keys stored with deduplication windows&lt;/li&gt;
&lt;li&gt;Server-side request tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Payment systems are a common example where idempotency is mandatory. Charging a user twice because of a retry is unacceptable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 3: Cascading Failures
&lt;/h2&gt;

&lt;p&gt;In complex systems, failures rarely stay isolated. A single degraded service can trigger failures in upstream services, which then overload others. This is how small issues turn into large outages.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Are Circuit Breakers in Place?
&lt;/h3&gt;

&lt;p&gt;Circuit breakers prevent repeated calls to failing dependencies.&lt;br&gt;
They typically operate in three states:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Closed: requests flow normally&lt;/li&gt;
&lt;li&gt;Open: requests are blocked&lt;/li&gt;
&lt;li&gt;Half-open: limited test requests are allowed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key configuration parameters include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Failure threshold, such as a 50% error rate&lt;/li&gt;
&lt;li&gt;Open-state timeout duration&lt;/li&gt;
&lt;li&gt;Number of test requests in half-open state&lt;/li&gt;
&lt;li&gt;Visibility into circuit breaker state through monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Circuit breakers protect systems by failing fast and giving dependencies time to recover.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Can One Service’s Failure Take Down the Entire System?
&lt;/h3&gt;

&lt;p&gt;This question reveals how tightly coupled a system is.&lt;br&gt;
Important steps include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identifying critical path services&lt;/li&gt;
&lt;li&gt;Mapping service dependencies&lt;/li&gt;
&lt;li&gt;Understanding which failures are acceptable and which are not&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Implementation strategies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer asynchronous communication where possible&lt;/li&gt;
&lt;li&gt;Use event-driven patterns to reduce tight coupling&lt;/li&gt;
&lt;li&gt;Provide fallback responses when dependencies fail&lt;/li&gt;
&lt;li&gt;Serve stale or cached data when appropriate&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chaos experiments are useful here. For example, intentionally disabling a downstream database and observing how the system behaves can reveal hidden coupling and unexpected dependencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 4: Data Consistency
&lt;/h2&gt;

&lt;p&gt;Distributed systems must make trade-offs between consistency, availability, and partition tolerance. Network partitions are not hypothetical events; they are guaranteed to happen at some point.&lt;br&gt;
This is where understanding consistency models becomes essential. The spectrum ranges from strong consistency, which is easier to reason about but less available, to eventual consistency, which improves availability but requires careful conflict handling.&lt;br&gt;
(This ties directly into the earlier discussion on &lt;a href="https://dev.to/samowolabi/strong-vs-eventual-consistency-in-system-design-52nd"&gt;strong vs eventual consistency. I wrote an article on this here&lt;/a&gt;, please check it out, you will gain deeper knowledge on consistency).&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Risk Pattern: Consistency Mismatch
&lt;/h3&gt;

&lt;h3&gt;
  
  
  1. Is the Consistency Model Appropriate for the Use Case?
&lt;/h3&gt;

&lt;p&gt;Different parts of a system often require different consistency guarantees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strong consistency&lt;/strong&gt; means that once a write succeeds, every subsequent read will immediately return that updated value. From the user’s point of view, the system behaves as if there is only one copy of the data. This makes the system easier to reason about, but it usually comes at the cost of higher latency and lower availability, especially during failures or network issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eventual consistency&lt;/strong&gt; means that after a write, different parts of the system may temporarily see different values, but given enough time and no new updates, all copies will converge to the same state. This model favors availability and performance, but it requires the system and sometimes the application to handle stale data and resolve conflicts.&lt;/p&gt;

&lt;p&gt;In practice, strong consistency optimizes for correctness and simplicity, while eventual consistency optimizes for availability and scale. Neither is universally better. The right choice depends on what the data represents and how critical immediate correctness is to the user experience.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Strong consistency is suitable for: Payments, Inventory updates, Account balances&lt;br&gt;
Eventual consistency works well for: Recommendations, Activity feeds, Analytics&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Hybrid approaches are common and often necessary.&lt;br&gt;
Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Strong consistency for checkout flows&lt;/li&gt;
&lt;li&gt;Eventual consistency for product recommendations&lt;/li&gt;
&lt;li&gt;Per-operation consistency levels&lt;/li&gt;
&lt;li&gt;Strong guarantees on critical paths, relaxed guarantees elsewhere&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. How Do We Handle Replication Lag?
&lt;/h3&gt;

&lt;p&gt;Replication lag occurs when followers fall behind leaders.&lt;br&gt;
Common causes include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Network latency&lt;/li&gt;
&lt;li&gt;High write throughput&lt;/li&gt;
&lt;li&gt;Slower follower nodes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lag becomes dangerous when users expect read-after-write consistency but do not receive it.&lt;br&gt;
Mitigation strategies include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reading from the leader immediately after writes&lt;/li&gt;
&lt;li&gt;Client-side caching of recent writes&lt;/li&gt;
&lt;li&gt;Versioning and monotonic reads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Replication lag should be measured and monitored. When lag grows beyond acceptable limits, it becomes a correctness issue, not just a performance concern.&lt;/p&gt;




&lt;h2&gt;
  
  
  Section 5: Recovery
&lt;/h2&gt;

&lt;p&gt;Failures will happen. What matters next is how quickly and safely the system recovers.&lt;br&gt;
This is where recovery metrics become practical tools rather than abstract concepts.&lt;br&gt;
Key metrics include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RTO (Recovery Time Objective): how long recovery can take&lt;/li&gt;
&lt;li&gt;RPO (Recovery Point Objective): how much data loss is acceptable&lt;/li&gt;
&lt;li&gt;MTTR (Mean Time to Recovery)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The business cost of downtime&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Risk Pattern: Slow or Manual Recovery
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. How Quickly Are Failures Detected?
&lt;/h3&gt;

&lt;p&gt;Detection speed directly affects recovery time.&lt;br&gt;
Effective detection relies on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Well-designed health checks&lt;/li&gt;
&lt;li&gt;Monitoring and alerting&lt;/li&gt;
&lt;li&gt;Observability across logs, metrics, and traces&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Examples include infrastructure and application monitoring tools, both within cloud providers and external platforms. Faster detection leads to faster mitigation.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Can the System Heal Itself?
&lt;/h3&gt;

&lt;p&gt;Self-healing reduces reliance on manual intervention.&lt;br&gt;
Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Auto Scaling Groups replacing failed instances&lt;/li&gt;
&lt;li&gt;Kubernetes restarting unhealthy containers&lt;/li&gt;
&lt;li&gt;Horizontal Pod Autoscaling&lt;/li&gt;
&lt;li&gt;Automated DNS failover&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Self-healing does not eliminate the need for humans, but it reduces response time significantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. What Is the Backup and Recovery Strategy?
&lt;/h3&gt;

&lt;p&gt;Backups exist to meet RPO and RTO goals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RPO (Recovery Point Objective)&lt;/strong&gt; answers the question:&lt;br&gt;
&lt;em&gt;How much data can we afford to lose?&lt;/em&gt;&lt;br&gt;
It defines the maximum acceptable amount of data loss measured in time.&lt;br&gt;
For example, an RPO of &lt;strong&gt;5 minutes&lt;/strong&gt; means losing up to 5 minutes of data is acceptable if a failure occurs. Anything beyond that is a problem.&lt;br&gt;
RPO directly influences how often you back up data and how replication is designed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RTO (Recovery Time Objective)&lt;/strong&gt; answers the question:&lt;br&gt;
&lt;em&gt;How long can the system be down?&lt;/em&gt;&lt;br&gt;
It defines the maximum acceptable time it should take to restore the system after a failure.&lt;br&gt;
For example, an RTO of &lt;strong&gt;30 minutes&lt;/strong&gt; means the system must be back online within 30 minutes of an outage.&lt;br&gt;
RTO affects automation, failover strategies, and disaster recovery architecture.&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%2Foue43d9xyhxj1sy1qvcr.jpg" 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%2Foue43d9xyhxj1sy1qvcr.jpg" alt="Image Credit goes to Stephane Maarek" width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;How much data loss is acceptable?&lt;/li&gt;
&lt;li&gt;How fast must recovery occur?&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Database snapshots&lt;/li&gt;
&lt;li&gt;File system backups&lt;/li&gt;
&lt;li&gt;Object storage versioning&lt;/li&gt;
&lt;li&gt;Backup frequency aligned with RPO requirements&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Bonus: Disaster Recovery Strategies
&lt;/h3&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%2Fwjyn7pt7vm3kv5jwza30.jpg" 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%2Fwjyn7pt7vm3kv5jwza30.jpg" alt="Image credit goes to Stephane Maarek" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Disaster recovery approaches vary by cost and recovery speed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Backup and Restore:&lt;/strong&gt; lowest cost, slowest recovery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pilot Light:&lt;/strong&gt; minimal services running, faster recovery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Warm Standby:&lt;/strong&gt; scaled-down but functional environment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hot Site / Multi-Site:&lt;/strong&gt; fastest recovery, highest cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing a strategy is a business decision informed by technical constraints.&lt;/p&gt;




&lt;h2&gt;
  
  
  Designing Systems That Expect Reality
&lt;/h2&gt;

&lt;p&gt;Designing for failure is not about pessimism. It is about acknowledging how real systems behave under real conditions.&lt;br&gt;
Each section in this article addresses a different failure category:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single points of failure&lt;/li&gt;
&lt;li&gt;Network unreliability&lt;/li&gt;
&lt;li&gt;Failure chain reactions&lt;/li&gt;
&lt;li&gt;Data consistency trade-offs&lt;/li&gt;
&lt;li&gt;Recovery and self-healing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The common thread is intentional design.&lt;br&gt;
Resilient systems are not accidental. They are built by teams who assume failure will happen, test for it deliberately, and learn continuously.&lt;br&gt;
The goal is not zero failure.&lt;br&gt;
The goal is controlled failure, fast recovery, and minimal impact on users.&lt;br&gt;
That is what designing for failure truly means.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>security</category>
      <category>cloudcomputing</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Strong vs Eventual Consistency in System Design</title>
      <dc:creator>Samuel Owolabi</dc:creator>
      <pubDate>Sat, 02 May 2026 15:22:10 +0000</pubDate>
      <link>https://dev.to/samowolabi/strong-vs-eventual-consistency-in-system-design-52nd</link>
      <guid>https://dev.to/samowolabi/strong-vs-eventual-consistency-in-system-design-52nd</guid>
      <description>&lt;p&gt;Why does this matter in distributed systems and why you should know this?&lt;/p&gt;

&lt;p&gt;Modern software systems are rarely contained on a single machine. They are spread across multiple servers and regions to ensure they stay fast and reliable for users everywhere.&lt;/p&gt;

&lt;p&gt;This distribution is a powerful tool, but it creates a challenge. When you have multiple copies of your data stored on different servers, you have to decide how to keep those copies in sync. This is the "consistency" problem.&lt;/p&gt;

&lt;p&gt;How you handle this determines how your system behaves when things get busy or when the network fails. As a developer or architect, you must choose your consistency model intentionally. If you don't, the system will choose for you, and usually at the most inconvenient time.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Consistency Really Means
&lt;/h3&gt;

&lt;p&gt;In a distributed system, consistency is about &lt;strong&gt;timing&lt;/strong&gt;. It answers the question: "After I save a piece of data, how soon will everyone else see the update?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it like a group chat.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strong Consistency:&lt;/strong&gt; Everyone in the chat sees every message in the exact same order at the exact same time. No one can reply to a message they haven't seen yet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eventual Consistency:&lt;/strong&gt; Some people might see messages a few seconds later than others. Eventually, everyone sees the same history, but for a moment, the "truth" looks different depending on who you ask.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Choosing between them is not purely technical. It is a business decision with architectural consequences.&lt;/p&gt;

&lt;p&gt;This article helps you understand how these models work, what trade-offs they impose, and how to choose intentionally without over-engineering or under-protecting your system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This is not the same as ACID consistency in single-node databases.&lt;br&gt;
&lt;strong&gt;ACID consistency&lt;/strong&gt; means the database moves from one valid state to another.&lt;br&gt;
&lt;strong&gt;Distributed consistency&lt;/strong&gt; is about when and where changes become visible.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Strong Consistency: The "Single Source of Truth"
&lt;/h2&gt;

&lt;p&gt;Strong consistency guarantees that once you write data, every subsequent read will return that new value. It doesn't matter which server the user connects to. They will always get the latest version.&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%2F5964afyjj49uqmw95lp8.jpg" 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%2F5964afyjj49uqmw95lp8.jpg" alt="Diagram illustrating Strong Consistency - Image credit goes to GeeksforGeeks" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;Strongly consistent systems typically rely on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A leader or primary node that orders writes&lt;/li&gt;
&lt;li&gt;Synchronous replication to replicas&lt;/li&gt;
&lt;li&gt;Quorums that require majority agreement before success&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A write is not considered successful until the system can guarantee that any subsequent read will see it.&lt;/p&gt;

&lt;p&gt;This often means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writes must wait&lt;/li&gt;
&lt;li&gt;Reads may block&lt;/li&gt;
&lt;li&gt;Network partitions reduce availability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Correctness is preserved.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real Use Cases of Strong Consistency
&lt;/h3&gt;

&lt;p&gt;These are places where &lt;strong&gt;showing the wrong data even briefly causes real damage&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You send money&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Bank transfers, wallet balances, payment confirmations. If your balance is wrong for even a moment, trust is gone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You buy the last item&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
E-commerce stock, flash sales, ticket booking. Two people cannot buy the same last seat.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You lose or gain access&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Logging in, role changes, permission updates. If access is revoked, it must be revoked everywhere immediately.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You hit a usage limit&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
API rate limits, subscription caps, quotas. Users must not exceed what they paid for.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You flip a critical switch&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Feature flags, kill switches, security toggles. Partial rollout can break production fast.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Predictability:&lt;/strong&gt; The system behaves exactly like a single-node database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Safety:&lt;/strong&gt; You never have to worry about a user seeing an "old" version of their bank balance or a deleted password.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Trade-offs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed:&lt;/strong&gt; The system is slower because it has to wait for multiple servers to agree.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Availability:&lt;/strong&gt; If one server is down or the network is lagging, the whole update might fail. The system chooses "Correctness" over "Being Online."&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Eventual Consistency: The "High Speed" Approach
&lt;/h2&gt;

&lt;p&gt;Eventual consistency allows different servers to hold different versions of the data for a short window of time. The system promises that "eventually," all copies will be the same, but it doesn't wait for that to happen before finishing your request.&lt;/p&gt;

&lt;h3&gt;
  
  
  How It Works
&lt;/h3&gt;

&lt;p&gt;When you save data, the system records it on one server and immediately tells you "Success." It then copies that data to other servers in the background. For a few milliseconds, or sometimes seconds, a user on the other side of the world might still see the old data.&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%2F5lmkka1xtp1lh6shvmzc.jpg" 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%2F5lmkka1xtp1lh6shvmzc.jpg" alt="Diagram illustrating Eventual Consistency - Image credit goes to BytebyteGo" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Eventual consistency relies on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Asynchronous replication&lt;/li&gt;
&lt;li&gt;Background synchronization&lt;/li&gt;
&lt;li&gt;Conflict resolution strategies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The system prioritizes availability, low latency, and partition tolerance.&lt;/p&gt;

&lt;p&gt;Stale reads are possible, but the system remains responsive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real Use Cases of Eventual Consistency
&lt;/h3&gt;

&lt;p&gt;These are places where &lt;strong&gt;being slightly wrong for a while is invisible or acceptable&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You refresh your feed&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Social posts, likes, comments. Seeing an update a few seconds late does not matter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You open an analytics dashboard&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Metrics, charts, reports. Near-real-time is good enough.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You get recommendations&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Suggested products, videos, content. Stale recommendations rarely cause harm.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You search for something&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Search indexes often lag behind writes. Users expect this.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;You receive notifications&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Emails, push notifications, background jobs. Reliability matters more than immediacy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; It is incredibly fast because there is no waiting for coordination.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilience:&lt;/strong&gt; Even if half the servers are having trouble communicating, the system can still accept new data and serve users.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Trade-offs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Confusion:&lt;/strong&gt; Users might refresh a page and see an old comment or an outdated notification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; As a developer, you have to write code that can handle "conflicts" if two people update the same thing at the same time on different servers.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Eventual vs Strong Consistency in Practice with DynamoDB
&lt;/h2&gt;

&lt;p&gt;DynamoDB is eventually consistent by default, meaning reads may return stale data immediately after a write.&lt;/p&gt;

&lt;p&gt;You can explicitly request strong consistency on a read to guarantee the latest committed value.&lt;/p&gt;

&lt;p&gt;This choice trades lower latency and higher availability for correctness, and you make it per request.&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;await&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&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;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;42&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;new@email.com&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;The write succeeds, but replicas may not all be updated yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Eventual Consistency (Default)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&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="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;42&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;You may receive stale data if the read hits a replica that has not applied the latest write.&lt;/p&gt;

&lt;h3&gt;
  
  
  Strong Consistency
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dynamodb&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="na"&gt;TableName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;42&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;ConsistentRead&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You are guaranteed to receive the latest committed value, at the cost of higher latency and throughput usage.&lt;/p&gt;




&lt;h2&gt;
  
  
  Making the Right and Intentional Choice
&lt;/h2&gt;

&lt;p&gt;When you design your next feature, ask yourself: "What is the worst thing that happens if a user sees data that is five seconds old?"&lt;/p&gt;

&lt;p&gt;If the answer is &lt;strong&gt;"nothing much,"&lt;/strong&gt; choose eventual consistency and enjoy the extra speed. If the answer is "we lose money or trust," stick with strong consistency.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Strong consistency prioritizes correctness over availability.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Eventual consistency prioritizes availability and scale over immediacy.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By being intentional with these trade-offs, you build systems that are not just technically sound, but also aligned with what your users actually need.&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>systemdesign</category>
      <category>dynamodb</category>
      <category>aws</category>
    </item>
    <item>
      <title>What if you are to build for a 1,000,000 daily active users?</title>
      <dc:creator>Samuel Owolabi</dc:creator>
      <pubDate>Tue, 20 Jan 2026 11:43:42 +0000</pubDate>
      <link>https://dev.to/samowolabi/what-if-you-are-to-build-for-a-1000000-daily-active-users-jm</link>
      <guid>https://dev.to/samowolabi/what-if-you-are-to-build-for-a-1000000-daily-active-users-jm</guid>
      <description>&lt;p&gt;Two years ago, a client approached me to build an end-to-end booking ecosystem. It is a B2B platform for businesses and their customers.&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;Business Dashboard&lt;/strong&gt;, I built a multi-tenant interface where owners could list their booking packages, manage inventory, manage customers, set booking hours and track their entire operation.&lt;/p&gt;

&lt;p&gt;For the &lt;strong&gt;Customer Portal&lt;/strong&gt;, I engineered a seamless flow for real-time booking, Stripe-integrated payment processing, automated daily booking reminders, automated receipt generation, dashboard for customers to view their bookings, reschedule bookings e.t.c.&lt;/p&gt;

&lt;p&gt;The initial stack was the standard developer starter pack: a monolithic architecture pushed to GitHub, with Vercel and Railway handling the frontend and backend orchestration.&lt;/p&gt;

&lt;p&gt;It was a clean implementation. It performed flawlessly. Businesses are being onboarded and setting up their booking platform, their customers are using their booking platform conveniently, the system works until last year.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Success Breaks Everything
&lt;/h2&gt;

&lt;p&gt;The platform hit a critical mass that transformed our steady growth into a high-concurrency challenge. We moved from predictable usage to a different league of infrastructure demands where every millisecond of latency translated into lost revenue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Shift in Request Volume&lt;/strong&gt;&lt;br&gt;
We moved from managing users to managing an onslaught. We were hitting millions of API calls and high-frequency I/O operations daily. These weren't just simple reads; they were massive surges in concurrent traffic that ignored any attempt at traditional capacity planning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Pressure on System Reliability&lt;/strong&gt;&lt;br&gt;
There was a much higher expectation for low latency and 99.9% uptime. The SLAs shifted overnight. We started seeing frequent service degradation because the systems were operating under constant resource exhaustion. The infrastructure was gasping for air.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Complexity of Concurrent Sessions&lt;/strong&gt;&lt;br&gt;
As the user base grew, we faced challenges with session persistence and shared state. Managing thousands of simultaneous web socket connections for real-time availability updates meant our single-server memory was constantly hitting its ceiling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Traditional Architecture Fails at Scale&lt;/strong&gt;&lt;br&gt;
When the system started buckling, it became clear that the standard deployment strategy has a ceiling. Growth doesn't just stress your code; it exposes the structural debt in your architecture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Catastrophe of Single Points of Failure&lt;/strong&gt;&lt;br&gt;
Our tight coupling became our biggest vulnerability. I vividly remember when the client called me around 01:00 AM because the entire production environment went down. The culprit was an SMTP timeout. Because the email service was part of the main execution thread, a transient failure in a third-party mail provider caused a deadlock that crashed the entire server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Bottleneck of Synchronous Execution&lt;/strong&gt;&lt;br&gt;
In the original build, everything was synchronous. When a user booked a package, the thread would stay open while processing the payment, updating the DB, and triggering transactional emails. At scale, this created massive overhead, slowing down the response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Silent Failures and Data Integrity&lt;/strong&gt;&lt;br&gt;
At scale, systems rarely just die; they degrade. We started seeing zombie states where a payment went through in Stripe, but the database didn't update because of a timeout. These partial failures are a nightmare for business owners because they create massive manual support work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Observability Gap&lt;/strong&gt;&lt;br&gt;
We are consuming high RAM usage, without evening know what part of the architecture consume that much. We were flying blind because we lacked an observability layer to track CPU Usage, latency, errors, throughput e.t.c&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Database Contention Over Compute&lt;/strong&gt;&lt;br&gt;
We hit the database wall long before we hit compute limits. The primary instance was struggling to manage the lock contention between the heavy analytics queries on the business dashboard and the high-frequency writes from the customer booking portal.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I did next?
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Transitioning to a Distributed Architecture
&lt;/h2&gt;

&lt;p&gt;To resolve these issues, I had to move away from the monolith and design for horizontal scalability. We transitioned the application to run across multiple stateless instances, managed by a Load Balancer and an Auto-Scaling Group (ASG).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decoupling Data with CQRS and Read Replicas&lt;/strong&gt;&lt;br&gt;
I separated the Read and Write workloads. We implemented a Primary-Replica configuration where the primary database handles all Write operations, while multiple Read Replicas handle the query traffic. We had to manage Replication Lag to ensure the business and customer dashboard could pull analytics without dragging down the payment engine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-layer Caching and Edge Delivery&lt;/strong&gt;&lt;br&gt;
To reduce the load on our origin servers, I implemented a robust caching strategy. We utilized Cloudfront for edge caching of static assets and integrated Redis as a distributed cache for frequently accessed or computationally expensive API queries. Now loads have been reduced on databases and downstream services, and response times improved at high traffic&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Asynchronous Event-Driven Architecture&lt;/strong&gt;&lt;br&gt;
The most critical shift was moving to a Producer-Queue-Consumer model. I offloaded non-critical tasks like emails, push notifications, and analytics processing to background workers using a Message Queue. This decoupled the critical path, allowing the user to receive faster response, while the request is being run as a background job. We have eliminated single points of failure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fault Tolerance and Cost Optimization&lt;/strong&gt;&lt;br&gt;
I deployed instances across multiple Availability Zones (AZs) which eliminated redundancy into every critical path. We set up Database Standbys and automated health checks for automatic failover. Importantly, we used Auto Scaling Groups (ASGs) to automatically scale down during low-traffic hours to ensure we weren't paying for compute we didn't need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Centralized Logging and Real-time Telemetry&lt;/strong&gt;&lt;br&gt;
I implemented a centralized logging stack to aggregate data from every distributed instance. This allowed us to query logs across the entire cluster in real-time, making it possible to trace a single user request as it traveled through the load balancer, the API, and the background workers. I set alerts for above usage and user impacting thresholds, monitor trends not just outages. Now we know “what is slow“, “what is failing“, “what has changed“ e.t.c&lt;/p&gt;

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

&lt;p&gt;Building for a million daily active users is less about the tools and more about the discipline of distributed systems. Today, we are managing tens of thousands of active users, but the architecture is now future-proofed to scale to a million and beyond without a total rewrite.&lt;/p&gt;

&lt;p&gt;I started simple to prove the business model, but I documented the limits and let real-world telemetry drive our path toward high availability. It’s about being ready for success so that when a million users show up, your system and your sleep schedule can handle it.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>distributedsystems</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Client vs Server-Side Encryption: The Real Meaning of End-to-End Security</title>
      <dc:creator>Samuel Owolabi</dc:creator>
      <pubDate>Wed, 08 Oct 2025 21:31:37 +0000</pubDate>
      <link>https://dev.to/samowolabi/client-vs-server-side-encryption-the-real-meaning-of-end-to-end-security-2kg5</link>
      <guid>https://dev.to/samowolabi/client-vs-server-side-encryption-the-real-meaning-of-end-to-end-security-2kg5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi5pc6hemnaz1zc3z0l22.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%2Fi5pc6hemnaz1zc3z0l22.png" alt="Client vs Server-Side Encryption: The Real Meaning of End-to-End Security" width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my early tech journey, I’d always see the phrase &lt;strong&gt;“End-to-End Encryption on WhatsApp”&lt;/strong&gt; and feel a bit skeptical. 🤔 How could WhatsApp not see my messages when their servers were the ones handling them? I honestly thought it was just a clever marketing gimmick.&lt;/p&gt;

&lt;p&gt;My curiosity grew when I started learning about crypto wallets. If I lose my seed phrase, why can’t the wallet company just hit a “reset” button and recover it for me? What was happening behind the scenes that made my data so inaccessible, even to the provider?&lt;/p&gt;

&lt;p&gt;Later, while working on real projects, I got comfortable implementing security measures like JWT, OAuth, and password hashing with bcrypt. But for a fintech project, I faced a new challenge: encrypting sensitive data moving between our users’ devices and our server. Suddenly, I was working with concepts like AES, data keys, and key management, and it felt fundamentally different from the security tokens I was used to.&lt;/p&gt;

&lt;p&gt;The real “aha!” moment came while I was studying for my AWS Certified Developer exam. The course material &lt;a href="https://www.udemy.com/share/101WgC3@rffUnKAimCMYsJ5In5tP4F7HSIHY454x8-Cv1g6SfCR5RshP5LuZW3DaaYd5uuFp/" rel="noopener noreferrer"&gt;(Stephane Maarek Udemy Course)&lt;/a&gt; finally connected all the dots, giving me a clear picture of how encryption works on both the client’s device and the server. This article is my attempt to share those lessons and clear up the confusion in a simple, beginner-friendly way.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, What Exactly is Encryption?
&lt;/h2&gt;

&lt;p&gt;At its heart, encryption is the process of taking readable data (plaintext) and scrambling it into an unreadable format (ciphertext) using a special algorithm. The only way to unscramble that data and turn it back into readable plaintext is with the correct decryption key.&lt;/p&gt;

&lt;p&gt;Think of it like locking a secret message in a box 📦. Anyone can see the box, but only the person who has the unique key can open it and read the message inside.&lt;/p&gt;

&lt;p&gt;Encryption is built on two main approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Symmetric Encryption: Imagine a lockbox that uses the same key to both lock and unlock it. This is great for speed. (e.g., AES).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Asymmetric Encryption: This is like having two different keys. One is a public key that you can give to anyone they can use it to put things in the box and lock it. But only you have the secret private key that can open it. (e.g., RSA).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  An Analogy That Made It All Click: The Two Lockboxes
&lt;/h2&gt;

&lt;p&gt;Imagine you want to send a secret letter from Alice to Bob using a courier service. How they handle security is the perfect analogy for client-side vs. server-side encryption.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 1: Server-Side Encryption (The Courier’s Lockbox)
&lt;/h3&gt;

&lt;p&gt;Alice writes her letter and hands it, unsealed, to the courier. The courier puts the letter into one of their standard company lockboxes and takes it to Bob. The courier company has the master key to all their boxes. They promise not to look, but they technically could if they wanted to. This is super convenient for Alice; she doesn’t have to worry about finding or managing her own lockbox.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scenario 2: Client-Side Encryption (Alice’s Personal Lockbox)
&lt;/h3&gt;

&lt;p&gt;Alice writes her letter, but before she even calls the courier, she puts it in her own personal lockbox. Only she and Bob have a copy of the key. She then gives the already locked box to the courier. The courier can transport the box, but they have absolutely no way of opening it to see the contents. This is maximum security.&lt;/p&gt;




&lt;h2&gt;
  
  
  Client-Side Encryption (CSE)
&lt;/h2&gt;

&lt;p&gt;Client-Side Encryption (CSE), also known as end-to-end encryption (E2EE) or zero-knowledge encryption, is the “personal lockbox” approach. The data is encrypted on your device (the client) before it’s sent to the server.&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%2F0h4zrndutxhm7zh9ryyb.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%2F0h4zrndutxhm7zh9ryyb.png" alt="Diagram showing Client Side Encryption Flows. Credit: Stephane Maarek&amp;lt;br&amp;gt;
" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How does it work?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;You create something: You type a message or create a file on your phone or computer, or data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Encrypted on your device: The app you’re using (like WhatsApp or your crypto wallet) uses a key on your device to encrypt this data right then and there.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scrambled data travels: The unreadable ciphertext is sent over the internet to the server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Server stores gibberish: The server stores this ciphertext. It has no key and cannot read the data. To the server, it’s just a meaningless blob of information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Decrypted on the other side: When your friend requests the message, the server sends them the ciphertext. Their app uses its key to decrypt the data back into a readable format on their device.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; Social Messaging Apps (WhatsApp, Signal): This finally answered my first question! When you send a message, it’s encrypted on your phone and only decrypted on your friend’s phone. WhatsApp’s servers only pass along the scrambled data. They truly can’t read your messages.&lt;/li&gt;
&lt;li&gt;Cryptocurrency Wallets: This solved my second puzzle! Your “seed phrase” is used to create your private key. This key lives only on your device. The wallet provider never sees it. If you lose it, it’s gone forever because they never had a copy to begin with.&lt;/li&gt;
&lt;li&gt;Secure Cloud Storage: Services like AWS S3, Tresorit e.t.c allow you to encrypt your files before you upload them. You manage the keys, and the cloud provider simply stores the encrypted blobs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Pros of Client Side Encryption
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maximum Privacy &amp;amp; Control:&lt;/strong&gt; You, and only you (and the recipient), hold the keys. The service provider cannot access your data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Breach Containment:&lt;/strong&gt; If the server is hacked, the attackers only get useless ciphertext.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regulatory Compliance:&lt;/strong&gt; Helps meet strict data privacy regulations like GDPR and HIPAA, as no unencrypted personal data is processed by the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Cons of Client Side Encryption
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key Management Burden&lt;/strong&gt;: If the user loses their key, the data is permanently lost. There is no “Forgot Password” option.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited Server Functionality&lt;/strong&gt;: The server can’t search, index, or perform any operations on the data because it can’t read it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation Complexity&lt;/strong&gt;: It requires more work on the client-side application.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Server-Side Encryption (SSE)
&lt;/h2&gt;

&lt;p&gt;Encryption happens after data reaches the server. The server manages keys and performs encryption before storage. It is the “delivery company’s lockbox” approach. You send your data to the server, and the server takes on the responsibility of encrypting it.&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%2F1vgg29q1b90dyzafxou2.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%2F1vgg29q1b90dyzafxou2.png" alt="Diagram showing Server Side Encryption Flows. Credit: Stephane Maarek&amp;lt;br&amp;gt;
" width="800" height="209"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How does it work?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;You send your data: You upload a file or send information to the server. This connection itself is usually encrypted (this is called “in-transit” encryption, thanks to HTTPS), but the server receives the data in its original, readable form.&lt;/li&gt;
&lt;li&gt;Server encrypts it: Once your data arrives, the server uses keys that it manages to encrypt it. This is called “encryption at rest.”&lt;/li&gt;
&lt;li&gt;Server stores it: The server stores the resulting ciphertext.&lt;/li&gt;
&lt;li&gt;Server decrypts for you: When you want your data back, the server fetches the ciphertext, decrypts it using its key, and sends the readable data back to you (again, over a secure connection).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Most Cloud Storage &amp;amp; Databases: When you upload a file to Google Drive, Dropbox, or a standard AWS S3 bucket, this is what’s happening. They encrypt it on their end to protect against someone physically stealing their hard drives.&lt;/li&gt;
&lt;li&gt;Backups: When a service backs up its database, it almost always uses SSE to protect that backup file.&lt;/li&gt;
&lt;li&gt;Key Management Systems (KMS): This is a more advanced topic, but cloud providers like AWS have services (like KMS) that make it really easy and secure for servers to manage these encryption keys.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Pros of Server Side Encryption
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Convenience:&lt;/strong&gt; It’s seamless for the user and often simple for the developer to enable. The provider handles all the complexity of key management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server Functionality:&lt;/strong&gt; Since the server can decrypt the data, it can perform useful operations like searching the content of files or indexing database fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy Recovery:&lt;/strong&gt; Key management is centralized, so data recovery is possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Cons of Server Side Encryption
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The Trust Issue: You must trust the service provider. They have the keys and can technically access your data. This can be a problem if they suffer an internal breach or are compelled by a government to hand over data.&lt;/li&gt;
&lt;li&gt;Broader Attack Surface: If an attacker compromises the server and gains access to the key management system, they could potentially decrypt all the data.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A Cool Pro Tip (Bonus): Envelope Encryption
&lt;/h2&gt;

&lt;p&gt;This is a clever technique used by almost all major cloud providers (like AWS KMS) to do SSE really efficiently. Instead of using one super-strong (but slow) key to encrypt huge amounts of data, they do this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;For each file, the system generates a unique, fast key called a Data Key.&lt;/li&gt;
&lt;li&gt;The file is quickly encrypted with this Data Key.&lt;/li&gt;
&lt;li&gt;Then, a highly protected Master Key is used to encrypt only the tiny Data Key.
4 . The encrypted file and the encrypted Data Key are stored together.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To decrypt, the server first uses the Master Key to unlock the Data Key, and then uses that Data Key to quickly unlock the main file. It’s the best of both worlds: the top-tier security of a master key with the high performance of data keys!&lt;/p&gt;




&lt;h2&gt;
  
  
  So, Which One Should I Choose for My Project?
&lt;/h2&gt;

&lt;p&gt;Now that we know the difference, the choice becomes a classic trade-off.&lt;/p&gt;

&lt;h3&gt;
  
  
  Go for Client-Side Encryption (CSE) when…
&lt;/h3&gt;

&lt;p&gt;Your entire product is built on a promise of privacy and trust. You want to be able to look your users in the eye and say, “We cannot see your data, period.” This is essential for secure chat apps, password managers, and services handling extremely sensitive health or legal information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Go for Server-Side Encryption (SSE) when…
&lt;/h3&gt;

&lt;p&gt;Your priority is user convenience and providing rich features like search and data analytics. SSE is the standard for most applications. It provides a strong security baseline, protects against data center theft, and helps you meet most compliance requirements without putting the burden of key management on your users.&lt;/p&gt;




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

&lt;p&gt;The debate isn’t about which encryption method is “better,” but which is right for the job. It’s a fundamental choice between giving the user absolute control versus offering centralized convenience.&lt;/p&gt;

&lt;p&gt;Understanding this difference locking the box yourself versus letting the courier lock it for you was a huge step in my developer journey. I hope this explanation clears things up for you too and helps you make more informed decisions, both as a developer building secure apps and as a user trusting services with your data.&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>beginners</category>
      <category>architecture</category>
      <category>security</category>
    </item>
    <item>
      <title>Deploying a Node.js App to AWS Elastic Beanstalk with GitHub Actions: A Beginner's Guide</title>
      <dc:creator>Samuel Owolabi</dc:creator>
      <pubDate>Fri, 08 Aug 2025 11:33:43 +0000</pubDate>
      <link>https://dev.to/samowolabi/deploying-a-nodejs-app-to-aws-elastic-beanstalk-with-github-actions-a-beginners-guide-2oml</link>
      <guid>https://dev.to/samowolabi/deploying-a-nodejs-app-to-aws-elastic-beanstalk-with-github-actions-a-beginners-guide-2oml</guid>
      <description>&lt;h1&gt;
  
  
  Automating Node.js Deployment to AWS Elastic Beanstalk with GitHub Actions
&lt;/h1&gt;

&lt;p&gt;Automating the deployment of your applications can save you time and streamline your development process. This step-by-step guide will walk you through deploying a Node.js application to AWS Elastic Beanstalk and setting up a CI/CD pipeline using GitHub Actions. By the end, you'll have a fully automated workflow that tests and deploys your code every time you push to your main branch.&lt;/p&gt;

&lt;p&gt;This tutorial is designed for beginners with a general familiarity with Git, GitHub, and AWS.&lt;/p&gt;

&lt;p&gt;You can find the complete code for this tutorial in this &lt;a href="https://github.com/samowolabi/nodejs-elastic-beanstalk-with-githubactions-app" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is AWS Elastic Beanstalk?
&lt;/h2&gt;

&lt;p&gt;AWS Elastic Beanstalk is a service that makes it easier to deploy and manage applications in the AWS cloud. You simply upload your application, and Elastic Beanstalk automatically handles the deployment details of capacity provisioning, load balancing, auto-scaling, and application health monitoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why use it?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity:&lt;/strong&gt; It abstracts away the complexity of setting up servers, load balancers, and databases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; It can automatically scale your application up or down based on demand.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost-Effective:&lt;/strong&gt; With the AWS Free Tier, you can run a simple application like the one in this guide at no cost. You only pay for what you use as your application grows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is GitHub Actions?
&lt;/h2&gt;

&lt;p&gt;GitHub Actions is a CI/CD (Continuous Integration/Continuous Deployment) platform that allows you to automate your build, test, and deployment pipeline. You can create workflows that build and test every pull request to your repository or deploy merged pull requests to production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our Workflow Structure:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event:&lt;/strong&gt; A trigger that starts the workflow, such as a &lt;code&gt;push&lt;/code&gt; to a branch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jobs:&lt;/strong&gt; A set of steps that execute on a runner. We will have a &lt;code&gt;test&lt;/code&gt; job and a &lt;code&gt;deploy&lt;/code&gt; job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steps:&lt;/strong&gt; Individual tasks that run commands in a job.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Combine Elastic Beanstalk with GitHub Actions?
&lt;/h2&gt;

&lt;p&gt;Combining these two powerful tools creates a seamless CI/CD pipeline. With every push to your repository, GitHub Actions will automatically test your Node.js application and, if the tests pass, deploy it to your Elastic Beanstalk environment. This automation eliminates manual deployment steps, reduces the risk of human error, and allows you to release new features faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we start, make sure you have the following set up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;An AWS Account:&lt;/strong&gt; You'll need an account with an IAM user that has permissions for Elastic Beanstalk. We'll create the necessary roles during the tutorial.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A GitHub Repository:&lt;/strong&gt; This is where your Node.js application code will live. For this tutorial, we will be using a pre-configured repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Node.js:&lt;/strong&gt; Ensure you have Node.js (version 18 or higher) installed on your local machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Let's Get Started!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Set Up Your Node.js Application
&lt;/h3&gt;

&lt;p&gt;To get you started quickly, a simple Node.js application using Express and TypeScript has been prepared.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clone the Repository:&lt;/strong&gt; Open your terminal and clone the sample application:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git clone https://github.com/samowolabi/nodejs-elastic-beanstalk-with-githubactions-app.git
   &lt;span class="nb"&gt;cd &lt;/span&gt;nodejs-elastic-beanstalk-with-githubactions-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install Dependencies:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This project has a straightforward structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.github/workflows/ci.yml&lt;/code&gt;: The GitHub Actions workflow file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/index.ts&lt;/code&gt;: The main application file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test/app.test.js&lt;/code&gt;: A simple test file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;package.json&lt;/code&gt;: Defines project scripts and dependencies.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tsconfig.json&lt;/code&gt;: The TypeScript configuration file.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 2: Set Up AWS Elastic Beanstalk
&lt;/h3&gt;

&lt;p&gt;Now, let's create the environment on AWS where our application will be deployed.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Navigate to Elastic Beanstalk:&lt;/strong&gt; Log in to your AWS Management Console and go to the Elastic Beanstalk service.&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%2Fz19ckj4348cbeoleilqi.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%2Fz19ckj4348cbeoleilqi.png" alt="Image showing AWS Elastic Beanstalk Homepage" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create a New Environment:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Click on &lt;strong&gt;Create Application&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application name:&lt;/strong&gt; Enter a name, for instance, &lt;code&gt;nodejs-app-eb-gh-app&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform:&lt;/strong&gt; Select &lt;code&gt;Node.js&lt;/code&gt;. For this guide, "Node.js 18" is used.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application code:&lt;/strong&gt; Keep &lt;code&gt;Sample application&lt;/code&gt; selected for now.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Presets&lt;/strong&gt;, select &lt;strong&gt;Single instance&lt;/strong&gt; (which is free-tier eligible).&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Next&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&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%2Fs6hsrekmx20shq4xh6da.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%2Fs6hsrekmx20shq4xh6da.png" alt="Image showing Select Environment for the newly AWS Elastic Beanstalk Environment to be created" width="800" height="248"&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%2Ftw0184ewak3atqqqjcgo.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%2Ftw0184ewak3atqqqjcgo.png" alt="Image showing Enter Application Name for the newly AWS Elastic Beanstalk Environment to be created" width="800" height="409"&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%2Fm2i3zwvhphk1ww6ftbbn.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%2Fm2i3zwvhphk1ww6ftbbn.png" alt="Image showing Select Platform and Application Code for the newly AWS Elastic Beanstalk Environment to be created" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Configure Service Access:&lt;/strong&gt;
This screen is crucial for granting Elastic Beanstalk the necessary permissions.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Service role:&lt;/strong&gt; This role allows Elastic Beanstalk to manage other AWS resources on your behalf.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you don't have one, select &lt;strong&gt;Create and use new service role&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;A new window will open to create an IAM role. The role will be pre-configured. Simply click &lt;strong&gt;Create role&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Back on the Elastic Beanstalk screen, refresh the dropdown and select the new role (&lt;code&gt;aws-elasticbeanstalk-service-role&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;EC2 instance profile:&lt;/strong&gt; This role grants permissions to the EC2 instances that will run your application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Create and use new instance profile&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the new window, name your role (e.g., &lt;code&gt;aws-elasticbeanstalk-ec2-new-role&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The necessary policies (&lt;code&gt;AWSElasticBeanstalkWebTier&lt;/code&gt;, etc.) will be attached automatically. Click &lt;strong&gt;Create role&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Return to the Elastic Beanstalk screen, refresh, and select the newly created instance profile.&lt;/li&gt;
&lt;/ul&gt;


&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%2Fisv12tja2gzno4l078pl.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%2Fisv12tja2gzno4l078pl.png" alt="Image showing screen to configure Service Access on Create Application" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Review and Launch:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;You can skip the networking, database, and tags setup for this simple deployment by clicking &lt;strong&gt;Skip to review&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;On the &lt;strong&gt;Review&lt;/strong&gt; page, look over your configurations and click &lt;strong&gt;Submit&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;AWS will now start building your environment. This process can take a few minutes. Once it's complete, you can access the provided domain, and you will see the default sample application page.&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%2Fnr9piqzriuaynfd0se0z.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%2Fnr9piqzriuaynfd0se0z.png" alt="Image showing AWS Elastic Beanstalk launched Environment" width="800" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Bonus: Setting Up Environment Variables
&lt;/h4&gt;

&lt;p&gt;Your Node.js application might require environment variables. You can set them up in the Elastic Beanstalk console:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to your environment's page.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Configuration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Software&lt;/strong&gt; &amp;gt; &lt;strong&gt;Edit&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;Environment properties&lt;/strong&gt;, add your key-value pairs (e.g., &lt;code&gt;NODE_ENV&lt;/code&gt; = &lt;code&gt;production&lt;/code&gt;). Elastic Beanstalk automatically provides the &lt;code&gt;PORT&lt;/code&gt; variable.&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%2Fqjt14spnbeovefhlytsi.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%2Fqjt14spnbeovefhlytsi.png" alt="Image showing setting Environment Properties (env) for the launched AWS Elastic Beanstalk Environment" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Configure the CI/CD Pipeline with GitHub Actions
&lt;/h3&gt;

&lt;p&gt;Now it's time to connect your GitHub repository to your new Elastic Beanstalk environment.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Get AWS Access Keys:&lt;/strong&gt;
The GitHub Actions workflow needs credentials to deploy to your AWS account.&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;In the AWS IAM console, navigate to &lt;strong&gt;Users&lt;/strong&gt; and select your IAM user.&lt;/li&gt;
&lt;li&gt;Go to the &lt;strong&gt;Security credentials&lt;/strong&gt; tab and click &lt;strong&gt;Create access key&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Third-party service&lt;/strong&gt;, acknowledge the recommendation, and proceed.&lt;/li&gt;
&lt;li&gt;You will be provided with an &lt;strong&gt;Access key ID&lt;/strong&gt; and a &lt;strong&gt;Secret access key&lt;/strong&gt;. &lt;strong&gt;Copy these immediately&lt;/strong&gt;, as you won't be able to see the secret key again.&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%2Fjhdmej757bj5db7e9qva.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%2Fjhdmej757bj5db7e9qva.png" alt="Image showing how to create access key to receive Access Key ID and Secret Access Key Environment Properties (env) for the user on IAM Console" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Ensure your IAM user has the necessary permissions. For this deployment, the following policies are recommended: &lt;code&gt;elasticbeanstalk:*&lt;/code&gt;, &lt;code&gt;s3:*&lt;/code&gt;, &lt;code&gt;ec2:*&lt;/code&gt;, &lt;code&gt;iam:PassRole&lt;/code&gt;, and &lt;code&gt;logs:*&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add Credentials to GitHub Secrets:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;In your GitHub repository, go to &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Secrets and variables&lt;/strong&gt; &amp;gt; &lt;strong&gt;Actions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;New repository secret&lt;/strong&gt; and add the following:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;: Your AWS access key ID.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;: Your AWS secret access key.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&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%2Fsvu5asox0zr3yv15uvlj.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%2Fsvu5asox0zr3yv15uvlj.png" alt="Image showing how to add Credentials to GitHub Repository Secret" width="800" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Understand the Workflow File:&lt;/strong&gt;
Open the &lt;code&gt;.github/workflows/ci.yml&lt;/code&gt; file in your repository. Let's break down what it does:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;   &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI/CD&lt;/span&gt;

   &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;master&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
     &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;master&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

   &lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
     &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

       &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
         &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
           &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;18.x&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;20.x&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

       &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
         &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js ${{ matrix.node-version }}&lt;/span&gt;
         &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
         &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
           &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.node-version }}&lt;/span&gt;
           &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
         &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
         &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;

       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build project&lt;/span&gt;
         &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;

     &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
       &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
       &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.ref == 'refs/heads/main' &amp;amp;&amp;amp; github.event_name == 'push'&lt;/span&gt;

       &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
         &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
         &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
         &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
           &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20.x'&lt;/span&gt;
           &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
         &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build project&lt;/span&gt;
         &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;

       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate deployment package&lt;/span&gt;
         &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
           &lt;span class="s"&gt;zip -r deploy.zip . -x '*.git*' 'node_modules/.cache/*' 'src/*' 'test/*' '*.md' '.env*'&lt;/span&gt;

       &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Elastic Beanstalk&lt;/span&gt;
         &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;einaregilsson/beanstalk-deploy@v22&lt;/span&gt;
         &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
           &lt;span class="na"&gt;aws_access_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;
           &lt;span class="na"&gt;aws_secret_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;
           &lt;span class="na"&gt;application_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs-app-eb-gh-app&lt;/span&gt;
           &lt;span class="na"&gt;environment_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Nodejs-app-eb-gh-app-env&lt;/span&gt;
           &lt;span class="na"&gt;version_label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.sha }}&lt;/span&gt;
           &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
           &lt;span class="na"&gt;deployment_package&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy.zip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;on&lt;/code&gt;:&lt;/strong&gt; This workflow triggers on a &lt;code&gt;push&lt;/code&gt; or &lt;code&gt;pull_request&lt;/code&gt; to the &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;master&lt;/code&gt; branches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;jobs&lt;/code&gt;:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;test&lt;/code&gt;:&lt;/strong&gt; This job runs on two Node.js versions (18.x and 20.x). It checks out your code, installs dependencies, and runs your tests (&lt;code&gt;npm test&lt;/code&gt;) and build script (&lt;code&gt;npm run build&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;deploy&lt;/code&gt;:&lt;/strong&gt; This job &lt;strong&gt;depends on the &lt;code&gt;test&lt;/code&gt; job succeeding&lt;/strong&gt; (&lt;code&gt;needs: test&lt;/code&gt;). It only runs on a &lt;code&gt;push&lt;/code&gt; to the &lt;code&gt;main&lt;/code&gt; branch. It performs the build again, zips the necessary files into &lt;code&gt;deploy.zip&lt;/code&gt;, and then uses the &lt;code&gt;beanstalk-deploy&lt;/code&gt; action to push the package to your Elastic Beanstalk environment.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Update the &lt;code&gt;application_name&lt;/code&gt;, &lt;code&gt;environment_name&lt;/code&gt;, and &lt;code&gt;region&lt;/code&gt; in the &lt;code&gt;deploy&lt;/code&gt; job to match your Elastic Beanstalk setup.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 4: Deploy the Application
&lt;/h3&gt;

&lt;p&gt;Now for the magic moment!&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Push to Main:&lt;/strong&gt; Commit any changes you've made and push them to your &lt;code&gt;main&lt;/code&gt; branch.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git add &lt;span class="nb"&gt;.&lt;/span&gt;
   git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"feat: setup CI/CD and deploy to Elastic Beanstalk"&lt;/span&gt;
   git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Monitor the Workflow:&lt;/strong&gt; Go to the &lt;strong&gt;Actions&lt;/strong&gt; tab in your GitHub repository. You will see your workflow running. Click on it to see the &lt;code&gt;test&lt;/code&gt; and &lt;code&gt;deploy&lt;/code&gt; jobs in action.&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%2Ftpa5h272dfv7ig4hpvsd.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%2Ftpa5h272dfv7ig4hpvsd.png" alt="Image showing Github Actions Workflow, on this screenshot, test and deployment is successful" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Verify Deployment:&lt;/strong&gt; Once the &lt;code&gt;deploy&lt;/code&gt; job is complete, navigate back to your Elastic Beanstalk environment's URL. You should now see the message: "Hello, TypeScript with Express!".&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%2Fpw6wse3s2agdh90wln95.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%2Fpw6wse3s2agdh90wln95.png" alt="Image showing how to retrieve your launched AWS Elastic Beanstalk Environment Domain" width="800" height="457"&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%2Fqlzvptuvrp0w6501uxi7.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%2Fqlzvptuvrp0w6501uxi7.png" alt="Image showing accessed domain to confirm that our Node JS is deployed successfully on AWS Elastic Beanstalk" width="800" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion and Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Congratulations! You have successfully set up a fully automated CI/CD pipeline. Every time you push a change to your &lt;code&gt;main&lt;/code&gt; branch, GitHub Actions will test your code and deploy it to AWS Elastic Beanstalk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Issues:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deployment Fails:&lt;/strong&gt; Double-check that your AWS credentials in GitHub Secrets are correct and that the IAM user has the required permissions. Verify that the application and environment names in your &lt;code&gt;ci.yml&lt;/code&gt; file match your Elastic Beanstalk setup exactly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build/Test Failures:&lt;/strong&gt; Run &lt;code&gt;npm test&lt;/code&gt; and &lt;code&gt;npm run build&lt;/code&gt; locally to debug any issues before pushing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check Logs:&lt;/strong&gt; The first place to look for errors is the GitHub Actions log. For environment-specific issues, check the logs in your AWS Elastic Beanstalk console (&lt;strong&gt;Logs&lt;/strong&gt; &amp;gt; &lt;strong&gt;Request Logs&lt;/strong&gt; &amp;gt; &lt;strong&gt;Last 100 Lines&lt;/strong&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This automated workflow is a fundamental practice in modern web development that will significantly improve your development and release process.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;A practical guide by &lt;a href="https://samowolabi.com" rel="noopener noreferrer"&gt;Samuel Owolabi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>githubactions</category>
      <category>node</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Don't Reach for Redux Just Yet: Mastering State with React Context, useReducer, and TypeScript</title>
      <dc:creator>Samuel Owolabi</dc:creator>
      <pubDate>Tue, 22 Jul 2025 21:03:16 +0000</pubDate>
      <link>https://dev.to/samowolabi/dont-reach-for-redux-just-yet-mastering-state-with-react-context-usereducer-and-typescript-iib</link>
      <guid>https://dev.to/samowolabi/dont-reach-for-redux-just-yet-mastering-state-with-react-context-usereducer-and-typescript-iib</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy21hxc9l3o70bxns7ank.jpg" 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%2Fy21hxc9l3o70bxns7ank.jpg" alt="React Context, useReducer, and TypeScript" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A practical guide by &lt;a href="https://samowolabi.com" rel="noopener noreferrer"&gt;Samuel Owolabi&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;What if you don't actually need Zustand, Jotai, or Redux for your next project?&lt;/p&gt;

&lt;p&gt;Hear me out. Those are fantastic libraries, but for a huge number of applications, you can build a powerful, scalable, and type-safe state management system using the tools React already gives you. The secret lies in combining React Context with the useReducer hook and wrapping it all in the warm, safe blanket of TypeScript.&lt;/p&gt;

&lt;p&gt;The purpose of this guide is to show you exactly how to set up this pattern in a simple, practical way. We'll cover a real-world example to show how flexible this approach is. Don't be overwhelmed! By the end, you'll be able to manage complex state with confidence.&lt;/p&gt;

&lt;p&gt;We'll build a state management system for a simple fintech dashboard. This will allow us to handle things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User authentication (login/logout)&lt;/li&gt;
&lt;li&gt;A theme switcher (dark/light mode)&lt;/li&gt;
&lt;li&gt;User's wallet data (adding, removing, and updating wallets)&lt;/li&gt;
&lt;li&gt;A global token for API requests&lt;/li&gt;
&lt;li&gt;A selected currency for display&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready? Let's dive in.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Concepts: What Are We Using and Why?
&lt;/h2&gt;

&lt;p&gt;First, let's quickly break down the tools we're bringing together.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is React Context? 📝
&lt;/h3&gt;

&lt;p&gt;React Context provides a way to pass data through the component tree without having to pass props down manually at every level. This is the perfect tool for sharing "global" data like the current user, theme, or language. It helps us avoid a problem called "prop drilling."&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the useReducer Hook? ⚙️
&lt;/h3&gt;

&lt;p&gt;useReducer is a React hook that you can, and should, use as an alternative to useState when you have complex state logic. Instead of just updating the state directly, you "dispatch" actions. A "reducer" function then takes the current state and an action and returns the new state. If you've ever used Redux, this pattern will feel very familiar. It helps keep your state transitions predictable and organized.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Add TypeScript to the Mix? 🛡️
&lt;/h3&gt;

&lt;p&gt;TypeScript is a superset of JavaScript that adds static types. Here's why it's a game-changer for this pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type Safety&lt;/strong&gt;: It prevents you from putting the wrong kind of data into your state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Amazing Autocomplete&lt;/strong&gt;: TypeScript knows exactly what your state looks like. When you type &lt;code&gt;state.&lt;/code&gt;, it will show you all the available properties.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smarter Actions&lt;/strong&gt;: It ensures you dispatch actions with the correct type and payload. For example, if your ADD_WALLET action needs a WalletType payload, TypeScript will give you an error if you try to send a simple string. This catches bugs before you even run the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Folder Structure
&lt;/h2&gt;

&lt;p&gt;We'll organize our context logic into a dedicated contexts folder. This keeps the state management code neatly separated from our UI components. For a Next.js App Router project, it might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/src
|-- /app
|   |-- layout.tsx          # We'll wrap our app here
|   |-- usage-examples.tsx  # Component to test our context
|
|-- /contexts
|   |-- appContext.tsx      # The main context provider file
|   |-- reducer.tsx         # The reducer function and initial state
|   |-- reducerTypes.tsx    # All our TypeScript types
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: Define Your Types (reducerTypes.tsx)
&lt;/h2&gt;

&lt;p&gt;This is the most important step for achieving type safety. We start by defining the "shape" of our state, the actions we can perform, and the context itself. By doing this first, we let TypeScript guide us through the rest of the implementation.&lt;/p&gt;

&lt;p&gt;Here we define the shape of a user's wallet, the entire application state (ContextStateType), and most importantly, our actions. We use a discriminated union for ContextActionTypes. This is a fancy term for a pattern where each object type in the union has a common property (in our case, type) that TypeScript can use to figure out the exact shape of the action, including its payload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;contexts/reducerTypes.tsx&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;import&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="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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;WalletType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;availableBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pendingBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;selectedCurrency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// For API Requests Authentication&lt;/span&gt;
    &lt;span class="na"&gt;wallets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WalletType&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Discriminated union for our action types&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ContextActionTypes&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="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;SET_THEME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;SET_SELECTED_CURRENCY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selectedCurrency&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;|&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;LOGIN_USER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userData&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;|&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;SET_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&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="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;LOGOUT_USER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// An action with no payload&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;SET_WALLETS&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wallets&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;|&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;UPDATE_WALLET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WalletType&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;ADD_WALLET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WalletType&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;REMOVE_WALLET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WalletType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;currency&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="c1"&gt;// Type for our reducer function&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ReducerType&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;ContextStateType&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;ContextActionTypes&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;ContextStateType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Type for the context value that will be provided&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ContextValueType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Dispatch&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ContextActionTypes&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;h2&gt;
  
  
  Step 2: Create the Reducer Function (reducer.tsx)
&lt;/h2&gt;

&lt;p&gt;The reducer is the heart of our state logic. It's a pure function that takes the previous state and an action, and returns the next state. We'll also define our initialState here.&lt;/p&gt;

&lt;p&gt;Notice how the switch statement handles each action type we defined earlier. Thanks to our discriminated union, if action.type is 'ADD_WALLET', TypeScript knows that action.payload must be a WalletType object.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;contexts/reducer.tsx&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ContextActionTypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ReducerType&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;./reducerTypes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Initial state for the reducer&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;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;selectedCurrency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;wallets&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReducerType&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;ContextStateType&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;ContextActionTypes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&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;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="kd"&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;SET_THEME&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;theme&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;payload&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;SET_SELECTED_CURRENCY&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;selectedCurrency&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;payload&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;LOGIN_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="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;userData&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;payload&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;SET_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;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;token&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;payload&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;LOGOUT_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="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;userData&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="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
            &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SET_WALLETS&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;wallets&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;payload&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;UPDATE_WALLET&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;wallets&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;wallets&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;wallet&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                    &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&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;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&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;wallet&lt;/span&gt;&lt;span class="p"&gt;,&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;payload&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                        &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;wallet&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;ADD_WALLET&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;walletExists&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;wallets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&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;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&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;walletExists&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// If wallet exists, update it instead of adding a duplicate&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;wallets&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;wallets&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;wallet&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                        &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&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;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&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;payload&lt;/span&gt;
                            &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;wallet&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;// Add new wallet&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;wallets&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;wallets&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;payload&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;REMOVE_WALLET&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;wallets&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;wallets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&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;payload&lt;/span&gt;&lt;span class="p"&gt;)&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Build the Context Provider (appContext.tsx)
&lt;/h2&gt;

&lt;p&gt;Now we tie everything together. In this file, we:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the context using &lt;code&gt;createContext()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create the AppProvider component. This component will use our &lt;code&gt;useReducer&lt;/code&gt; hook to create the state and dispatch function. It then makes them available to all of its children via the &lt;code&gt;&amp;lt;AppContext.Provider&amp;gt;&lt;/code&gt; component.&lt;/li&gt;
&lt;li&gt;Create a custom hook &lt;code&gt;useAppContext()&lt;/code&gt;. This is a best practice that makes consuming the context much cleaner and safer in our components. It abstracts the useContext call and also throws an error if we try to use the context outside of its provider.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;contexts/appContext.tsx&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;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&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;ReactNode&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ContextActionTypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ContextValueType&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;./reducerTypes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;reducer&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;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./reducer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create the context&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AppContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ContextValueType&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Provider component props&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;AppProviderProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Provider component&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;AppProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;AppProviderProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&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="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;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;reducer&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="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ContextValueType&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;dispatch&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;AppContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&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;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="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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/AppContext.Provider&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;// Custom hook to use the context&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;useAppContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ContextValueType&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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppContext&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;context&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;useAppContext must be used within an AppProvider&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="nx"&gt;context&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;
  
  
  Step 4: Wrap Your App (layout.tsx)
&lt;/h2&gt;

&lt;p&gt;For our context to be available everywhere, we need to wrap our entire application with the AppProvider we just created. In a Next.js app, the root layout.tsx is the perfect place to do this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;layout.tsx&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;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppProvider&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;@/contexts/appContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Root Layout 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;RootLayout&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="nl"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&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;html&lt;/span&gt; &lt;span class="nx"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en&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;body&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;/* Wrap the entire app with AppProvider */&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;AppProvider&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;children&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;/AppProvider&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;/body&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;/html&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;
  
  
  Step 5: Using Your New Context! (usage-examples.tsx)
&lt;/h2&gt;

&lt;p&gt;Setup complete! Now for the fun part: using it.&lt;/p&gt;

&lt;p&gt;In any component that's a child of AppProvider, you can now use your custom &lt;code&gt;useAppContext&lt;/code&gt; hook to get access to the state and the dispatch function.&lt;/p&gt;

&lt;p&gt;Here are a few examples of how you might read data and dispatch actions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app/usage-examples.tsx&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;import&lt;/span&gt; &lt;span class="nx"&gt;React&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useAppContext&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;@/contexts/appContext&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;UsageExamples&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;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;useAppContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// --- Accessing State Data ---&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentTheme&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;theme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 'light' or 'dark'&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLoggedIn&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;userData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usdWallet&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;wallets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currency&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;USD&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// --- Dispatching Actions ---&lt;/span&gt;

    &lt;span class="c1"&gt;// Toggle between light and dark theme&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggleTheme&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="nx"&gt;newTheme&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;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="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;SET_THEME&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newTheme&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Set user data when logging in&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loginUser&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="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;LOGIN_USER&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;john.doe@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="c1"&gt;// Try sending the wrong payload and see TypeScript complain!&lt;/span&gt;
        &lt;span class="c1"&gt;// dispatch({ type: 'LOGIN_USER', payload: 'wrong data' });&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Add a new wallet&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;addWallet&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="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;ADD_WALLET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ETH&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;availableBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;2.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;pendingBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.1&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;// Remove wallet by currency&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;removeWallet&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="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;REMOVE_WALLET&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BTC&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// Payload is just a string, as we defined!&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// ... other functions&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;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Welcome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isLoggedIn&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;userData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Guest&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;/h2&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="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Current&lt;/span&gt; &lt;span class="na"&gt;Theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;currentTheme&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="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;toggleTheme&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;Toggle&lt;/span&gt; &lt;span class="nx"&gt;Theme&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="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;loginUser&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;Login&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="cm"&gt;/* ... other UI elements */&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;h2&gt;
  
  
  Bonus: How to Persist State on Refresh?
&lt;/h2&gt;

&lt;p&gt;You're right, there's one problem. If you refresh the page, all your state disappears! We can solve this by syncing our state with the browser's localStorage.&lt;/p&gt;

&lt;p&gt;Here is an enhanced version of appContext.tsx that saves the state to localStorage whenever it changes and loads it back on the initial render. This uses the logic from your appContextWithPersistence.tsx file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;contexts/appContext.tsx&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;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&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;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ContextActionTypes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ContextValueType&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;./reducerTypes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;reducer&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;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./reducer&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;AppContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ContextValueType&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Helper function to get initial state from localStorage&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getPersistedState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ContextStateType&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&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="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Default for server-side rendering&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;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app_theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectedCurrency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app_selectedCurrency&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;initialState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedCurrency&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;userData&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;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app_userData&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;null&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;initialState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userData&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;wallets&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;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app_wallets&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;null&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;initialState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wallets&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="c1"&gt;// Start with defaults&lt;/span&gt;
            &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;selectedCurrency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nx"&gt;wallets&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;error&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;Error loading persisted state:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Helper function to save state to localStorage&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;saveToStorage&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;ContextStateType&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&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="k"&gt;try&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app_theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;theme&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app_selectedCurrency&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedCurrency&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app_userData&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;userData&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app_wallets&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;wallets&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;error&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;Error saving state to localStorage:&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AppProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&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;=&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Pass the getPersistedState function as the third argument to useReducer&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;reducer&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="nx"&gt;getPersistedState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Save state to localStorage whenever it changes&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;saveToStorage&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;state&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;AppContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&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;dispatch&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;children&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;/AppContext.Provider&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useAppContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ContextValueType&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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppContext&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;context&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;useAppContext must be used within an AppProvider&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="nx"&gt;context&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 changes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;getPersistedState&lt;/code&gt; function lazy-initializes our reducer's state with data from localStorage. We pass it as the third argument to useReducer.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;saveToStorage&lt;/code&gt; function is called inside a useEffect hook that listens for any changes to the state object. When a change occurs, it saves the new state to localStorage. We've also taken care not to persist sensitive data like an authentication token by always starting with the initialState.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To use this, you would simply import AppProvider from this persistence file instead of the original appContext.tsx in your layout.tsx.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Download full code on &lt;a href="https://github.com/samowolabi/react-context-typescript-guide" rel="noopener noreferrer"&gt;my GitHub repo&lt;/a&gt;&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;And there you have it! You've successfully built a robust, type-safe, and maintainable state management solution using only React's built-in tools and TypeScript.&lt;/p&gt;

&lt;p&gt;While state management libraries like Redux, Zustand, and Jotai have their place, especially in very large-scale applications, this useContext + useReducer pattern is often more than enough. It gives you centralized logic, predictable state transitions, and top-tier developer experience thanks to TypeScript, all without adding another dependency to your project.&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was crafted with care by &lt;a href="https://samowolabi.com" rel="noopener noreferrer"&gt;Samuel Owolabi&lt;/a&gt;. Find more of his work on his portfolio.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
