<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Code & Cloud with Othmane]]></title><description><![CDATA[Welcome to my corner of the tech universe! I'm Othmane, a passionate explorer of cloud computing and web development. This blog is my digital notebook where i share what i learned.]]></description><link>https://blog.othmanekahtal.me</link><generator>RSS for Node</generator><lastBuildDate>Sun, 17 May 2026 12:01:39 GMT</lastBuildDate><atom:link href="https://blog.othmanekahtal.me/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[AWS Networking Basics For Developers]]></title><description><![CDATA[What this blog covers: Building a VPC from scratch, understanding every component, calculating CIDR ranges, and connecting everything together securely.

What is a VPC?
A Virtual Private Cloud (VPC) i]]></description><link>https://blog.othmanekahtal.me/aws-networking-basics-for-developers</link><guid isPermaLink="true">https://blog.othmanekahtal.me/aws-networking-basics-for-developers</guid><category><![CDATA[AWS]]></category><category><![CDATA[vpc]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[Othmane Kahtal]]></dc:creator><pubDate>Fri, 01 May 2026 23:27:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/617143b30c5e175a1c612899/67dcdec0-8979-4f78-b248-6929fc400b3e.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>What this blog covers:</strong> Building a VPC from scratch, understanding every component, calculating CIDR ranges, and connecting everything together securely.</p>
</blockquote>
<h2>What is a VPC?</h2>
<p>A <strong>Virtual Private Cloud (VPC)</strong> is your own isolated network inside AWS. Everything you deploy — EC2 instances, RDS databases, Lambda functions — lives inside a VPC.  </p>
<img src="https://cdn.hashnode.com/uploads/covers/617143b30c5e175a1c612899/f63621b9-53d2-468d-89aa-8bc6642f470a.png" alt="" style="display:block;margin:0 auto" />

<p><strong>Key facts:</strong></p>
<ul>
<li><p>One VPC per region (but you can have multiple VPCs)</p>
</li>
<li><p>You define its IP address space using CIDR notation</p>
</li>
<li><p>By default it's completely isolated — no internet access until you configure it</p>
</li>
<li><p>Free to create (you pay for resources inside it, not the VPC itself)</p>
</li>
</ul>
<hr />
<h2>CIDR Notation — How IP Ranges Work</h2>
<p>CIDR stands for <strong>Classless Inter-Domain Routing</strong>. It's how you define a block of IP addresses.</p>
<h3>The Format</h3>
<pre><code class="language-plaintext">10.0.0.0 / 16
─────────
IP Address  Prefix length (how many bits are "fixed")
</code></pre>
<p>An IPv4 address is <strong>32 bits</strong> total, written as 4 groups of 8 bits (octets):</p>
<pre><code class="language-plaintext">10   .   0   .   0   .   0
│      │        │       │
8 bits  8 bits  8 bits  8 bits  =  32 bits total
</code></pre>
<h3>The Prefix Length Controls How Many IPs You Get</h3>
<p>The prefix <code>/N</code> means the <strong>first N bits are fixed</strong> (your network). The <strong>remaining (32 - N) bits are free</strong> (your hosts).</p>
<pre><code class="language-plaintext">Number of usable IPs = 2^(32 - N)
</code></pre>
<table>
<thead>
<tr>
<th>CIDR</th>
<th>Fixed bits</th>
<th>Free bits</th>
<th>Total IPs</th>
<th>Usable IPs</th>
<th>Typical use</th>
</tr>
</thead>
<tbody><tr>
<td>/8</td>
<td>8</td>
<td>24</td>
<td>16,777,216</td>
<td>16,777,214</td>
<td>Huge enterprise</td>
</tr>
<tr>
<td>/16</td>
<td>16</td>
<td>16</td>
<td>65,536</td>
<td>65,534</td>
<td>Large VPC</td>
</tr>
<tr>
<td>/20</td>
<td>20</td>
<td>12</td>
<td>4,096</td>
<td>4,094</td>
<td>Medium VPC</td>
</tr>
<tr>
<td>/24</td>
<td>24</td>
<td>8</td>
<td>256</td>
<td>254</td>
<td>Typical subnet</td>
</tr>
<tr>
<td>/26</td>
<td>26</td>
<td>6</td>
<td>64</td>
<td>62</td>
<td>Small subnet</td>
</tr>
<tr>
<td>/28</td>
<td>28</td>
<td>4</td>
<td>16</td>
<td>14</td>
<td>Tiny subnet</td>
</tr>
<tr>
<td>/32</td>
<td>32</td>
<td>0</td>
<td>1</td>
<td>1</td>
<td>Single IP address</td>
</tr>
</tbody></table>
<blockquote>
<p><strong>Why "usable IPs" is always 2 less than total:</strong> AWS reserves 5 IPs per subnet (first 4 + last 1). So a /24 gives 256 - 5 = <strong>251 usable IPs</strong>.</p>
</blockquote>
<h3>Visual: What /16 vs /24 Looks Like</h3>
<pre><code class="language-plaintext">10.0.0.0/16  →  covers  10.0.0.0  to  10.0.255.255
               fixed: [10.0]  free: [0-255].[0-255]

10.0.1.0/24  →  covers  10.0.1.0  to  10.0.1.255
               fixed: [10.0.1]  free: [0-255]
</code></pre>
<h3>The 5 Reserved IPs AWS Takes (per subnet)</h3>
<p>For a subnet <code>10.0.1.0/24</code>:</p>
<table>
<thead>
<tr>
<th>IP Address</th>
<th>Reserved for</th>
</tr>
</thead>
<tbody><tr>
<td>10.0.1.0</td>
<td>Network address</td>
</tr>
<tr>
<td>10.0.1.1</td>
<td>VPC router</td>
</tr>
<tr>
<td>10.0.1.2</td>
<td>AWS DNS</td>
</tr>
<tr>
<td>10.0.1.3</td>
<td>AWS future use</td>
</tr>
<tr>
<td>10.0.1.255</td>
<td>Broadcast address</td>
</tr>
</tbody></table>
<p>So <strong>251 IPs</strong> are actually available for your resources.</p>
<hr />
<h2>How to Calculate IPs for Your VPC and Subnets</h2>
<h3>Step-by-step planning</h3>
<ul>
<li><p><strong>Rule #1 — VPC must be bigger than all subnets combined</strong></p>
</li>
<li><p><strong>Rule #2 — Subnet CIDR must be within the VPC CIDR range</strong></p>
</li>
<li><p><strong>Rule #3 — Subnets in the same VPC cannot overlap</strong></p>
</li>
</ul>
<h3>Example: Planning a production VPC</h3>
<p><strong>Goal:</strong> 2 public subnets + 2 private subnets across 2 Availability Zones</p>
<img src="https://cdn.hashnode.com/uploads/covers/617143b30c5e175a1c612899/b4991ab4-85b4-4a2a-9143-1a4de769c0c3.png" alt="" style="display:block;margin:0 auto" />

<p><strong>Why 10.0.1.0 and not 10.0.0.0 for subnets?</strong> Leave <code>10.0.0.0/24</code> as a spare block. Start subnets at <code>.1</code>, <code>.2</code>, <code>.3</code>, <code>.4</code> so your numbering is clean and you have room to add subnets later.</p>
<h3>The CIDR Calculation Formula</h3>
<pre><code class="language-plaintext">First IP of subnet:  your chosen start address
Last IP of subnet:   first IP + (2^free_bits - 1)

Example: 10.0.3.0/24
  free bits = 32 - 24 = 8
  total IPs = 2^8 = 256
  range: 10.0.3.0 → 10.0.3.255
</code></pre>
<h3>Quick sizing guide</h3>
<table>
<thead>
<tr>
<th>Need</th>
<th>Use this</th>
<th>IPs available</th>
</tr>
</thead>
<tbody><tr>
<td>VPC (large)</td>
<td>/16</td>
<td>65,531</td>
</tr>
<tr>
<td>VPC (medium)</td>
<td>/20</td>
<td>4,091</td>
</tr>
<tr>
<td>Subnet (standard)</td>
<td>/24</td>
<td>251</td>
</tr>
<tr>
<td>Subnet (medium)</td>
<td>/22</td>
<td>1,019</td>
</tr>
<tr>
<td>Subnet (small)</td>
<td>/26</td>
<td>59</td>
</tr>
<tr>
<td>Single resource</td>
<td>/32</td>
<td>1</td>
</tr>
</tbody></table>
<h3>Private IP ranges you must use (RFC 1918)</h3>
<p>AWS VPCs must use private IP ranges — these are NOT routable on the public internet:</p>
<pre><code class="language-plaintext">10.0.0.0    –  10.255.255.255   →  /8  (16M IPs)
172.16.0.0  –  172.31.255.255   →  /12 (1M IPs)
192.168.0.0 –  192.168.255.255  →  /16 (65K IPs)
</code></pre>
<blockquote>
<p><strong>Best practice:</strong> Use <code>10.0.0.0/16</code> for your VPC CIDR. It's the most spacious and clearest for mental mapping.</p>
</blockquote>
<hr />
<h2>Step 1 — Create the VPC</h2>
<h3>What it is</h3>
<p>The outer boundary of your private network in AWS. All subnets, gateways, and resources live inside it.</p>
<h3>How to create it</h3>
<p><strong>AWS Console:</strong></p>
<ol>
<li><p>Go to VPC → Your VPCs → Create VPC</p>
</li>
<li><p>Choose "VPC only" (not VPC and more — we want manual control)</p>
</li>
<li><p>Set the IPv4 CIDR block</p>
</li>
</ol>
<p><strong>Configuration:</strong></p>
<pre><code class="language-plaintext">Name:        my-production-vpc
IPv4 CIDR:   10.0.0.0/16
Tenancy:     Default  (shared hardware — cheaper)
</code></pre>
<h3>What gets created automatically</h3>
<ul>
<li><p>A default route table (main route table)</p>
</li>
<li><p>A default security group</p>
</li>
<li><p>A default network ACL</p>
</li>
</ul>
<blockquote>
<p>⚠️ <strong>Don't use the default VPC for production.</strong> It has permissive defaults and a fixed CIDR. Always create a custom VPC.</p>
</blockquote>
<hr />
<h2>Step 2 — Create Subnets</h2>
<h3>What they are</h3>
<p>Subnets carve your VPC CIDR into smaller chunks, each tied to a specific <strong>Availability Zone</strong>. A subnet is "public" only if its route table has a route to an Internet Gateway — not because of its name.</p>
<pre><code class="language-plaintext">VPC: 10.0.0.0/16
Public Subnet A  (10.0.1.0/24)  [us-east-1a]  ← has route to IGW
Public Subnet B  (10.0.2.0/24)  [us-east-1b]  ← has route to IGW
Private Subnet A (10.0.3.0/24)  [us-east-1a]  ← no route to IGW
Private Subnet B (10.0.4.0/24)  [us-east-1b]  ← no route to IGW
</code></pre>
<h3>How to create them</h3>
<p>For each subnet, go to VPC → Subnets → Create Subnet:</p>
<pre><code class="language-plaintext">Subnet 1 — Public A
  Name:   public-subnet-a
  VPC:    my-production-vpc
  AZ:     us-east-1a
  CIDR:   10.0.1.0/24

Subnet 2 — Public B
  Name:   public-subnet-b
  VPC:    my-production-vpc
  AZ:     us-east-1b
  CIDR:   10.0.2.0/24

Subnet 3 — Private A
  Name:   private-subnet-a
  VPC:    my-production-vpc
  AZ:     us-east-1a
  CIDR:   10.0.3.0/24

Subnet 4 — Private B
  Name:   private-subnet-b
  VPC:    my-production-vpc
  AZ:     us-east-1b
  CIDR:   10.0.4.0/24
</code></pre>
<h3>Enable auto-assign public IP (public subnets only)</h3>
<p>For each public subnet: Actions → Edit Subnet Settings → Enable auto-assign public IPv4 address</p>
<blockquote>
<p><strong>Why two AZs?</strong> If one AZ goes down (rare but it happens), your service keeps running from the other AZ. This is <strong>high availability</strong>.</p>
</blockquote>
<hr />
<h2>Step 3 — Internet Gateway (IGW)</h2>
<h3>What it is</h3>
<p>The door between your VPC and the internet. Without it, nothing in your VPC can reach the outside world — even resources in "public" subnets.</p>
<pre><code class="language-plaintext">Internet
  │
[Internet Gateway]  ← attached to VPC
  │
[VPC]
</code></pre>
<h3>How it works</h3>
<ul>
<li><p><strong>Two-way:</strong> allows inbound AND outbound internet traffic</p>
</li>
<li><p><strong>Stateless at the gateway level</strong> (Security Groups handle stateful inspection)</p>
</li>
<li><p><strong>Horizontally scalable:</strong> AWS manages it — no bandwidth limits, no single point of failure</p>
</li>
<li><p><strong>One per VPC</strong></p>
</li>
</ul>
<h3>How to create and attach it</h3>
<ol>
<li><p>VPC → Internet Gateways → Create Internet Gateway</p>
</li>
<li><p>Name it <code>my-igw</code></p>
</li>
<li><p>After creation: Actions → Attach to VPC → select <code>my-production-vpc</code></p>
</li>
</ol>
<blockquote>
<p>⚠️ <strong>Two-step process!</strong> Creating the IGW is not enough. You must explicitly <strong>attach</strong> it to your VPC. Then you must add a <strong>route</strong> pointing to it in your public subnet route table.</p>
</blockquote>
<hr />
<h2>Step 4 — Route Tables</h2>
<h3>What they are</h3>
<p>A route table is a set of rules that tells traffic "where to go." Every subnet must be associated with exactly one route table.</p>
<h3>The two route tables you need</h3>
<p><strong>Public Route Table</strong> (for public subnets):</p>
<pre><code class="language-plaintext">Destination      Target
10.0.0.0/16      local      ← talk to any resource in the VPC
0.0.0.0/0        igw-xxxxx  ← everything else → internet
</code></pre>
<p><strong>Private Route Table</strong> (for private subnets):</p>
<pre><code class="language-plaintext">Destination      Target
10.0.0.0/16      local      ← talk to any resource in the VPC
0.0.0.0/0        nat-xxxxx  ← everything else → NAT Gateway (outbound only)
</code></pre>
<h3>How to create them</h3>
<p><strong>Create public route table:</strong></p>
<ol>
<li><p>VPC → Route Tables → Create Route Table</p>
</li>
<li><p>Name: <code>public-rt</code>, VPC: <code>my-production-vpc</code></p>
</li>
<li><p>Routes tab → Edit routes → Add route: <code>0.0.0.0/0</code> → Target: Internet Gateway → <code>my-igw</code></p>
</li>
<li><p>Subnet Associations tab → Associate <code>public-subnet-a</code> and <code>public-subnet-b</code></p>
</li>
</ol>
<p><strong>Create private route table:</strong></p>
<ol>
<li><p>Create Route Table: <code>private-rt</code>, VPC: <code>my-production-vpc</code></p>
</li>
<li><p>Add route: <code>0.0.0.0/0</code> → Target: NAT Gateway (do this after creating the NAT GW)</p>
</li>
<li><p>Associate <code>private-subnet-a</code> and <code>private-subnet-b</code></p>
</li>
</ol>
<h3>Route table flow diagram</h3>
<pre><code class="language-plaintext">Incoming request from internet:
  0.0.0.0/0 → [IGW] → [Public subnet] → ALB → EC2 in private subnet

EC2 making outbound call:
  EC2 → [Private subnet route table] → 0.0.0.0/0 → [NAT GW] → [IGW] → Internet
  Response comes back through NAT GW → EC2 (NAT handles translation)
</code></pre>
<hr />
<h2>Step 5 — NAT Gateway</h2>
<h3>What it is</h3>
<p>NAT = Network Address Translation. The NAT Gateway gives private subnet resources <strong>one-way internet access</strong> — they can initiate outbound connections, but the internet cannot initiate connections to them.</p>
<pre><code class="language-plaintext">Private EC2 (10.0.3.10)
    │ wants to reach api.github.com
    
NAT Gateway (in public subnet, has Elastic IP: 54.x.x.x)
    │ translates 10.0.3.10 → 54.x.x.x
   
Internet Gateway
    │
    
api.github.com
    │ response goes back to 54.x.x.x (Elastic IP)
    
NAT Gateway translates back → delivers to 10.0.3.10

⛔ Someone trying to reach 10.0.3.10 FROM the internet? Blocked.
</code></pre>
<h3>Where it lives</h3>
<p><strong>In the public subnet</strong> — it needs internet access itself to forward traffic out.</p>
<h3>How to create it</h3>
<ol>
<li><p>VPC → NAT Gateways → Create NAT Gateway</p>
</li>
<li><p>Subnet: <code>public-subnet-a</code> (must be public!)</p>
</li>
<li><p>Connectivity: Public</p>
</li>
<li><p>Elastic IP: Allocate Elastic IP (click the button)</p>
</li>
<li><p>Create</p>
</li>
</ol>
<p>Then add to your private route table:</p>
<pre><code class="language-plaintext">0.0.0.0/0  →  nat-xxxxxxxx
</code></pre>
<h3>Cost warning</h3>
<p>NAT Gateway costs ~\(0.045/hour + \)0.045/GB data. For dev environments, consider a <strong>NAT Instance</strong> (cheaper but requires manual management).</p>
<hr />
<h2>Step 6 — Security Groups</h2>
<h3>What they are</h3>
<p>Virtual firewalls attached to <strong>individual resources</strong> (EC2, RDS, ALB, etc.). They control what traffic is allowed in (inbound) and out (outbound).</p>
<h3>Key properties</h3>
<table>
<thead>
<tr>
<th>Property</th>
<th>Detail</th>
</tr>
</thead>
<tbody><tr>
<td>Stateful</td>
<td>Allow inbound → response automatically allowed out</td>
</tr>
<tr>
<td>Allow-only</td>
<td>No deny rules — only allow rules</td>
</tr>
<tr>
<td>Default</td>
<td>All inbound DENIED, all outbound ALLOWED</td>
</tr>
<tr>
<td>Scope</td>
<td>Attached per resource, not per subnet</td>
</tr>
<tr>
<td>Multiple</td>
<td>One resource can have up to 5 security groups</td>
</tr>
</tbody></table>
<h3>Stateful explained</h3>
<pre><code class="language-plaintext">Client → [HTTPS port 443, ALLOWED] → EC2
EC2   → [Response traffic]         → Client  ✅ automatic, no rule needed

Compare with NACL (stateless):
EC2   → [Response on port 52341]   → Client  ❌ must explicitly allow ephemeral ports
</code></pre>
<h3>The 3-tier Security Group pattern</h3>
<pre><code class="language-plaintext">Internet
    │  (HTTPS 443)
   
[SG-ALB]  →  inbound: 443 from 0.0.0.0/0
              outbound: 8080 to SG-App
    │  (HTTP 8080)
   
[SG-App]  →  inbound: 8080 from SG-ALB
              outbound: 3306 to SG-DB
    │  (MySQL 3306)
   
[SG-DB]   →  inbound: 3306 from SG-App
              outbound: all (default)
</code></pre>
<h3>How to create Security Groups</h3>
<p><strong>SG-ALB (Load Balancer):</strong></p>
<pre><code class="language-plaintext">Name: sg-alb
VPC:  my-production-vpc

Inbound rules:
  Type: HTTPS  Port: 443   Source: 0.0.0.0/0   (internet traffic)
  Type: HTTP   Port: 80    Source: 0.0.0.0/0   (redirect to HTTPS)

Outbound rules:
  Type: Custom TCP  Port: 8080  Destination: sg-app  (to app servers)
</code></pre>
<p><strong>SG-App (EC2 App Servers):</strong></p>
<pre><code class="language-plaintext">Name: sg-app
VPC:  my-production-vpc

Inbound rules:
  Type: Custom TCP  Port: 8080  Source: sg-alb  ← reference by SG ID!

Outbound rules:
  Type: MySQL/Aurora  Port: 3306  Destination: sg-db
  Type: HTTPS         Port: 443   Destination: 0.0.0.0/0  (for API calls via NAT)
</code></pre>
<p><strong>SG-DB (RDS Database):</strong></p>
<pre><code class="language-plaintext">Name: sg-db
VPC:  my-production-vpc

Inbound rules:
  Type: MySQL/Aurora  Port: 3306  Source: sg-app  ← only app servers can reach it!

Outbound rules:
  (leave default — all outbound allowed)
</code></pre>
<h3>Why reference SGs by ID instead of IP?</h3>
<pre><code class="language-plaintext">If you used IP:   Source = 10.0.3.15
                  → scale to 10 EC2s = update rule 10 times

If you used SG:   Source = sg-app
                  → any EC2 with sg-app attached = always works
                  → scale to 100 EC2s = no changes needed ✅
</code></pre>
<hr />
<h2>Step 7 — Network ACLs (Extra Layer)</h2>
<h3>What they are</h3>
<p>Network ACLs (NACLs) are <strong>subnet-level firewalls</strong>. They're stateless (must allow return traffic explicitly) and support both allow and deny rules.</p>
<h3>SG vs NACL comparison</h3>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Security Group</th>
<th>Network ACL</th>
</tr>
</thead>
<tbody><tr>
<td>Level</td>
<td>Resource (EC2, RDS...)</td>
<td>Subnet</td>
</tr>
<tr>
<td>State</td>
<td>Stateful</td>
<td>Stateless</td>
</tr>
<tr>
<td>Rules</td>
<td>Allow only</td>
<td>Allow AND Deny</td>
</tr>
<tr>
<td>Rule evaluation</td>
<td>All rules at once</td>
<td>In order (lowest wins)</td>
</tr>
<tr>
<td>Default</td>
<td>Deny all inbound</td>
<td>Allow all in and out</td>
</tr>
<tr>
<td>Use for</td>
<td>Primary defense</td>
<td>Extra layer / IP blocks</td>
</tr>
</tbody></table>
<h3>Private subnet NACL example</h3>
<pre><code class="language-plaintext">Inbound rules:
  Rule 100  ALLOW  TCP  10.0.0.0/16  all ports  ← VPC-internal traffic only
  Rule *    DENY   ALL  0.0.0.0/0               ← block everything else

Outbound rules:
  Rule 100  ALLOW  TCP  10.0.0.0/16  all ports  ← VPC-internal
  Rule 200  ALLOW  TCP  0.0.0.0/0   1024-65535  ← ephemeral ports for responses
  Rule *    DENY   ALL  0.0.0.0/0               ← block everything else
</code></pre>
<blockquote>
<p><strong>Ephemeral ports (1024–65535):</strong> When a server responds to a client, it uses a random high port. Since NACLs are stateless, you must explicitly allow these return ports in your outbound rules.</p>
</blockquote>
<hr />
<h2>Full Architecture — How It All Connects</h2>
<img src="https://cdn.hashnode.com/uploads/covers/617143b30c5e175a1c612899/32e19fae-4a65-4b9c-93e0-d97c94347cd4.png" alt="" style="display:block;margin:0 auto" />

<pre><code class="language-plaintext">Traffic flow (inbound request):
  Internet → IGW → ALB (public subnet) → EC2 (private subnet) → RDS (private subnet)

Traffic flow (outbound from EC2):
  EC2 → Private route table → NAT GW (public subnet) → IGW → Internet
</code></pre>
<h3>Request lifecycle step by step</h3>
<pre><code class="language-plaintext">1. User hits https://myapp.com
        │
2. DNS resolves to ALB's public IP
        │
3. Internet → IGW → ALB (SG-ALB allows port 443 from 0.0.0.0/0)
        │
4. ALB → EC2 (SG-App allows port 8080 from SG-ALB)
        │
5. EC2 → RDS (SG-DB allows port 3306 from SG-App)
        │
6. RDS returns data → EC2 → ALB → User

[EC2 calling external API]
7. EC2 → private route table → 0.0.0.0/0 → NAT GW → IGW → api.example.com
8. Response → IGW → NAT GW → EC2  (internet can't initiate step 7 in reverse)
</code></pre>
<hr />
<h2>Security Group Cheat Sheet</h2>
<table>
<thead>
<tr>
<th>Resource</th>
<th>Inbound</th>
<th>Source</th>
</tr>
</thead>
<tbody><tr>
<td>Load Balancer</td>
<td>443 (HTTPS), 80 (HTTP)</td>
<td>0.0.0.0/0</td>
</tr>
<tr>
<td>EC2 app server</td>
<td>8080 (or your app port)</td>
<td>SG-ALB</td>
</tr>
<tr>
<td>RDS MySQL</td>
<td>3306</td>
<td>SG-App</td>
</tr>
<tr>
<td>RDS Postgres</td>
<td>5432</td>
<td>SG-App</td>
</tr>
<tr>
<td>Redis</td>
<td>6379</td>
<td>SG-App</td>
</tr>
<tr>
<td>Bastion host</td>
<td>22 (SSH)</td>
<td>Your IP only</td>
</tr>
<tr>
<td>EC2 via Bastion</td>
<td>22 (SSH)</td>
<td>SG-Bastion</td>
</tr>
</tbody></table>
<hr />
<h2>Common Mistakes</h2>
<p>❌ Subnet is "public" because I named it public</p>
<p>✅ A subnet is public ONLY if its route table routes 0.0.0.0/0 to an IGW</p>
<p>❌ I created the IGW but resources still can't reach the internet</p>
<p>✅ Did you: (1) attach IGW to VPC? (2) add the route in the route table?</p>
<p>❌ My private EC2 can't download packages</p>
<p>✅ Check: (1) NAT Gateway exists? (2) private route table has 0.0.0.0/0 → NAT? (3) NAT Gateway is in a PUBLIC subnet?</p>
<p>❌ I set SG inbound source to the app server's IP address</p>
<p>✅ Set source to the SG ID (sg-xxxxxxxx) — it scales automatically</p>
<p>❌ NACL is blocking my traffic even though SG allows it</p>
<p>✅ NACLs are stateless — add outbound rule for ephemeral ports 1024-65535</p>
<p>❌ My subnets overlap (10.0.1.0/24 and 10.0.1.128/24)</p>
<p>✅ AWS will reject this. Plan CIDR ranges before creating subnets.</p>
<p>❌ I used /32 for my VPC CIDR</p>
<p>✅ VPC minimum is /28, maximum is /16. Recommended: /16 or /20.</p>
<hr />
<h3>NAT Gateway MUST live in a public subnet</h3>
<p>Here's the core logic: the NAT Gateway's entire job is to <strong>forward your private resources' traffic out to the internet</strong>. To do that, it needs internet access itself. And to have internet access, it must be in a subnet whose route table points to the Internet Gateway.</p>
<p>That's the definition of a public subnet.</p>
<pre><code class="language-plaintext">Private EC2 wants to reach github.com
        │
       
Private route table: 0.0.0.0/0 → NAT Gateway
        │
       
NAT Gateway (sitting in PUBLIC subnet, has Elastic IP)
        │
       
Public route table: 0.0.0.0/0 → IGW
        │
       
Internet Gateway → github.com
</code></pre>
<p>If you put the NAT Gateway in a <strong>private subnet</strong>, it would have no route to the internet itself — so it could never forward traffic anywhere. It would be completely useless, like a postal relay office with no road connecting it to the outside world.</p>
<h2>What makes the NAT Gateway "safe" even though it's public</h2>
<p>People get nervous hearing "NAT Gateway is in the public subnet" — but here's why it's not a security risk:</p>
<p>The NAT Gateway <strong>does not have a Security Group</strong>. It's not a compute resource you SSH into. It's a managed AWS appliance that only does one thing: translate source IPs. It never accepts unsolicited inbound connections from the internet — it only handles return traffic for connections that your private resources initiated.</p>
<p>The protection of your private EC2 and RDS instances comes from the fact that they have <strong>no public IP</strong> and <strong>no route from the internet reaching them</strong>. The NAT Gateway is just a forwarding pipe — it's not the thing being protected.</p>
<pre><code class="language-plaintext">Internet trying to reach your private EC2:
  Internet → IGW → ... no route to 10.0.3.x exists in public subnet → DROPPED

Your private EC2 calling the internet:
  EC2 → NAT GW → IGW → Internet → response back to NAT GW → EC2  ✅
</code></pre>
<h2>One more practical tip</h2>
<p>You should create <strong>one NAT Gateway per Availability Zone</strong>, not one total. If you have subnets in us-east-1a and us-east-1b, create a NAT Gateway in each public subnet, then point each private subnet's route table to the NAT Gateway in the same AZ. If you share a single NAT Gateway across AZs and that AZ goes down, all your private instances lose internet access simultaneously — which defeats the whole purpose of multi-AZ architecture.</p>
]]></content:encoded></item><item><title><![CDATA[Getting Started with Amazon S3: Core Concepts and Benefits]]></title><description><![CDATA[The most widely used service in AWS is Amazon S3, which was launched in March 2006 as one of AWS's first services. It provides a ton of features, offering scalability and seamless integration with other services while remaining easy to use and perfor...]]></description><link>https://blog.othmanekahtal.me/getting-started-with-amazon-s3-core-concepts-and-benefits</link><guid isPermaLink="true">https://blog.othmanekahtal.me/getting-started-with-amazon-s3-core-concepts-and-benefits</guid><category><![CDATA[S3]]></category><category><![CDATA[S3 static website hosting]]></category><category><![CDATA[AWS]]></category><category><![CDATA[storage]]></category><dc:creator><![CDATA[Othmane Kahtal]]></dc:creator><pubDate>Fri, 06 Dec 2024 14:56:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1732800681513/829993ef-ff50-438a-9d5c-09966ac2143b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The most widely used service in AWS is Amazon S3, which was launched in March 2006 as one of AWS's first services. It provides a ton of features, offering scalability and seamless integration with other services while remaining easy to use and performant. S3 is a crucial service that allows companies to delegate their storage needs to AWS at a very affordable price point, making it accessible for most companies, including startups, whether they need to store things publicly or privately. As a developer, mastering S3 is essential. In this first article of the series, I'll cover the core concepts, benefits, common use cases, and key considerations.</p>
<p>Before we start, I'm assuming you have basic knowledge of AWS IAM, know how to navigate the AWS console, and are familiar with using the AWS CLI.</p>
<h2 id="heading-understanding-core-concepts">Understanding core concepts:</h2>
<h3 id="heading-what-is-aws-s3">What is AWS S3?</h3>
<p>Amazon S3 is defined by AWS as an industry-leading object storage service that sets the standard for scalability, data availability, security, and performance. The service enables organizations to store and protect unlimited amounts of data for diverse use cases, ranging from data lakes and websites to cloud-native applications and machine learning initiatives. Amazon S3 is engineered to deliver exceptional reliability with a designed durability of 99.999999999% (11 9's), serving millions of customers globally with their data storage needs. This robust infrastructure supports various operational requirements, including data analytics, archival storage, and backup solutions, making it a cornerstone of modern cloud architecture.</p>
<h3 id="heading-why-choose-s3">Why Choose S3?</h3>
<p>Amazon S3 has established itself as the high standard for cloud storage, by offering a combination of reliability, scalability, and cost-effectiveness that makes it an ideal choice for businesses of all sizes. At its core, S3's architecture delivers exceptional durability—99.999999999% (11 nines)—<strong>meaning that if you store 10 million objects, you can expect to lose only one object every 10,000 years.</strong> This durability is achieved through automatic replication across multiple facilities within your chosen region, ensuring your data remains safe even if entire data centers fail.</p>
<p>Beyond durability, S3's availability guarantee of 99.99% means your data is accessible when you need it, which is crucial for business-critical applications. The service scales automatically without any intervention. Whether you're storing a few gigabytes or several petabytes, S3 grows with your needs without requiring any capacity planning or hardware provisioning.</p>
<p>The pay-as-you-go pricing model transforms how organizations approach storage costs. Rather than making large upfront investments in storage hardware that might go underutilized, you pay only for the storage you actually use. This model is particularly beneficial for startups and growing businesses, as it allows them to start small and scale their storage costs in direct proportion to their growth.</p>
<p>S3's deep integration with <strong>other AWS services</strong> creates a powerful ecosystem for building sophisticated applications. You can trigger Lambda functions when new files arrive, process data with Amazon EMR, or deliver content through CloudFront with just a few clicks. This seamless integration eliminates the complexity of building and maintaining custom integration points between different services.</p>
<p>Security is another cornerstone of S3's design. The service provides multiple layers of security controls, including bucket policies, IAM policies, encryption at rest and in transit, and versioning to protect against accidental deletions or modifications. These features help organizations meet various compliance requirements while maintaining the flexibility to implement security best practices.</p>
<h3 id="heading-what-is-object">What is Object?</h3>
<p>An object in Amazon S3 represents any data you wish to store and retrieve, including static files, backups, unstructured data, log files, and more. Objects can range in size from zero bytes to 5 terabytes. Each object consists of two main components: the actual file and its associated metadata. The metadata contains essential information such as content type and additional key-value data. Amazon S3 supports two distinct types of metadata: system-defined and user-defined. <strong>System-defined</strong> metadata includes automatically tracked properties such as the object's creation date, size, and storage class. <strong>User-defined</strong> metadata consists of optional name-value pairs that you can specify when uploading an object, allowing you to add custom attributes that suit your specific needs.</p>
<h3 id="heading-what-is-bucket">What is Bucket?</h3>
<p>In Amazon S3, a bucket serves as container for storing objects. Think of a bucket as a top-level directory that helps organize and control access to your objects. Each bucket must have a globally <strong>unique name</strong> across all AWS accounts worldwide, as this name becomes part of the URL used to access your objects. When creating a bucket, you select a specific AWS Region where it will reside, which affects factors such as latency, cost, and compliance requirements. Buckets also play a role in access control and security management—you can configure bucket-level permissions, <strong>enable versioning, set lifecycle policies, and implement encryption settings</strong>. By default, all buckets and objects are <strong>private</strong>, but you have the flexibility to make either the entire bucket public or specific objects within it using bucket policies and Access Control Lists (<strong>ACLs</strong>). Bucket policies operate at the bucket level, allowing you to grant broad permissions, while ACLs provide more granular control at the object level—letting you make individual files publicly accessible even if the bucket itself remains private. This dual-layer approach to access control gives you precise control over your data's visibility, While S3 provides unlimited storage capacity, there are some service limits to consider: an AWS account can have up to 100 buckets by default (though this limit can be increased), and objects within a bucket can't be nested more than approximately 1,000 levels deep.</p>
<h3 id="heading-organizing-objects-with-keysprefixes">Organizing Objects with Keys/Prefixes</h3>
<p>In Amazon S3, every object needs a unique identifier within its bucket, and this identifier is called a key. Think of a key as the complete path to your object, similar to how you might locate a file on your computer. For example, if you store a file called "profile.jpg" in a folder named "images" within your "users" folder, its key would be "users/images/profile.jpg".</p>
<p>The prefix is any part of the key that helps create a logical grouping of objects, much like folders in a traditional file system. In our example, both "users/" and "users/images/" are prefixes. However, it's important to understand that S3 doesn't actually have a folder hierarchy—it just uses these prefixes to create the illusion of folders for easier organization. When you see folders in the S3 console, you're actually looking at a user-friendly visualization of these prefixes.</p>
<p>Let's consider a practical example. Imagine you're storing user data for a social media application. You might organize your objects with keys like:</p>
<ul>
<li><p>"users/john/profile.jpg"</p>
</li>
<li><p>"users/john/posts/2024/01/post1.jpg"</p>
</li>
<li><p>"users/sarah/profile.jpg"</p>
</li>
<li><p>"users/sarah/posts/2024/01/post2.jpg"</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733495844163/95fd27c2-1f7c-4d7f-9e89-f630d9324fa5.png" alt class="image--center mx-auto" /></p>
<p>In this structure, "users/", "users/john/", and "users/john/posts/2024/01/" are all prefixes that help organize the objects logically. This organization becomes particularly valuable when you want to:</p>
<ul>
<li><p>List all objects for a specific user</p>
</li>
<li><p>Find all posts from a particular month</p>
</li>
<li><p>Apply permissions to groups of related objects</p>
</li>
<li><p>Set lifecycle policies for certain types of content</p>
</li>
</ul>
<p>Understanding keys and prefixes is important because they affect how you interact with your objects, influence performance, and impact your application's design. For instance, if you expect to have millions of objects, choosing an effective key naming strategy becomes essential for optimal performance.</p>
<h3 id="heading-urls-and-endpoints">URLs and endpoints</h3>
<p>Every object in Amazon S3 has its own unique URL that serves as its web address. These URLs follow a consistent structure that reflects how S3 organizes your data. The standard format for an S3 URL looks like this: <a target="_blank" href="https://bucket-name.s3.region.amazonaws.com/object-key">https://bucket-name.s3.region.amazonaws.com/object-key</a>. For instance, if you have an image called "profile.jpg" in a bucket named "my-company-assets" in the us-east-1 region, its URL would be: <a target="_blank" href="https://my-company-assets.s3.us-east-1.amazonaws.com/profile.jpg">https://my-company-assets.s3.us-east-1.amazonaws.com/profile.jpg</a>.</p>
<p>S3 also provides different types of endpoints to suit various access patterns and security requirements. The most common endpoint types are Virtual-hosted style and Path-style. Virtual-hosted style URLs place the bucket name as part of the domain name (as shown in the example above), while Path-style URLs place the bucket name after the domain (like <a target="_blank" href="https://s3.region.amazonaws.com/bucket-name/object-key">https://s3.region.amazonaws.com/bucket-name/object-key</a>). Though Path-style endpoints are being phased out for newer regions, they're still supported in older ones.</p>
<h2 id="heading-common-use-cases">Common Use Cases</h2>
<p>Let's explore these common use cases and important considerations to keep in mind when implementing S3:</p>
<h3 id="heading-static-website-hosting">Static website hosting</h3>
<p>One of the most popular uses of S3 is static website hosting. Whether you're hosting a simple portfolio site or a complex web application's assets, S3 can serve your HTML, CSS, JavaScript, images, and other static content reliably and efficiently. When combined with Amazon CloudFront, S3 becomes a powerful content delivery platform, ensuring your content reaches users quickly regardless of their location.</p>
<h3 id="heading-data-lakes-and-big-data-analytics">Data lakes and big data analytics</h3>
<p>Data lakes represent another significant use case, where S3 serves as the foundation for big data analytics. Organizations store vast amounts of structured and unstructured data in S3, then use services like Amazon Athena or Amazon EMR to analyze this data without moving it to separate analytics platforms. This approach eliminates the need for complex extract, transform, and load (ETL) processes and allows for more flexible data analysis.</p>
<h3 id="heading-backup-and-disaster-recovery">Backup and disaster recovery</h3>
<p>Backup and disaster recovery solutions frequently rely on S3's durability and availability guarantees. Companies can automatically backup their databases, file systems, and application data to S3, knowing it's protected across multiple facilities. The various storage classes available in S3, from Standard to Glacier, allow organizations to optimize costs based on how quickly they need to access their backups.</p>
<h3 id="heading-application-asset-storage">Application asset storage</h3>
<p>Mobile and cloud-native applications often use S3 as their primary storage backend. Whether storing user-generated content, application assets, or log files, S3's scalability ensures the storage grows seamlessly with the application's user base. The robust security features and fine-grained access controls make it suitable for handling sensitive user data while maintaining compliance requirements.</p>
<p>When implementing S3 for any of these use cases, several key considerations should guide your design decisions. First, consider your data access patterns – how frequently you'll need to retrieve data and how quickly you need it – as this affects your choice of storage class and potentially your costs. Security should be a primary concern; implement the principle of least privilege using IAM roles and bucket policies, and decide whether you need server-side encryption or additional security measures.</p>
<p>Cost optimization is another crucial consideration. While S3's pay-as-you-go model is attractive, costs can accumulate through storage, requests, and data transfer. Implement lifecycle policies to move infrequently accessed data to cheaper storage tiers and set up monitoring to track usage patterns and identify optimization opportunities. Finally, consider your application's performance requirements and implement best practices like enabling transfer acceleration or using appropriate naming schemes for high request rates.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>We've covered the core concepts of objects, buckets, keys, and endpoints, providing you with the essential knowledge needed to understand how S3 organizes and manages your data, however, this is just the beginning of our journey into Amazon S3. In our next article, we'll dive deeper into S3's security features, exploring how to protect your data using IAM policies, bucket policies, and encryption options. We'll examine access control mechanisms in detail, including how to safely configure public access when needed and how to implement fine-grained permissions using ACLs. Understanding these security concepts is crucial for building secure and compliant storage solutions in the cloud.</p>
]]></content:encoded></item><item><title><![CDATA[Building Multilingual React Apps: Practical Guide to i18next and RTL Support]]></title><description><![CDATA[In previous article, we understood why localization and internationalization are crucial parts that should be handled to create globally-ready products or apps. Now, in this article, we'll jump into a practical guide about how you can set up and conf...]]></description><link>https://blog.othmanekahtal.me/building-multilingual-react-apps-practical-guide-to-i18next-and-rtl-support</link><guid isPermaLink="true">https://blog.othmanekahtal.me/building-multilingual-react-apps-practical-guide-to-i18next-and-rtl-support</guid><category><![CDATA[i18next]]></category><category><![CDATA[i18n]]></category><category><![CDATA[localization]]></category><category><![CDATA[internationalization]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Othmane Kahtal]]></dc:creator><pubDate>Tue, 19 Nov 2024 00:30:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/l68Z6eF2peA/upload/dccdeba73abf095736a1ebbaf409e71b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In <a target="_blank" href="https://blog.othmane.me/why-internationalization-and-localization-are-no-longer-optional-for-modern-apps">previous article</a>, we understood why localization and internationalization are crucial parts that should be handled to create globally-ready products or apps. Now, in this article, we'll jump into a practical guide about how you can set up and configure your React app for localization using the i18next framework, and how we can adapt the UI for <strong>RTL</strong> languages using Tailwind CSS. Let's begin.</p>
<h2 id="heading-setup-react-project">Setup React Project</h2>
<h3 id="heading-create-new-project-amp-configure-tailwindcss">Create new project &amp; configure Tailwind.css</h3>
<p>Before we dive in, you'll need a React project with Tailwind CSS configured. Feel free to set up the project in your preferred way, or to save time, you can clone <a target="_blank" href="https://github.com/othmanekahtal/react-i18n-practical-guide">repository</a> and start from the <code>base</code> branch</p>
<h3 id="heading-install-necessary-dependencies">Install necessary dependencies</h3>
<p>We'll need these key dependencies:</p>
<ul>
<li><p><code>i18next</code> - core internationalization framework</p>
</li>
<li><p><code>react-i18next</code> - React bindings for i18next</p>
</li>
<li><p><code>i18next-browser-languagedetector</code> - auto-detects user's language preference from browser settings (optional)</p>
</li>
</ul>
<p><strong>📒 Note:</strong> The language detector is optional if you prefer implementing your own language switcher, which we'll also cover in this guide</p>
<p>💡 <strong>Quick Link:</strong> For this section's code, check out the <a target="_blank" href="https://github.com/othmanekahtal/react-i18n-practical-guide/tree/base"><code>base</code></a> branch</p>
<h2 id="heading-basic-i18next-configuration">Basic i18next Configuration</h2>
<h3 id="heading-initialize-i18next">Initialize i18next</h3>
<p>Let's start with the basic configuration. To understand how i18next works, we first need to create a configuration file. This file needs to be bundled, so we'll import it at the root of our app</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> i18n <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next"</span>;
<span class="hljs-keyword">import</span> { initReactI18next } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;
<span class="hljs-keyword">import</span> LanguageDetector <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next-browser-languagedetector"</span>;

i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    debug: <span class="hljs-literal">true</span>,
    lng:<span class="hljs-string">"en"</span>,
    fallbackLng: <span class="hljs-string">"en"</span>,
    interpolation: {
      escapeValue: <span class="hljs-literal">false</span>, <span class="hljs-comment">// not needed for react as it escapes by default</span>
    },
    resources: {
      en: {
        translation: {
          welcome: <span class="hljs-string">"Hi"</span>,
        },
      },
      de: {
        translation: {
          welcome: <span class="hljs-string">"Hallo"</span>,
        },
      },
      ar: {
        translation: {
          welcome: <span class="hljs-string">"مرحبا"</span>,
        },
      },
    },
  });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> i18n;
</code></pre>
<p>root app file: <code>main.tsx</code> (in my case)</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { StrictMode } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { createRoot } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-dom/client"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./index.css"</span>;

<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">"./app"</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">"./app/i18n"</span>;

createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"root"</span>)!).render(
  &lt;StrictMode&gt;
    &lt;App /&gt;
  &lt;/StrictMode&gt;
);
</code></pre>
<p>Now let's test our localization setup. Open the <code>App</code> file and verify that everything works correctly</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useTranslation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { t } = useTranslation();
  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"h-screen flex justify-center items-center"</span>&gt;
      &lt;h1 className=<span class="hljs-string">"text-red-600 font-bold"</span>&gt;{t(<span class="hljs-string">"welcome"</span>)}&lt;/h1&gt;
    &lt;/div&gt;
  );
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>if you see a message like this in your browser's console, congratulations! 🎉 The localization setup is working correctly</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731600368415/1359a92e-94ef-4de7-9d70-564f7d75ef6a.png" alt class="image--center mx-auto" /></p>
<p>and you get this result in the interface:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731601636026/7ed29dd9-0ec3-4738-a08f-2f10c13420da.png" alt class="image--center mx-auto" /></p>
<p>Now let's explore the key configuration options:</p>
<ul>
<li><p><code>use</code>: Loads additional i18next plugins (<a target="_blank" href="https://www.i18next.com/overview/plugins-and-utils">more details</a>)</p>
</li>
<li><p><code>debug</code>: Enables debugging in development (<a target="_blank" href="https://www.i18next.com/overview/configuration-options#logging">more details</a>)</p>
</li>
<li><p><code>fallbackLng</code>: Sets default language when translations are missing (<a target="_blank" href="https://www.i18next.com/principles/fallback">more details</a>)<br />  <code>lng</code>: Sets default language</p>
</li>
<li><p><code>interpolation</code>: Handles dynamic values in translations (<a target="_blank" href="https://www.i18next.com/translation-function/interpolation">more details</a>)</p>
</li>
<li><p><code>resources</code>: Defines translations in key-value format (<a target="_blank" href="https://www.i18next.com/overview/configuration-options#languages-namespaces-resources">more details</a>)</p>
</li>
</ul>
<p>Let's dive into implementing localization:</p>
<ul>
<li><p><code>useTranslation</code>: The main hook provided by <code>i18next</code> that returns:</p>
<ul>
<li><p><code>t</code>: Function that translates your content</p>
</li>
<li><p><code>i18n</code>: Instance that manages language switching and other configuration options</p>
</li>
</ul>
</li>
</ul>
<p>i18next provides multiple ways to handle translations based on your needs:</p>
<ul>
<li><p><code>withTranslation</code>: A Higher-Order Component (HOC) that gives your component access to <code>t</code> and <code>i18n</code> (<a target="_blank" href="https://react.i18next.com/latest/withtranslation-hoc">more details</a>)</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
  <span class="hljs-keyword">import</span> { withTranslation } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-i18next'</span>;

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params">{ t, i18n }</span>) </span>{
    <span class="hljs-keyword">return</span> &lt;div className=<span class="hljs-string">"h-screen flex justify-center items-center"</span>&gt;
        &lt;h1 className=<span class="hljs-string">"text-red-600 font-bold"</span>&gt;{t(<span class="hljs-string">"welcome"</span>)}&lt;/h1&gt;
      &lt;/div&gt;
  }

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> withTranslation()(App);
</code></pre>
</li>
<li><p><code>Translation</code>: A render prop component that provides <code>t</code> function and <code>i18n</code> instance to your component (<a target="_blank" href="https://react.i18next.com/latest/translation-render-prop">more details</a>)</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
  <span class="hljs-keyword">import</span> { Translation } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-i18next'</span>;

  <span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
      &lt;Translation&gt;
        {
          <span class="hljs-function">(<span class="hljs-params">t, { i18n }</span>) =&gt;</span> &lt;div className=<span class="hljs-string">"h-screen flex justify-center items-center"</span>&gt;
        &lt;h1 className=<span class="hljs-string">"text-red-600 font-bold"</span>&gt;{t(<span class="hljs-string">"welcome"</span>)}&lt;/h1&gt;
      &lt;/div&gt;
        }
      &lt;/Translation&gt;
    )
  }
</code></pre>
</li>
<li><p><code>&lt;Trans&gt;</code>: A component that handles complex translations with React elements and interpolation (<a target="_blank" href="https://react.i18next.com/latest/trans-component">more details</a>)</p>
</li>
</ul>
<p><strong>📒 Note:</strong> In most cases, you'll only need <code>useTranslation</code> hook, which provides a simpler and more straightforward way to handle translations.</p>
<p>💡 <strong>Quick Link:</strong> For explore more language tags locale codes check out <a target="_blank" href="https://en.wikipedia.org/wiki/IETF_language_tag">Wikipedia</a></p>
<p>💡 <strong>Quick Link:</strong> For this section's code, check out the <a target="_blank" href="https://github.com/othmanekahtal/react-i18n-practical-guide/tree/basic-configuration"><code>basic-configuration</code></a> branch</p>
<h2 id="heading-translation-file-organization">Translation File Organization</h2>
<p>As discussed in <a target="_blank" href="https://blog.othmane.me/why-internationalization-and-localization-are-no-longer-optional-for-modern-apps">our previous article</a>, localization involves collaboration with translators and legal teams. Therefore, we need a well-organized and structured approach to manage translations, In this section, we'll dive into that</p>
<h3 id="heading-json-structure">JSON structure</h3>
<p>For better translation management, we can separate our translations into JSON files and spread them into the i18next configuration like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> i18n <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next"</span>;
<span class="hljs-keyword">import</span> { initReactI18next } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;
<span class="hljs-keyword">import</span> LanguageDetector <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next-browser-languagedetector"</span>;
<span class="hljs-keyword">import</span> enTranslation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/english/translation.json"</span>;
<span class="hljs-keyword">import</span> deTranslation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/deutsch/translation.json"</span>;
<span class="hljs-keyword">import</span> arTranslation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/arabic/translation.json"</span>;
i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    debug: <span class="hljs-literal">true</span>,
    lng: <span class="hljs-string">"en"</span>,
    fallbackLng: <span class="hljs-string">"ar"</span>,
    interpolation: {
      escapeValue: <span class="hljs-literal">false</span>, <span class="hljs-comment">// not needed for react as it escapes by default</span>
    },
    resources: {
      en: {
        translation: {
          ...enTranslation,
        },
      },
      de: {
        translation: {
          ...deTranslation,
        },
      },
      ar: {
        translation: {
          ...arTranslation,
        },
      },
    },
  });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> i18n;
</code></pre>
<p><strong>📒 Note:</strong> We'll improve this later by using the <code>i18next-http-backend</code> plugin to enhance performance when loading translations.</p>
<p>💡 <strong>Quick Link:</strong> For this section's code, check out the <a target="_blank" href="https://github.com/othmanekahtal/react-i18n-practical-guide/tree/json-structure"><code>json-structure</code></a> branch</p>
<h3 id="heading-namespaces">Namespaces</h3>
<p>When considering performance, we need to optimize how translations are loaded. i18next provides a powerful feature called <code>namespaces</code> that helps us:</p>
<ul>
<li><p>Split translations into multiple files</p>
</li>
<li><p>Load translations on demand, not on initial page load</p>
</li>
<li><p>Organize translations into logical categories</p>
</li>
</ul>
<p>Common namespace examples:</p>
<ul>
<li><p><code>common</code>: Shared translations (buttons, labels)</p>
</li>
<li><p><code>validation</code>: Form validation messages</p>
</li>
<li><p><code>auth</code>: Authentication-related content</p>
</li>
</ul>
<p>This approach improves both performance and maintainability by loading only the translations needed for each part of your application, let’s dive into it</p>
<p>I've organized the translations into these namespaces:</p>
<ul>
<li><p><code>auth</code>: Authentication-related content</p>
</li>
<li><p><code>common</code>: Shared actions and frequently used text</p>
</li>
<li><p><code>validation</code> : Form validation messages</p>
</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-comment">// File structure:</span>
<span class="hljs-comment">// src/</span>
<span class="hljs-comment">//  locales/</span>
<span class="hljs-comment">//    english/</span>
<span class="hljs-comment">//      common.json</span>
<span class="hljs-comment">//      validation.json</span>
<span class="hljs-comment">//      auth.json</span>


<span class="hljs-comment">// English (english/common.json)</span>
{
 <span class="hljs-string">"actions"</span>: {
   <span class="hljs-string">"create"</span>: <span class="hljs-string">"Create"</span>,
   <span class="hljs-string">"delete"</span>: <span class="hljs-string">"Delete"</span>,
   <span class="hljs-string">"cancel"</span>: <span class="hljs-string">"Cancel"</span>,
   <span class="hljs-string">"save"</span>: <span class="hljs-string">"Save"</span>
 },
 <span class="hljs-string">"messages"</span>: {
   <span class="hljs-string">"loading"</span>: <span class="hljs-string">"Please wait..."</span>,
   <span class="hljs-string">"success"</span>: <span class="hljs-string">"Operation completed successfully"</span>,
   <span class="hljs-string">"error"</span>: <span class="hljs-string">"Something went wrong"</span>
 }
}

<span class="hljs-comment">// English (english/validation.json)</span>
{
  <span class="hljs-string">"required"</span>: <span class="hljs-string">"This field is required"</span>,
  <span class="hljs-string">"email"</span>: <span class="hljs-string">"Please enter a valid email"</span>,
  <span class="hljs-string">"password"</span>: {
    <span class="hljs-string">"min"</span>: <span class="hljs-string">"Password must be at least {{min}} characters"</span>,
    <span class="hljs-string">"match"</span>: <span class="hljs-string">"Passwords do not match"</span>
  }
}

<span class="hljs-comment">// English (english/auth.json)</span>
{
  <span class="hljs-string">"login"</span>: {
    <span class="hljs-string">"title"</span>: <span class="hljs-string">"Welcome Back"</span>,
    <span class="hljs-string">"submit"</span>: <span class="hljs-string">"Sign In"</span>,
    <span class="hljs-string">"forgotPassword"</span>: <span class="hljs-string">"Forgot Password?"</span>
  },
  <span class="hljs-string">"register"</span>: {
    <span class="hljs-string">"title"</span>: <span class="hljs-string">"Create Account"</span>,
    <span class="hljs-string">"submit"</span>: <span class="hljs-string">"Sign Up"</span>
  }
}
</code></pre>
<p>back to configuration to fit current change:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> i18n <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next"</span>;
<span class="hljs-keyword">import</span> { initReactI18next } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;
<span class="hljs-keyword">import</span> LanguageDetector <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next-browser-languagedetector"</span>;

<span class="hljs-keyword">import</span> enCommon <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/english/common.json"</span>;
<span class="hljs-keyword">import</span> enValidation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/english/validation.json"</span>;
<span class="hljs-keyword">import</span> enAuth <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/english/auth.json"</span>;

<span class="hljs-keyword">import</span> deCommon <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/deutsch/common.json"</span>;
<span class="hljs-keyword">import</span> deValidation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/deutsch/validation.json"</span>;
<span class="hljs-keyword">import</span> deAuth <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/deutsch/auth.json"</span>;

<span class="hljs-keyword">import</span> arCommon <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/arabic/common.json"</span>;
<span class="hljs-keyword">import</span> arValidation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/arabic/validation.json"</span>;
<span class="hljs-keyword">import</span> arAuth <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/arabic/auth.json"</span>;

i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    debug: <span class="hljs-literal">true</span>,
    lng: <span class="hljs-string">"en"</span>,
    fallbackLng: <span class="hljs-string">"en"</span>,

    <span class="hljs-comment">// Define default namespace</span>
    defaultNS: <span class="hljs-string">"common"</span>,

    interpolation: {
      escapeValue: <span class="hljs-literal">false</span>, <span class="hljs-comment">// not needed for react as it escapes by default</span>
    },

    resources: {
      en: {
        common: enCommon,
        validation: enValidation,
        auth: enAuth,
      },
      de: {
        common: deCommon,
        validation: deValidation,
        auth: deAuth,
      },
      ar: {
        common: arCommon,
        validation: arValidation,
        auth: arAuth,
      },
    },
  });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> i18n;
</code></pre>
<p>Now let's test our implementation. I've created a login form component that includes validation. First, install the required packages:</p>
<pre><code class="lang-bash">pnpm install zod react-hook-form @hookform/resolvers
</code></pre>
<p>Let's create our form component that leverages namespaces with form validation:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useTranslation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;
<span class="hljs-keyword">import</span> { z } <span class="hljs-keyword">from</span> <span class="hljs-string">"zod"</span>;
<span class="hljs-keyword">import</span> { useForm } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-hook-form"</span>;
<span class="hljs-keyword">import</span> { zodResolver } <span class="hljs-keyword">from</span> <span class="hljs-string">"@hookform/resolvers/zod"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">LoginForm</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { t } = useTranslation([<span class="hljs-string">"auth"</span>, <span class="hljs-string">"validation"</span>, <span class="hljs-string">"common"</span>]);

  <span class="hljs-keyword">const</span> loginSchema = z.object({
    email: z
      .string()
      .min(<span class="hljs-number">1</span>, t(<span class="hljs-string">"validation:required"</span>))
      .email(t(<span class="hljs-string">"validation:email"</span>)),
    password: z.string().min(<span class="hljs-number">8</span>, t(<span class="hljs-string">"validation:password.min"</span>, { min: <span class="hljs-number">8</span> })),
  });

  <span class="hljs-keyword">type</span> LoginFormData = z.infer&lt;<span class="hljs-keyword">typeof</span> loginSchema&gt;;

  <span class="hljs-keyword">const</span> {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm&lt;LoginFormData&gt;({
    resolver: zodResolver(loginSchema),
  });

  <span class="hljs-keyword">const</span> onSubmit = <span class="hljs-function">(<span class="hljs-params">data: LoginFormData</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(data);
  };

  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8"</span>&gt;
      &lt;div className=<span class="hljs-string">"max-w-md w-full space-y-8"</span>&gt;
        &lt;div&gt;
          &lt;h1 className=<span class="hljs-string">"mt-6 text-center text-3xl font-extrabold text-gray-900"</span>&gt;
            {t(<span class="hljs-string">"auth:login.title"</span>)}
          &lt;/h1&gt;
        &lt;/div&gt;

        &lt;form className=<span class="hljs-string">"mt-8 space-y-6"</span> onSubmit={handleSubmit(onSubmit)}&gt;
          &lt;div className=<span class="hljs-string">"rounded-md shadow-sm -space-y-px"</span>&gt;
            &lt;div&gt;
              &lt;input
                {...register(<span class="hljs-string">"email"</span>)}
                <span class="hljs-keyword">type</span>=<span class="hljs-string">"email"</span>
                className=<span class="hljs-string">"appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"</span>
              /&gt;
              {errors.email &amp;&amp; (
                &lt;span className=<span class="hljs-string">"text-red-500 text-xs mt-1"</span>&gt;
                  {errors.email.message}
                &lt;/span&gt;
              )}
            &lt;/div&gt;

            &lt;div&gt;
              &lt;input
                {...register(<span class="hljs-string">"password"</span>)}
                <span class="hljs-keyword">type</span>=<span class="hljs-string">"password"</span>
                className=<span class="hljs-string">"appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"</span>
              /&gt;
              {errors.password &amp;&amp; (
                &lt;span className=<span class="hljs-string">"text-red-500 text-xs mt-1"</span>&gt;
                  {errors.password.message}
                &lt;/span&gt;
              )}
            &lt;/div&gt;
          &lt;/div&gt;

          &lt;div className=<span class="hljs-string">"flex items-center justify-between"</span>&gt;
            &lt;a
              href=<span class="hljs-string">"#"</span>
              className=<span class="hljs-string">"text-sm text-indigo-600 hover:text-indigo-500"</span>
            &gt;
              {t(<span class="hljs-string">"auth:login.forgotPassword"</span>)}
            &lt;/a&gt;
          &lt;/div&gt;

          &lt;div className=<span class="hljs-string">"flex gap-4"</span>&gt;
            &lt;button
              <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span>
              className=<span class="hljs-string">"group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"</span>
            &gt;
              {t(<span class="hljs-string">"auth:login.submit"</span>)}
            &lt;/button&gt;
            &lt;button
              <span class="hljs-keyword">type</span>=<span class="hljs-string">"button"</span>
              className=<span class="hljs-string">"group relative w-full flex justify-center py-2 px-4 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"</span>
            &gt;
              {t(<span class="hljs-string">"common:actions.cancel"</span>)}
            &lt;/button&gt;
          &lt;/div&gt;
        &lt;/form&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> LoginForm;
</code></pre>
<p>Import the form component into your main interface/App file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> LoginForm <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/auth/login/form"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> &lt;LoginForm /&gt;;
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>Expected result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731675509719/386b3a47-9c2c-4668-8861-9e05caeea805.png" alt class="image--center mx-auto" /></p>
<p>now let’s break out the details:</p>
<ul>
<li><p>In our configuration, we:</p>
<ul>
<li><p>Changed the default namespace from <code>translation</code> to multiple namespaces: <code>auth</code>, <code>common</code>, and <code>validation</code></p>
</li>
<li><p>Set <code>common</code> as the default namespace using <code>defaultNS: 'common'</code></p>
</li>
</ul>
</li>
<li><p>To use these namespaces in components:</p>
<ul>
<li><p>Load required namespaces:</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">const</span> { t } = useTranslation([<span class="hljs-string">"auth"</span>, <span class="hljs-string">"validation"</span>, <span class="hljs-string">"common"</span>]);
</code></pre>
</li>
<li><p>Access translations using namespace prefix:</p>
<pre><code class="lang-typescript">  &lt;h1 className=<span class="hljs-string">"mt-6 text-center text-3xl font-extrabold text-gray-900"</span>&gt;
    {t(<span class="hljs-string">"auth:login.title"</span>)}
  &lt;/h1&gt;
</code></pre>
</li>
</ul>
</li>
</ul>
<p>💡 <strong>Quick Link:</strong> For this section's code, check out the <a target="_blank" href="https://github.com/othmanekahtal/react-i18n-practical-guide/tree/namespaces">namespaces</a> branch</p>
<h2 id="heading-core-translation-features">Core Translation Features</h2>
<p>What makes <code>i18next</code> a mature, production-ready framework is its ability to handle complex scenarios such as:</p>
<ul>
<li><p>Plural forms</p>
</li>
<li><p>Number and date formatting</p>
</li>
<li><p>Translation interpolation</p>
</li>
<li><p>Dynamic values and variables</p>
</li>
</ul>
<h3 id="heading-basic-text-translation">Basic text translation</h3>
<p>Let's look at a real example. Imagine we have a dashboard where we want to display a personalized greeting. For instance, if the user's name is Othmane, we want to show '<strong>Hello Othmane!'</strong>. Here's how to implement this:</p>
<ul>
<li><p>In your localization folder (<code>/locales/[language]/common.json</code>), add these translations:</p>
<pre><code class="lang-typescript">  <span class="hljs-string">"greeting"</span>: {
          <span class="hljs-string">"title"</span>: <span class="hljs-string">"Hello, {{name}}"</span>,
          <span class="hljs-string">"subtitle"</span>: <span class="hljs-string">"Welcome to your dashboard"</span>
        },
</code></pre>
</li>
<li><p>Add new component <code>dashboard</code> and import it to page:</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> { useTranslation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Dashboard</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> { t } = useTranslation();
    <span class="hljs-comment">// This can be dynamic based on your needs</span>
    <span class="hljs-keyword">const</span> userName = <span class="hljs-string">"Othmane"</span>;

    <span class="hljs-keyword">return</span> (
      &lt;div className=<span class="hljs-string">"min-h-screen flex items-center justify-center"</span>&gt;
        &lt;div className=<span class="hljs-string">"max-w-2xl w-full mx-auto p-8 bg-white rounded-lg shadow-lg"</span>&gt;
          &lt;h1 className=<span class="hljs-string">"text-3xl font-bold text-center bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent"</span>&gt;
            {t(<span class="hljs-string">"greeting.title"</span>, { name: userName })}
          &lt;/h1&gt;
          &lt;p className=<span class="hljs-string">"mt-4 text-lg text-gray-600 text-center leading-relaxed"</span>&gt;
            {t(<span class="hljs-string">"greeting.subtitle"</span>)}
          &lt;/p&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    );
  }
  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Dashboard;
</code></pre>
<p>  There's no need to explicitly specify 'common' as the namespace in <code>useTranslation()</code>, since we've already configured it as the default namespace in our i18n configuration. This simplifies our code and reduces redundancy while maintaining the same functionality.</p>
</li>
<li><p>Expected result:</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731760960825/2698e381-6bc3-451d-a299-8d1602c42a39.png" alt /></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731760790889/63e8a7c6-1dc0-4ddb-84a5-ff69fe7c9795.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<p>📝 <strong>Note:</strong> Arabic is a Right-to-Left (RTL) language. We will cover later how to handle RTL text direction using Tailwind CSS's built-in RTL support through the <code>rtl:</code> directive. This includes:</p>
<ul>
<li><p>Text alignment</p>
</li>
<li><p>Margin and padding</p>
</li>
<li><p>Borders and shadows</p>
</li>
<li><p>Layout direction</p>
</li>
</ul>
<h3 id="heading-handling-plurals">Handling plurals</h3>
<p>Handling plurals is a crucial and challenging aspect of internationalization, as it extends beyond the simple singular/plural format found in English. Many languages, such as Arabic and Russian, have multiple plural forms. The i18n framework efficiently manages these complexities by providing built-in support for the plural rules of all your supported languages.</p>
<p>starting by real example and break out the details box, we have a listItem we need to render the total of those items based on counter that dynamically passed to <code>t</code> function:</p>
<ul>
<li><p>In your localization folder (<code>/locales/en/common.json</code>), add these translations:</p>
<pre><code class="lang-typescript">   <span class="hljs-string">"item_zero"</span>: <span class="hljs-string">"No items"</span>,
    <span class="hljs-string">"item_one"</span>: <span class="hljs-string">"{{count}} item"</span>,
    <span class="hljs-string">"item_other"</span>: <span class="hljs-string">"{{count}} items"</span>,
    <span class="hljs-string">"pluralExample"</span>: {
      <span class="hljs-string">"title"</span>: <span class="hljs-string">"Plural example"</span>,
      <span class="hljs-string">"currentLang"</span>: <span class="hljs-string">"current Language"</span>
    },
</code></pre>
</li>
<li><p>In your localization folder (<code>/locales/ar/common.json</code>), add these translations:</p>
<pre><code class="lang-typescript">    <span class="hljs-string">"item_zero"</span>: <span class="hljs-string">"لا توجد عناصر"</span>,
    <span class="hljs-string">"item_one"</span>: <span class="hljs-string">"عنصر واحد"</span>,
    <span class="hljs-string">"item_two"</span>: <span class="hljs-string">"عنصران"</span>,
    <span class="hljs-string">"item_few"</span>: <span class="hljs-string">"{{count}} عناصر"</span>,
    <span class="hljs-string">"item_many"</span>: <span class="hljs-string">"{{count}} عنصراً"</span>,
    <span class="hljs-string">"item_other"</span>: <span class="hljs-string">"{{count}} عنصر"</span>,
    <span class="hljs-string">"pluralExample"</span>: {
      <span class="hljs-string">"title"</span>: <span class="hljs-string">"مثال الجمع"</span>,
      <span class="hljs-string">"currentLang"</span>: <span class="hljs-string">"اللغة الحالية"</span>
    },
</code></pre>
</li>
<li><p>Add new component <code>dashboard</code> and import it to page:</p>
<pre><code class="lang-typescript">  <span class="hljs-keyword">import</span> { useTranslation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;

  <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ItemsList</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> { t, i18n } = useTranslation();

    <span class="hljs-keyword">const</span> quantities = [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">11</span>, <span class="hljs-number">100</span>];

    <span class="hljs-keyword">return</span> (
      &lt;div className=<span class="hljs-string">"min-h-screen flex items-center justify-center bg-gray-50"</span>&gt;
        &lt;div className=<span class="hljs-string">"max-w-2xl w-full mx-auto p-8 bg-white rounded-xl shadow-lg"</span>&gt;
          {<span class="hljs-comment">/* Header Section */</span>}
          &lt;div className=<span class="hljs-string">"mb-8 text-center"</span>&gt;
            &lt;h2 className=<span class="hljs-string">"text-2xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent"</span>&gt;
              {t(<span class="hljs-string">"pluralExample.title"</span>)}
            &lt;/h2&gt;
            &lt;p className=<span class="hljs-string">"mt-2 text-gray-600"</span>&gt;
              {t(<span class="hljs-string">"pluralExample.currentLang"</span>)}:{<span class="hljs-string">" "</span>}
              {i18n.language === <span class="hljs-string">"en"</span> ? <span class="hljs-string">"English"</span> : <span class="hljs-string">"العربية"</span>}
            &lt;/p&gt;
          &lt;/div&gt;

          {<span class="hljs-comment">/* Examples Grid */</span>}
          &lt;div className=<span class="hljs-string">"grid grid-cols-1 md:grid-cols-2 gap-4"</span>&gt;
            {quantities.map(<span class="hljs-function">(<span class="hljs-params">count</span>) =&gt;</span> (
              &lt;div
                key={count}
                className=<span class="hljs-string">"p-4 border border-gray-200 rounded-lg hover:border-blue-500 transition-colors duration-300"</span>
              &gt;
                &lt;div className=<span class="hljs-string">"flex items-center justify-between"</span>&gt;
                  &lt;span className=<span class="hljs-string">"text-lg font-semibold text-blue-600"</span>&gt;
                    {count}
                  &lt;/span&gt;
                  &lt;span className=<span class="hljs-string">"text-gray-700"</span>&gt;{t(<span class="hljs-string">"item"</span>, { count })}&lt;/span&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            ))}
          &lt;/div&gt;

          {<span class="hljs-comment">/* Language Switch Button */</span>}
          &lt;div className=<span class="hljs-string">"mt-8 text-center"</span>&gt;
            &lt;button
              onClick={<span class="hljs-function">() =&gt;</span>
                i18n.changeLanguage(i18n.language === <span class="hljs-string">"en"</span> ? <span class="hljs-string">"ar"</span> : <span class="hljs-string">"en"</span>)
              }
              className=<span class="hljs-string">"px-6 py-2 bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-lg
                       hover:from-blue-700 hover:to-purple-700 transition-all duration-300
                       focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"</span>
            &gt;
              Switch to {i18n.language === <span class="hljs-string">"en"</span> ? <span class="hljs-string">"Arabic"</span> : <span class="hljs-string">"English"</span>}
            &lt;/button&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    );
  }
</code></pre>
</li>
<li><p>Expected result:</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731934394994/bab752a7-95ae-468d-b8ab-6f43db58ebb3.png" alt class="image--center mx-auto" /></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731934407122/99b1d592-67ff-45c0-817a-8b4a8d1cb89d.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<p>Different languages have varying rules for handling plural forms:</p>
<p><strong>English</strong> (2 forms):</p>
<ul>
<li><p><code>one</code>: for exactly 1 (1 item)</p>
</li>
<li><p><code>other</code>: for 0 and numbers greater than 1 (0 items, 2 items, etc.)</p>
</li>
</ul>
<p><strong>Arabic</strong> (6 forms):</p>
<ul>
<li><p><code>zero</code>: for 0</p>
</li>
<li><p><code>one</code>: for exactly 1</p>
</li>
<li><p><code>two</code>: for exactly 2</p>
</li>
<li><p><code>few</code>: for 3-10</p>
</li>
<li><p><code>many</code>: for 11-99</p>
</li>
<li><p><code>other</code>: for 100+</p>
</li>
</ul>
<p>Example quantities:</p>
<ul>
<li><p>0 → No items | لا توجد عناصر</p>
</li>
<li><p>1 → 1 item | عنصر واحد</p>
</li>
<li><p>2 → 2 items | عنصران</p>
</li>
<li><p>5 → 5 items | 5 عناصر</p>
</li>
<li><p>15 → 15 items | 15 عنصراً</p>
</li>
<li><p>100 → 100 items | 100 عنصر</p>
</li>
</ul>
<p>i18n automatically handles these rules based on the selected language and the provided count value.</p>
<p>📝 <strong>Note:</strong> To enhance your multilingual development workflow in VSCode, you can use the i18n-ally extension. It provides:</p>
<ul>
<li><p>Real-time translation previews in your code</p>
</li>
<li><p>Locale file management</p>
</li>
<li><p>Auto-completion for translation keys</p>
</li>
<li><p>Inline translation annotations</p>
</li>
</ul>
<p>In this project, I've configured i18n-ally with English as the source language. This extension works with any framework and helps streamline translation management.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731935310293/3bd91f25-34ca-4d9f-b3ee-be5baffcd443.png" alt class="image--center mx-auto" /></p>
<p>💡 <strong>Quick Link:</strong> For this section's code, check out the <a target="_blank" href="https://github.com/othmanekahtal/react-i18n-practical-guide/tree/plural">Plural</a> branch</p>
<h3 id="heading-number-amp-date-formatting">Number &amp; Date formatting</h3>
<p>i18next's flexible interpolation system allows you to create custom format functions by leveraging the <code>Intl</code> API, which is widely supported in modern browsers. This enables advanced number formatting through custom interpolation functions. Let's examine a comprehensive implementation:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// i18n configuration:</span>
<span class="hljs-keyword">import</span> i18n, { FormatFunction } <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next"</span>;
<span class="hljs-keyword">import</span> { initReactI18next } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;
<span class="hljs-keyword">import</span> LanguageDetector <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next-browser-languagedetector"</span>;

<span class="hljs-comment">// Import namespaces for each language</span>
<span class="hljs-keyword">import</span> enCommon <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/en/common.json"</span>;
<span class="hljs-keyword">import</span> enValidation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/en/validation.json"</span>;
<span class="hljs-keyword">import</span> enAuth <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/en/auth.json"</span>;

<span class="hljs-keyword">import</span> deCommon <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/de/common.json"</span>;
<span class="hljs-keyword">import</span> deValidation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/de/validation.json"</span>;
<span class="hljs-keyword">import</span> deAuth <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/de/auth.json"</span>;

<span class="hljs-keyword">import</span> arCommon <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/ar/common.json"</span>;
<span class="hljs-keyword">import</span> arValidation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/ar/validation.json"</span>;
<span class="hljs-keyword">import</span> arAuth <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/ar/auth.json"</span>;

i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    debug: <span class="hljs-literal">true</span>,
    lng: <span class="hljs-string">"ar"</span>,
    fallbackLng: <span class="hljs-string">"en"</span>,

    <span class="hljs-comment">// Define default namespace</span>
    defaultNS: <span class="hljs-string">"common"</span>,

    interpolation: {
      format: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">value, format, lng</span>) </span>{
        <span class="hljs-built_in">console</span>.log({ value, format, lng });
        <span class="hljs-keyword">const</span> numValue = <span class="hljs-built_in">Number</span>(value);
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">isNaN</span>(numValue)) {
          <span class="hljs-keyword">switch</span> (format) {
            <span class="hljs-comment">// Currency, Decimal, Percent, Compact</span>
            <span class="hljs-keyword">case</span> <span class="hljs-string">"currency"</span>:
              <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.NumberFormat(lng, {
                style: <span class="hljs-string">"currency"</span>,
                currency: lng === <span class="hljs-string">"ar"</span> ? <span class="hljs-string">"SAR"</span> : <span class="hljs-string">"USD"</span>,
              }).format(numValue);
            <span class="hljs-keyword">case</span> <span class="hljs-string">"decimal"</span>:
              <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.NumberFormat(lng, {
                minimumFractionDigits: <span class="hljs-number">2</span>,
                maximumFractionDigits: <span class="hljs-number">2</span>,
              }).format(numValue);
            <span class="hljs-keyword">case</span> <span class="hljs-string">"percent"</span>:
              <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.NumberFormat(lng, {
                style: <span class="hljs-string">"percent"</span>,
                minimumFractionDigits: <span class="hljs-number">2</span>,
                maximumFractionDigits: <span class="hljs-number">2</span>,
              }).format(numValue);
            <span class="hljs-keyword">case</span> <span class="hljs-string">"compact"</span>:
              <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.NumberFormat(lng, {
                notation: <span class="hljs-string">"compact"</span>,
                compactDisplay: <span class="hljs-string">"short"</span>,
              }).format(numValue);

            <span class="hljs-comment">// Units</span>
            <span class="hljs-keyword">case</span> <span class="hljs-string">"bytes"</span>:
              <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.NumberFormat(lng, {
                style: <span class="hljs-string">"unit"</span>,
                unit: <span class="hljs-string">"byte"</span>,
                unitDisplay: <span class="hljs-string">"long"</span>,
              }).format(numValue);
            <span class="hljs-keyword">case</span> <span class="hljs-string">"kg"</span>:
              <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.NumberFormat(lng, {
                style: <span class="hljs-string">"unit"</span>,
                unit: <span class="hljs-string">"kilogram"</span>,
                unitDisplay: <span class="hljs-string">"long"</span>,
              }).format(numValue);
            <span class="hljs-keyword">case</span> <span class="hljs-string">"meter"</span>:
              <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.NumberFormat(lng, {
                style: <span class="hljs-string">"unit"</span>,
                unit: <span class="hljs-string">"meter"</span>,
                unitDisplay: <span class="hljs-string">"long"</span>,
              }).format(numValue);
            <span class="hljs-keyword">case</span> <span class="hljs-string">"temperature"</span>:
              <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Intl</span>.NumberFormat(lng, {
                style: <span class="hljs-string">"unit"</span>,
                unit: <span class="hljs-string">"celsius"</span>,
                unitDisplay: <span class="hljs-string">"long"</span>,
              }).format(numValue);
            <span class="hljs-keyword">default</span>:
              <span class="hljs-keyword">return</span> numValue.toString();
          }
        }
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">String</span>(value);
      } <span class="hljs-keyword">as</span> FormatFunction,
      escapeValue: <span class="hljs-literal">false</span>, <span class="hljs-comment">// not needed for react as it escapes by default</span>
    },

    resources: {
      en: {
        common: enCommon,
        validation: enValidation,
        auth: enAuth,
      },
      de: {
        common: deCommon,
        validation: deValidation,
        auth: deAuth,
      },
      ar: {
        common: arCommon,
        validation: arValidation,
        auth: arAuth,
      },
    },
  });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> i18n;
</code></pre>
<p>for translations :</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// en:</span>
  <span class="hljs-string">"numberExample"</span>: {
    <span class="hljs-string">"title"</span>: <span class="hljs-string">"Number Formatting Examples"</span>,
    <span class="hljs-string">"currency"</span>: <span class="hljs-string">"Currency: {{value, currency}}"</span>,
    <span class="hljs-string">"decimal"</span>: <span class="hljs-string">"Decimal: {{value, decimal}}"</span>,
    <span class="hljs-string">"percent"</span>: <span class="hljs-string">"Percentage: {{value, percent}}"</span>,
    <span class="hljs-string">"compact"</span>: <span class="hljs-string">"Compact: {{value, compact}}"</span>,
    <span class="hljs-string">"bytes"</span>: <span class="hljs-string">"Bytes: {{value, bytes}}"</span>,
    <span class="hljs-string">"kg"</span>: <span class="hljs-string">"Weight: {{value, kg}}"</span>,
    <span class="hljs-string">"meter"</span>: <span class="hljs-string">"Length: {{value, meter}}"</span>,
    <span class="hljs-string">"temperature"</span>: <span class="hljs-string">"Temperature: {{value, temperature}}"</span>
  },
<span class="hljs-comment">// ar:</span>
<span class="hljs-string">"numberExample"</span>: {
    <span class="hljs-string">"title"</span>: <span class="hljs-string">"أمثلة تنسيق الأرقام"</span>,
    <span class="hljs-string">"currency"</span>: <span class="hljs-string">"العملة: {{value, currency}}"</span>,
    <span class="hljs-string">"decimal"</span>: <span class="hljs-string">"العدد العشري: {{value, decimal}}"</span>,
    <span class="hljs-string">"percent"</span>: <span class="hljs-string">"النسبة المئوية: {{value, percent}}"</span>,
    <span class="hljs-string">"compact"</span>: <span class="hljs-string">"مختصر: {{value, compact}}"</span>,
    <span class="hljs-string">"bytes"</span>: <span class="hljs-string">"الحجم: {{value, bytes}}"</span>,
    <span class="hljs-string">"kg"</span>: <span class="hljs-string">"الوزن: {{value, kg}}"</span>,
    <span class="hljs-string">"meter"</span>: <span class="hljs-string">"الطول: {{value, meter}}"</span>,
    <span class="hljs-string">"temperature"</span>: <span class="hljs-string">"درجة الحرارة: {{value, temperature}}"</span>
  },
</code></pre>
<p>Component implementation:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useTranslation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">NumberFormats</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { t, i18n } = useTranslation();
  <span class="hljs-keyword">const</span> isRTL = i18n.language === <span class="hljs-string">"ar"</span>;

  <span class="hljs-keyword">const</span> examples = [
    { <span class="hljs-keyword">type</span>: <span class="hljs-string">"currency"</span>, value: <span class="hljs-number">1234567.89</span> },
    { <span class="hljs-keyword">type</span>: <span class="hljs-string">"decimal"</span>, value: <span class="hljs-number">1234567.89</span> },
    { <span class="hljs-keyword">type</span>: <span class="hljs-string">"percent"</span>, value: <span class="hljs-number">0.8567</span> },
    { <span class="hljs-keyword">type</span>: <span class="hljs-string">"compact"</span>, value: <span class="hljs-number">1234567</span> },
    { <span class="hljs-keyword">type</span>: <span class="hljs-string">"bytes"</span>, value: <span class="hljs-number">1024</span> },
    { <span class="hljs-keyword">type</span>: <span class="hljs-string">"kg"</span>, value: <span class="hljs-number">75.5</span> },
    { <span class="hljs-keyword">type</span>: <span class="hljs-string">"meter"</span>, value: <span class="hljs-number">150</span> },
    { <span class="hljs-keyword">type</span>: <span class="hljs-string">"temperature"</span>, value: <span class="hljs-number">25</span> },
  ];

  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"min-h-screen flex items-center justify-center bg-gray-50"</span>&gt;
      &lt;div
        dir={isRTL ? <span class="hljs-string">"rtl"</span> : <span class="hljs-string">"ltr"</span>}
        className=<span class="hljs-string">"max-w-2xl w-full mx-auto p-8 bg-white rounded-xl shadow-lg"</span>
      &gt;
        &lt;div className=<span class="hljs-string">"mb-8 text-center"</span>&gt;
          &lt;h2 className=<span class="hljs-string">"text-2xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent"</span>&gt;
            {t(<span class="hljs-string">"numberExample.title"</span>)}
          &lt;/h2&gt;
        &lt;/div&gt;

        &lt;div className=<span class="hljs-string">"space-y-4"</span>&gt;
          {examples.map(<span class="hljs-function">(<span class="hljs-params">{ <span class="hljs-keyword">type</span>, value }</span>) =&gt;</span> (
            &lt;div
              key={<span class="hljs-keyword">type</span>}
              className=<span class="hljs-string">"p-4 border border-gray-200 rounded-lg hover:border-blue-500 transition-colors duration-300"</span>
            &gt;
              &lt;p className=<span class="hljs-string">"text-gray-700"</span>&gt;
                {t(<span class="hljs-string">`numberExample.<span class="hljs-subst">${<span class="hljs-keyword">type</span>}</span>`</span>, {
                  value,
                })}
              &lt;/p&gt;
            &lt;/div&gt;
          ))}
        &lt;/div&gt;

        &lt;div className=<span class="hljs-string">"mt-8 text-center"</span>&gt;
          &lt;button
            onClick={<span class="hljs-function">() =&gt;</span> i18n.changeLanguage(isRTL ? <span class="hljs-string">"en"</span> : <span class="hljs-string">"ar"</span>)}
            className=<span class="hljs-string">"px-6 py-2 bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-lg
                     hover:from-blue-700 hover:to-purple-700 transition-all duration-300
                     focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"</span>
          &gt;
            Switch to {isRTL ? <span class="hljs-string">"English"</span> : <span class="hljs-string">"Arabic"</span>}
          &lt;/button&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>Expected result:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731938535814/470d279e-d401-465e-ab78-4ba02871cbe3.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731939306625/bfa8aa8e-1cc0-42c1-878a-9ec4ef329345.png" alt class="image--center mx-auto" /></p>
<p>The custom format function provides flexibility beyond just <code>Intl</code> API usage. You can integrate other formatting libraries as fallbacks for browsers with limited <code>Intl</code> API support. Similarly, this approach can be applied to date formatting.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731939961707/aa90503d-a8e4-4ed2-ac8c-822e36572d84.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731939970122/b023788d-c0b0-406e-9797-531a59640623.png" alt class="image--center mx-auto" /></p>
<p>💡 <strong>Quick Link:</strong> For this section's code, check out the <a target="_blank" href="https://github.com/othmanekahtal/react-i18n-practical-guide/tree/Formatting">Formatting</a> branch</p>
<h2 id="heading-language-switching">Language Switching</h2>
<p>A crucial aspect of supporting multiple languages is allowing users to choose their preferred language rather than relying solely on browser-detected settings. i18next provides comprehensive tools to handle language switching, loading, and configuration. It also manages language detection, changing, and initialization. Let's explore how to implement these features:</p>
<h3 id="heading-language-selector-component">Language selector component</h3>
<p>We've already implemented some of these features in previous sections. The <code>useTranslation</code> hook returns an <code>i18n</code> instance that we can use to change and retrieve the current language, as shown in the example below:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useTranslation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">NumberFormats</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { t, i18n } = useTranslation();
  <span class="hljs-keyword">const</span> isRTL = i18n.language === <span class="hljs-string">"ar"</span>;
  <span class="hljs-keyword">return</span> (
        &lt;div className=<span class="hljs-string">"mt-8 text-center"</span>&gt;
          &lt;button
            onClick={<span class="hljs-function">() =&gt;</span> i18n.changeLanguage(isRTL ? <span class="hljs-string">"en"</span> : <span class="hljs-string">"ar"</span>)}
            className=<span class="hljs-string">"px-6 py-2 bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-lg
                     hover:from-blue-700 hover:to-purple-700 transition-all duration-300
                     focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"</span>
          &gt;
            Switch to {isRTL ? <span class="hljs-string">"English"</span> : <span class="hljs-string">"Arabic"</span>}
          &lt;/button&gt;
        &lt;/div&gt;
  );
}
</code></pre>
<p>i18n provides several useful functions and properties for language management:</p>
<ul>
<li><p><code>i18n.language</code>: Gets the currently loaded language</p>
</li>
<li><p><code>i18n.dir()</code>: Returns the text direction (RTL/LTR) based on the current language, or for a specific language when passed as an argument</p>
</li>
<li><p><code>i18n.changeLanguage()</code>: Asynchronous function that changes the active language. Returns a Promise, allowing error handling with try/catch</p>
</li>
</ul>
<p><strong>💡 Quick Link:</strong> For more details about API, check out <a target="_blank" href="https://www.i18next.com/overview/api">this page</a></p>
<h2 id="heading-events-amp-persisting-language-preference">Events &amp; Persisting language preference</h2>
<p>Persisting the preference language of the user is most handled case you should covered in your application, i18n provides powerful events for make it easier for you</p>
<p>One important event for data persistence is <code>languageChanged</code>. This event fires when the language changes, allowing you to execute callback functions. Here's how to implement this functionality to save the preferred language in localStorage:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> i18n, { FormatFunction } <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next"</span>;
<span class="hljs-keyword">import</span> { initReactI18next } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;
<span class="hljs-keyword">import</span> LanguageDetector <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next-browser-languagedetector"</span>;

<span class="hljs-comment">// Import namespaces for each language</span>
<span class="hljs-keyword">import</span> enCommon <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/en/common.json"</span>;
<span class="hljs-keyword">import</span> enValidation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/en/validation.json"</span>;
<span class="hljs-keyword">import</span> enAuth <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/en/auth.json"</span>;

<span class="hljs-keyword">import</span> deCommon <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/de/common.json"</span>;
<span class="hljs-keyword">import</span> deValidation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/de/validation.json"</span>;
<span class="hljs-keyword">import</span> deAuth <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/de/auth.json"</span>;

<span class="hljs-keyword">import</span> arCommon <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/ar/common.json"</span>;
<span class="hljs-keyword">import</span> arValidation <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/ar/validation.json"</span>;
<span class="hljs-keyword">import</span> arAuth <span class="hljs-keyword">from</span> <span class="hljs-string">"../locales/ar/auth.json"</span>;
<span class="hljs-keyword">const</span> getStoredLanguage = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> storedLanguage = <span class="hljs-built_in">localStorage</span>.getItem(<span class="hljs-string">"language"</span>);
  <span class="hljs-keyword">return</span> storedLanguage || navigator.language.split(<span class="hljs-string">"-"</span>)[<span class="hljs-number">0</span>];
};
i18n
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    debug: <span class="hljs-literal">true</span>,
    lng: getStoredLanguage(),
    fallbackLng: <span class="hljs-string">"en"</span>,
    <span class="hljs-comment">// Define default namespace</span>
    defaultNS: <span class="hljs-string">"common"</span>,
    interpolation: {
      escapeValue: <span class="hljs-literal">false</span>, <span class="hljs-comment">// not needed for react as it escapes by default</span>
    },
    resources: {
     <span class="hljs-comment">//</span>
    },
  });

i18n.on(<span class="hljs-string">"languageChanged"</span>, <span class="hljs-function">(<span class="hljs-params">lng</span>) =&gt;</span> {
  <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">"language"</span>, lng);
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> i18n;
</code></pre>
<p><strong>💡 Quick Link:</strong> For this section's code, check out the <a target="_blank" href="https://github.com/othmanekahtal/react-i18n-practical-guide/tree/persistence-events">persistence-events</a> branch</p>
<p><strong>💡 Quick Link:</strong> For more details about Events, check out <a target="_blank" href="https://www.i18next.com/overview/api#events">this page</a></p>
<h2 id="heading-rtl-support">RTL Support</h2>
<p>Several languages are written from right to left (RTL). While these languages are less common in Western web development, they serve millions of users globally. The main RTL languages include Arabic, Hebrew, and Persian.</p>
<p>Supporting RTL languages significantly expands your application's reach. Tailwind CSS v3 makes this easier with its built-in <code>rtl:</code> and <code>ltr:</code> modifiers, allowing you to create multi-directional layouts efficiently. (<a target="_blank" href="https://tailwindcss.com/docs/hover-focus-and-other-states#rtl-support">more details</a>)</p>
<p>To enable Tailwind's RTL/LTR modifiers, we add a language change listener in our i18next configuration that updates the document's direction and language attributes:</p>
<pre><code class="lang-typescript">i18n.on(<span class="hljs-string">"languageChanged"</span>, <span class="hljs-function">(<span class="hljs-params">lng</span>) =&gt;</span> {
  <span class="hljs-built_in">localStorage</span>.setItem(<span class="hljs-string">"language"</span>, lng);
  <span class="hljs-comment">// Set HTML lang attribute</span>
  <span class="hljs-built_in">document</span>.documentElement.lang = lng;
  <span class="hljs-comment">// Set HTML dir attribute based on language</span>
  <span class="hljs-built_in">document</span>.dir = i18n.dir(lng);
});
<span class="hljs-built_in">document</span>.dir = i18n.dir(i18n.language);
<span class="hljs-built_in">document</span>.documentElement.lang = i18n.language;
</code></pre>
<p>Key RTL-aware classes in Tailwind CSS:</p>
<ol>
<li><strong>Spacing Classes:</strong></li>
</ol>
<ul>
<li><p><code>ps-</code> instead of <code>pl-</code> for padding-start</p>
</li>
<li><p><code>pe-</code> instead of <code>pr-</code> for padding-end</p>
</li>
<li><p><code>ms-</code> instead of <code>ml-</code> for margin-start</p>
</li>
<li><p><code>me-</code> instead of <code>mr-</code> for margin-end</p>
</li>
</ul>
<ol start="2">
<li><strong>Positioning Classes:</strong></li>
</ol>
<ul>
<li><p><code>start-</code> instead of <code>left-</code> for positioning</p>
</li>
<li><p><code>end-</code> instead of <code>right-</code> for positioning</p>
</li>
<li><p><code>text-start</code> instead of <code>text-left</code> for text alignment</p>
</li>
</ul>
<ol start="3">
<li><strong>Flex Layout:</strong></li>
</ol>
<ul>
<li><p><code>rtl:flex-row-reverse</code> for reversing flex direction</p>
</li>
<li><p>Flex items automatically adjust in RTL contexts</p>
</li>
</ul>
<p>These logical properties ensure your layout works correctly in both LTR and RTL directions</p>
<p><strong>💡 Quick Link:</strong> For this section's code, check out the <a target="_blank" href="https://github.com/othmanekahtal/react-i18n-practical-guide/tree/rtl-support">rtl-support</a> branch</p>
<h2 id="heading-performance-optimization">Performance Optimization</h2>
<p>Now that we've set up our internationalization system, let's optimize it for better performance. When dealing with multiple languages and translation files, it's crucial to consider bundle size and loading strategies. In this section, we'll explore how to optimize our i18next implementation using dynamic imports and file splitting</p>
<p>Now that we've set up our internationalization system, let's optimize it for better performance. When dealing with multiple languages and translation files, it's crucial to consider bundle size and loading strategies. In this section, we'll explore how to optimize our i18next implementation using dynamic imports and file splitting.</p>
<p>First, install the HTTP backend plugin:</p>
<pre><code class="lang-bash">pnpm add i18next-http-backend
</code></pre>
<ol>
<li>Setup <code>i18next-http-backend</code>:</li>
</ol>
<pre><code class="lang-typescript">typescriptCopy<span class="hljs-comment">// i18n.ts</span>
<span class="hljs-keyword">import</span> i18n <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next"</span>;
<span class="hljs-keyword">import</span> { initReactI18next } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-i18next"</span>;
<span class="hljs-keyword">import</span> HttpBackend <span class="hljs-keyword">from</span> <span class="hljs-string">'i18next-http-backend'</span>;
<span class="hljs-keyword">import</span> LanguageDetector <span class="hljs-keyword">from</span> <span class="hljs-string">"i18next-browser-languagedetector"</span>;

i18n
  .use(HttpBackend)
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    backend: {
      loadPath: <span class="hljs-string">'/locales/{{lng}}/{{ns}}.json'</span>,
    },
    fallbackLng: <span class="hljs-string">"en"</span>,
    defaultNS: <span class="hljs-string">"common"</span>,

    <span class="hljs-comment">// Disable suspense if needed</span>
    react: {
      useSuspense: <span class="hljs-literal">false</span>
    }
  });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> i18n;
</code></pre>
<ol start="2">
<li>Translation File Structure:</li>
</ol>
<pre><code class="lang-typescript"><span class="hljs-keyword">public</span>/
  locales/
    en/
      common.json     # Shared translations
      auth.json       # Authentication translations
      dashboard.json  # Dashboard-specific translations
    ar/
      common.json
      auth.json
      dashboard.json
</code></pre>
<ol start="3">
<li>Lazy Loading Translations:</li>
</ol>
<pre><code class="lang-typescript">
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Dashboard</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Load dashboard translations only when needed</span>
  <span class="hljs-keyword">const</span> { t, i18n } = useTranslation(<span class="hljs-string">'dashboard'</span>, {
    useSuspense: <span class="hljs-literal">false</span>
  });

  <span class="hljs-comment">// Load multiple namespaces</span>
  <span class="hljs-keyword">const</span> { t } = useTranslation([<span class="hljs-string">'dashboard'</span>, <span class="hljs-string">'common'</span>], {
    useSuspense: <span class="hljs-literal">false</span>
  });

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;h1&gt;{t(<span class="hljs-string">'dashboard:title'</span>)}&lt;/h1&gt;
      &lt;button&gt;{t(<span class="hljs-string">'common:actions.save'</span>)}&lt;/button&gt;
    &lt;/div&gt;
  );
}
</code></pre>
<p>By splitting translation files and implementing lazy loading, we achieve a smaller initial bundle size since only necessary translations are loaded. The on-demand translation loading means resources are fetched only when needed, while better caching capabilities ensure efficient resource management.</p>
<p><strong>💡 Quick Link:</strong> For this section's code, check out the <a target="_blank" href="https://github.com/othmanekahtal/react-i18n-practical-guide/tree/performance">performance</a> branch</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This guide has walked you through implementing internationalization in modern web applications using i18next, from basic setup to handling complex formatting, plural rules, and RTL support. You can find the complete implementation and examples in the <a target="_blank" href="https://github.com/othmanekahtal/react-i18n-practical-guide">GitHub repository.</a> While we've covered significant ground, there's always more to explore in the world of i18next and internationalization. If you need help implementing these features in your application or are interested in collaborating on i18n-related projects, feel free to reach out to me on <a target="_blank" href="https://www.linkedin.com/in/othmanekahtal/">LinkedIn</a> or via email at contact@othmanekahtal.me. Stay tuned for more articles diving into advanced i18next features and internationalization patterns!</p>
]]></content:encoded></item><item><title><![CDATA[Serverless CSV Processing with AWS SAM: Lambda, S3, and DynamoDB Guide]]></title><description><![CDATA[As part of my AWS Certified Developer Associate (DVA-C02) exam preparation, I've built a practical project to understand core AWS services better. This project demonstrates how to process CSV files using AWS SAM, Lambda, S3, and DynamoDB – all common...]]></description><link>https://blog.othmanekahtal.me/serverless-csv-processing-with-aws-sam-lambda-s3-and-dynamodb-guide</link><guid isPermaLink="true">https://blog.othmanekahtal.me/serverless-csv-processing-with-aws-sam-lambda-s3-and-dynamodb-guide</guid><category><![CDATA[AWS]]></category><category><![CDATA[lambda]]></category><category><![CDATA[DynamoDB]]></category><category><![CDATA[serverless]]></category><category><![CDATA[Cloud Computing]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[S3-bucket]]></category><category><![CDATA[AWS Certified Developer - Associate]]></category><dc:creator><![CDATA[Othmane Kahtal]]></dc:creator><pubDate>Mon, 11 Nov 2024 13:10:57 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/fHXP17AxOEk/upload/6869b7b35b6895b33d5a625c88d4a667.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As part of my AWS Certified Developer Associate (DVA-C02) exam preparation, I've built a practical project to understand core AWS services better. This project demonstrates how to process CSV files using AWS SAM, Lambda, S3, and DynamoDB – all common topics in the certification exam.</p>
<h2 id="heading-project-repository">Project Repository</h2>
<p>You can find the complete source code for this project on GitHub: <a target="_blank" href="https://github.com/othmanekahtal/import-clients-aws-app">Import Clients' Data via CSV</a></p>
<h2 id="heading-prerequisites">Prerequisites:</h2>
<ul>
<li><p>Basic understanding of AWS Lambda and S3</p>
</li>
<li><p>AWS SAM CLI installed</p>
</li>
<li><p>Node.js knowledge</p>
</li>
<li><p>AWS account with appropriate permissions</p>
</li>
</ul>
<h2 id="heading-project-overview">Project Overview</h2>
<p>This application automates the process of importing clients' data via CSV files. Here's what it does:</p>
<ol>
<li><p>Accepts CSV file uploads to an S3 bucket</p>
</li>
<li><p>Triggers a Lambda function to process the file</p>
</li>
<li><p>Validates and stores the data in DynamoDB</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731330221188/4bec6e95-d835-44f5-83c4-805e9cebb275.jpeg" alt class="image--center mx-auto" /></p>
<h2 id="heading-implementation-details">Implementation Details</h2>
<h3 id="heading-sam-template">SAM Template</h3>
<p>Our infrastructure is defined using AWS SAM:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">AWSTemplateFormatVersion:</span> <span class="hljs-string">'2010-09-09'</span>
<span class="hljs-attr">Transform:</span> <span class="hljs-string">AWS::Serverless-2016-10-31</span>
<span class="hljs-attr">Description:</span> <span class="hljs-string">Lambda</span> <span class="hljs-string">function</span> <span class="hljs-string">with</span> <span class="hljs-string">S3</span> <span class="hljs-string">and</span> <span class="hljs-string">DynamoDB</span> <span class="hljs-string">access</span>

<span class="hljs-attr">Parameters:</span>
  <span class="hljs-attr">ExistingBucketName:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">String</span>
    <span class="hljs-attr">Description:</span> <span class="hljs-string">Name</span> <span class="hljs-string">of</span> <span class="hljs-string">the</span> <span class="hljs-string">existing</span> <span class="hljs-string">S3</span> <span class="hljs-string">bucket</span> <span class="hljs-string">to</span> <span class="hljs-string">use</span>

<span class="hljs-attr">Resources:</span>
  <span class="hljs-attr">AppDynamoDBTable:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::Serverless::SimpleTable</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">PrimaryKey:</span>
        <span class="hljs-attr">Name:</span> <span class="hljs-string">id</span>
        <span class="hljs-attr">Type:</span> <span class="hljs-string">String</span>
      <span class="hljs-attr">ProvisionedThroughput:</span>
        <span class="hljs-attr">ReadCapacityUnits:</span> <span class="hljs-number">5</span>
        <span class="hljs-attr">WriteCapacityUnits:</span> <span class="hljs-number">5</span>

  <span class="hljs-attr">S3JsonLoggerFunction:</span>
    <span class="hljs-attr">Type:</span> <span class="hljs-string">AWS::Serverless::Function</span>
    <span class="hljs-attr">Properties:</span>
      <span class="hljs-attr">Handler:</span> <span class="hljs-string">src/handlers/s3-json-logger.s3JsonLoggerHandler</span>
      <span class="hljs-attr">Runtime:</span> <span class="hljs-string">nodejs18.x</span>
      <span class="hljs-attr">Architectures:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-string">x86_64</span>
      <span class="hljs-attr">MemorySize:</span> <span class="hljs-number">128</span>
      <span class="hljs-attr">Timeout:</span> <span class="hljs-number">60</span>
      <span class="hljs-attr">Policies:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">S3ReadPolicy:</span>
            <span class="hljs-attr">BucketName:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">ExistingBucketName</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">DynamoDBCrudPolicy:</span>
            <span class="hljs-attr">TableName:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">AppDynamoDBTable</span>
      <span class="hljs-attr">Environment:</span>
        <span class="hljs-attr">Variables:</span>
          <span class="hljs-attr">APPDYNAMODBTABLE_TABLE_NAME:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">AppDynamoDBTable</span>
          <span class="hljs-attr">APPDYNAMODBTABLE_TABLE_ARN:</span> <span class="hljs-type">!GetAtt</span> <span class="hljs-string">AppDynamoDBTable.Arn</span>
          <span class="hljs-attr">S3_BUCKET_NAME:</span> <span class="hljs-type">!Ref</span> <span class="hljs-string">ExistingBucketName</span>
</code></pre>
<h3 id="heading-lambda-function">Lambda Function</h3>
<p>The Lambda function processes CSV files and stores data in DynamoDB:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { S3Client, GetObjectCommand } <span class="hljs-keyword">from</span> <span class="hljs-string">"@aws-sdk/client-s3"</span>;
<span class="hljs-keyword">import</span> { sdkStreamMixin } <span class="hljs-keyword">from</span> <span class="hljs-string">"@aws-sdk/util-stream-node"</span>;
<span class="hljs-keyword">import</span> { parseString } <span class="hljs-keyword">from</span> <span class="hljs-string">"@fast-csv/parse"</span>;
<span class="hljs-keyword">import</span> { DynamoDBClient } <span class="hljs-keyword">from</span> <span class="hljs-string">"@aws-sdk/client-dynamodb"</span>;
<span class="hljs-keyword">import</span> {
  DynamoDBDocumentClient,
  PutCommand,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@aws-sdk/lib-dynamodb"</span>;

<span class="hljs-keyword">const</span> tableName = process.env.APPDYNAMODBTABLE_TABLE_NAME;
<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> DynamoDBClient({});
<span class="hljs-keyword">const</span> dynamo = DynamoDBDocumentClient.from(client);
<span class="hljs-keyword">const</span> s3 = <span class="hljs-keyword">new</span> S3Client({});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> s3JsonLoggerHandler = <span class="hljs-keyword">async</span> (event, context) =&gt; {
  <span class="hljs-keyword">const</span> getObjectRequests = event.Records.map(<span class="hljs-function">(<span class="hljs-params">record</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> processS3Object(
      <span class="hljs-keyword">new</span> GetObjectCommand({
        <span class="hljs-attr">Bucket</span>: record.s3.bucket.name,
        <span class="hljs-attr">Key</span>: record.s3.object.key,
      })
    );
  });
  <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(getObjectRequests);
};

<span class="hljs-comment">// Data validation</span>
<span class="hljs-keyword">const</span> isValidClient = <span class="hljs-function">(<span class="hljs-params">record</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { name, email, company, phone, id, status } = record;
  <span class="hljs-keyword">return</span> (
    name &amp;&amp;
    email &amp;&amp;
    company &amp;&amp;
    phone &amp;&amp;
    id &amp;&amp;
    status &amp;&amp;
    <span class="hljs-regexp">/^[^\s@]+@[^\s@]+\.[^\s@]+$/</span>.test(email)
  );
};

<span class="hljs-comment">// Process S3 objects</span>
<span class="hljs-keyword">const</span> processS3Object = <span class="hljs-keyword">async</span> (object) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> s3.send(object);
    <span class="hljs-keyword">const</span> objectString = <span class="hljs-keyword">await</span> sdkStreamMixin(data.Body).transformToString();

    <span class="hljs-keyword">const</span> records = <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> records = [];
      parseString(objectString, { <span class="hljs-attr">headers</span>: <span class="hljs-literal">true</span> })
        .on(<span class="hljs-string">"error"</span>, <span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
          <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error parsing CSV:"</span>, error);
          reject(error);
        })
        .on(<span class="hljs-string">"data"</span>, <span class="hljs-keyword">async</span> (row) =&gt; {
          <span class="hljs-keyword">if</span> (isValidClient(row)) {
            records.push(row);
          }
        })
        .on(<span class="hljs-string">"end"</span>, <span class="hljs-function">(<span class="hljs-params">rowCount</span>) =&gt;</span> {
          resolve(records);
        });
    });

    <span class="hljs-keyword">const</span> insertPromises = records.map(insertIntoDynamoDB);
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(insertPromises);
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error in processS3Object:"</span>, err);
    <span class="hljs-keyword">throw</span> err;
  }
};

<span class="hljs-comment">// Insert into DynamoDB</span>
<span class="hljs-keyword">const</span> insertIntoDynamoDB = <span class="hljs-keyword">async</span> (record) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> command = <span class="hljs-keyword">new</span> PutCommand({
      <span class="hljs-attr">TableName</span>: tableName,
      <span class="hljs-attr">Item</span>: {
        <span class="hljs-attr">id</span>: record.id,
        <span class="hljs-attr">name</span>: record.name,
        <span class="hljs-attr">email</span>: record.email,
        <span class="hljs-attr">company</span>: record.company,
        <span class="hljs-attr">phone</span>: record.phone,
        <span class="hljs-attr">status</span>: record.status,
      },
    });
    <span class="hljs-keyword">await</span> dynamo.send(command);
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">`Error inserting client <span class="hljs-subst">${record.email}</span>:`</span>, err);
  }
};
</code></pre>
<p><strong>Smart Variable Usage</strong>: This project uses SAM parameters and environment variables instead of hard-coded values for S3 bucket names and DynamoDB configurations. This makes the application easy to maintain and deploy across different environments without code changes.</p>
<p><strong>Event-Driven Architecture</strong>: The application uses S3 events to trigger Lambda functions automatically. This serverless approach enables efficient processing of multiple CSV files in parallel, improving performance for large data sets.</p>
<p><strong>Data Validation</strong>: Before storing in DynamoDB, each record goes through validation checks for email format and required fields. This ensures data quality while properly handling and logging any invalid records.</p>
<h2 id="heading-csv-format">CSV Format</h2>
<p>Your CSV files should follow this structure:</p>
<pre><code class="lang-plaintext">id,name,email,company,phone,status
1,John Doe,john@example.com,Acme Inc,+1234567890,active
2,Jane Smith,jane@example.com,Tech Corp,+0987654321,pending
</code></pre>
<h2 id="heading-dva-c02-exam-topics-covered">DVA-C02 Exam Topics Covered</h2>
<p>This project helps understand several key exam topics:</p>
<ul>
<li><p>SAM template structure and deployment</p>
</li>
<li><p>Lambda function development</p>
</li>
<li><p>S3 event notifications</p>
</li>
<li><p>DynamoDB operations</p>
</li>
<li><p>IAM roles and policies</p>
</li>
<li><p>Error handling patterns</p>
</li>
</ul>
<h2 id="heading-deployment">Deployment</h2>
<p>Deploy the application using SAM CLI (check the repository for the guide):</p>
<pre><code class="lang-bash">sam build
sam deploy --guided
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Why Internationalization and localization are No Longer Optional for Modern Apps]]></title><description><![CDATA[Internationalization and localization are crucial parts to take into consideration when you start your application or product, to make it limitless across the world. In this article, we'll dive deep into these concepts and why you should take care of...]]></description><link>https://blog.othmanekahtal.me/why-internationalization-and-localization-are-no-longer-optional-for-modern-apps</link><guid isPermaLink="true">https://blog.othmanekahtal.me/why-internationalization-and-localization-are-no-longer-optional-for-modern-apps</guid><category><![CDATA[localization]]></category><category><![CDATA[Localization Services]]></category><category><![CDATA[i18n]]></category><category><![CDATA[i18next]]></category><category><![CDATA[internationalization]]></category><dc:creator><![CDATA[Othmane Kahtal]]></dc:creator><pubDate>Sat, 02 Nov 2024 15:06:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/l68Z6eF2peA/upload/c27af6e22a24ac6aa6dd64df29362c9d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Internationalization and localization are crucial parts to take into consideration when you start your application or product, to make it limitless across the world. In this article, we'll dive deep into these concepts and why you should take care of them in your product.</p>
<h2 id="heading-what-is-internationalization">What is Internationalization:</h2>
<p>Internationalization or i18n (18 represents the number of characters between 'i' and 'n') is a process to make your product ready for a global audience by adopting multiple languages and cultures, as well as adding a smooth switching in your product based on user preferences.</p>
<p>Let's take a look at PayPal as a real example. When you select China as your region, the website adapts completely - not just the language changes, but also the example images used to demonstrate transactions. They show payment scenarios involving neighboring countries (India, Thailand, China) as well as transactions with distant countries like the United States.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730554346054/0b859f56-27b7-4cc1-a6ca-c34e250b4065.png" alt="PayPal china region" class="image--center mx-auto" /></p>
<p>In this section, we can see that even the testimonials are localized - PayPal displays only region-specific feedback from Chinese users, making the content more relevant and relatable to their local audience</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730554591023/da2af941-0f9e-43d7-92b9-b7d2a82a5e27.png" alt class="image--center mx-auto" /></p>
<p>Now, if we change the region to Saudi Arabia, we see a completely different page that respects the local culture and legal requirements of this region</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730555014363/be29d017-e9f5-4c2a-88fa-734a915a984e.png" alt class="image--center mx-auto" /></p>
<p>The example above shows how PayPal creates region-specific landing pages, adapting their content, layout, and visuals based on each region's requirements and preferences</p>
<h2 id="heading-internationalization-best-practices">Internationalization best practices:</h2>
<p>There are many things to keep in mind when implementing i18n properly, regardless of your role (Frontend or Backend engineer). before writing any code, you need to consider several crucial aspects:</p>
<ul>
<li><p><strong>Database Design Considerations:</strong></p>
<ul>
<li><p>User preferences storage</p>
</li>
<li><p>Preferred language(s)</p>
</li>
<li><p>Region/location settings</p>
</li>
<li><p>Timezone</p>
</li>
<li><p>Date format preferences</p>
</li>
<li><p>Number format preferences (decimal separators, thousand separators)</p>
</li>
<li><p>Currency preferences</p>
</li>
</ul>
</li>
<li><p><strong>Content Management:</strong></p>
<ul>
<li><p>Text content in multiple languages</p>
</li>
<li><p>Media assets adapted for different cultures</p>
</li>
<li><p>Region-specific legal content</p>
</li>
<li><p>Direction of text (LTR vs RTL support)</p>
</li>
<li><p>Local holidays and important dates</p>
</li>
<li><p>Region-specific contact information</p>
</li>
</ul>
</li>
<li><p><strong>Technical Implementation:</strong></p>
<ul>
<li><p>Smooth language/region switching without page reload</p>
</li>
<li><p>Proper data formatting based on locale</p>
<ul>
<li><p>Numbers (1,234.56 vs 1.234,56)</p>
</li>
<li><p>Dates (MM/DD/YYYY vs DD/MM/YYYY)</p>
</li>
<li><p>Times (12-hour vs 24-hour format)</p>
</li>
<li><p>Phone numbers</p>
</li>
<li><p>Addresses</p>
</li>
</ul>
</li>
<li><p>Currency conversion and display</p>
</li>
<li><p>Timezone handling for scheduled events</p>
</li>
<li><p>Form validation for different formats</p>
</li>
<li><p>Search functionality across multiple languages</p>
</li>
</ul>
</li>
<li><p><strong>User Experience:</strong></p>
<ul>
<li><p>Automatic language detection</p>
</li>
<li><p>Remembering user preferences</p>
</li>
<li><p>Fallback languages when translations are missing</p>
</li>
<li><p>Loading states during language switches</p>
</li>
<li><p>Clear language/region selection interface</p>
</li>
<li><p>Consistent formatting throughout the application</p>
</li>
</ul>
</li>
<li><p><strong>Performance Considerations:</strong></p>
<ul>
<li><p>Lazy loading of language packs</p>
</li>
<li><p>Caching strategies for localized content</p>
</li>
<li><p>Bundle size management for multiple languages</p>
</li>
<li><p>Server-side rendering with correct locale</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-what-is-localization">What is localization:</h2>
<p>Localization or l10n (10 represents the number of characters between 'l' and 'n') is the process of adapting internationalized products and taking care of their content, whether it's images, videos, text, documents, colors, legal requirements, fonts, etc.</p>
<p>Let's go back to PayPal as an example. We notice that PayPal goes beyond just language translation and adapts both images and structure based on the activities and products they provide in each region. For instance, look at the image of the person used in the guide's cover.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730555833363/7457fb4b-0e1b-405e-953f-cd2302744bc7.png" alt class="image--center mx-auto" /></p>
<p>Let's look at another region, like India. As we can see, the internationalization is working as expected - in this region, PayPal localizes the landing page specifically for the Indian market. We can notice this in both the images used and the types of businesses showcased below.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730556037970/7fc6b227-7671-4d0d-b048-4da1a04f806e.png" alt class="image--center mx-auto" /></p>
<p>Looking at Singapore's version, we can see several regional adaptations: the phone number formats are localized, the hero section displays a different background</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1730556370162/c78a046c-1570-45c9-a3e2-91e08d502717.png" alt class="image--center mx-auto" /></p>
<p>Internationalization and localization work in combination to maximize the chances of making your product reachable to a global audience. Engineers and developers take on the duty of implementing internationalization, while localization is a mission for translators.</p>
<p>Both concepts fall under the umbrella of globalization - making products ready to use everywhere in the world across all markets, regardless of cultures, languages, or regions.</p>
<p>As demonstrated in the examples above, PayPal takes a comprehensive approach to localization. Their region-specific landing pages go beyond simple translation - they adapt content, layout, visuals, and even featured businesses to match each region's unique requirements, cultural preferences, and market characteristics</p>
<h2 id="heading-i18n-vs-i10n-key-differences">I18n vs I10n : Key differences</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Internationalization</strong></td><td><strong>localization</strong></td></tr>
</thead>
<tbody>
<tr>
<td>first phase, handled by developers &amp; engineers</td><td>second phase of globalization, handled by translators, designers, marketers, and copywriters</td></tr>
<tr>
<td>comes to facilitate localization</td><td>comes to facilitate adaptation based on culture and region</td></tr>
<tr>
<td>It's a process to develop products that support multiple cultures and different markets</td><td>it's a process to adapt content, including: Layout - Images - Videos - Colors and many other cultural elements</td></tr>
</tbody>
</table>
</div><h2 id="heading-the-culture-is-more-than-different-language">The culture is more than different language</h2>
<p>Local culture in each region isn't just about different languages - it's about different worlds, ideas, and much more. What's acceptable in one region might not be in others, like the Middle East or China. For example, red symbolizes luck and prosperity in Chinese culture, but can represent danger or warning in Western countries</p>
<h3 id="heading-examples-about-that">Examples about that :</h3>
<ul>
<li><p><strong>E-commerce Platforms:</strong></p>
<ul>
<li><p>In the US, Black Friday is the biggest shopping event</p>
</li>
<li><p>In China, Singles' Day (11.11) is the major shopping festival</p>
</li>
<li><p>In Middle Eastern countries, Ramadan-specific shopping sections are prominent</p>
</li>
<li><p>In Japan, gift-wrapping options are crucial as presentation is highly valued</p>
</li>
</ul>
</li>
<li><p><strong>Payment Preferences:</strong></p>
<ul>
<li><p>In China, QR code payments (WeChat Pay, Alipay) dominate</p>
</li>
<li><p>In Japan, cash payments are still widely preferred</p>
</li>
<li><p>In Northern Europe, card payments are standard</p>
</li>
<li><p>In India, Cash on Delivery is very popular</p>
</li>
</ul>
</li>
<li><p><strong>Numbers and Symbols:</strong></p>
<ul>
<li><p>Number <a target="_blank" href="https://en.wikipedia.org/wiki/Tetraphobia">4 is avoided</a> in East Asian products (<a target="_blank" href="https://en.wikipedia.org/wiki/13_\(number\)#cite_note-about1-12">similar to 13 in Western countries</a>)</p>
</li>
<li><p><a target="_blank" href="https://en.wikipedia.org/wiki/Green_in_Islam">Green is positive in Islamic countries</a> but associated with infidelity in China</p>
</li>
<li><p><a target="_blank" href="https://www.businessinsider.com/hand-gestures-offensive-different-countries-2018-6#and-definitely-dont-give-a-thumbs-up-in-the-middle-east-6">Thumbs-up gesture</a> is offensive in some Middle Eastern countries</p>
</li>
<li><p><a target="_blank" href="https://texasdebrazil.com/recipes/brazilian-highlight-local-customs/">OK hand gesture</a> is considered rude in Brazil</p>
</li>
</ul>
</li>
<li><p><strong>Content Presentation:</strong></p>
<ul>
<li><p>In Western designs, white space is considered elegant</p>
</li>
<li><p>In East Asian markets, dense information layouts are often preferred</p>
</li>
<li><p>In Arab countries, designs should accommodate right-to-left reading</p>
</li>
<li><p>In Japan, cute mascots (kawaii culture) are effective for business communication</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-conclusion">conclusion:</h2>
<p>We can recap everything we discussed in this article: globalization is a crucial part of launching a successful product that respects both global requirements and local cultures. It's not a simple task but rather a continuous workflow, starting with prioritizing your target markets and ending with teams working closely together to achieve this goal.</p>
]]></content:encoded></item><item><title><![CDATA[TypeScript for React Developers Series: Practical Tips and Real-World Scenarios]]></title><description><![CDATA[Switching to TypeScript can significantly enhance your React development experience by making your code more maintainable and scalable. With TypeScript’s powerful type-checking, you’ll catch bugs early, enjoy better code navigation, and find refactor...]]></description><link>https://blog.othmanekahtal.me/typescript-for-react-developers-series-practical-tips-and-real-world-scenarios</link><guid isPermaLink="true">https://blog.othmanekahtal.me/typescript-for-react-developers-series-practical-tips-and-real-world-scenarios</guid><category><![CDATA[TypeScript Tutorial]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[React]]></category><category><![CDATA[react hooks]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Othmane Kahtal]]></dc:creator><pubDate>Sat, 31 Aug 2024 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729797986710/d1ca2998-ee5b-47db-8bfe-91c449c741e7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Switching to TypeScript can significantly enhance your React development experience by making your code more maintainable and scalable. With TypeScript’s powerful type-checking, you’ll catch bugs early, enjoy better code navigation, and find refactoring easier. In this article, we’ll dive into practical tips and real-world scenarios to help you effectively adopt TypeScript in your React projects. Whether you’re new to TypeScript or looking to refine your skills, this guide has something for you. Let’s get started!</p>
<h2 id="heading-prerequisites"><strong>Prerequisites:</strong></h2>
<ul>
<li><p>Familiarity with basic TypeScript types</p>
</li>
<li><p>Familiarity with React.js</p>
</li>
<li><p>Reading the <a target="_blank" href="https://blog.othmane.me/typescript-for-react-developers-series-how-to-get-started"><strong>previous article</strong></a> is recommended</p>
</li>
</ul>
<p>TypeScript provides several built-in utilities that help reduce type duplication, making your code cleaner and easier to maintain. Let’s explore some of these utilities and see how they can be applied in real-world scenarios.</p>
<h2 id="heading-for-objects"><strong>For Objects:</strong></h2>
<p>TypeScript provides utilities to avoid duplicating type definitions for objects. Let’s start with a basic object and apply some of these utilities!</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> User = {
  email: <span class="hljs-built_in">string</span>;
  first_name: <span class="hljs-built_in">string</span>;
  last_name: <span class="hljs-built_in">string</span>;
  username: <span class="hljs-built_in">string</span>;
}
</code></pre>
<h3 id="heading-pick-vs-omit"><strong>Pick vs. Omit:</strong></h3>
<p>Sometimes, we need to create a type similar to an existing one but with some properties added or removed. TypeScript provides the <code>Omit</code> and <code>Pick</code> utilities to make this easy:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> User = {
  email: <span class="hljs-built_in">string</span>;
  first_name: <span class="hljs-built_in">string</span>;
  last_name: <span class="hljs-built_in">string</span>;
  username: <span class="hljs-built_in">string</span>;
}

<span class="hljs-comment">// Create new type without email and username:</span>
<span class="hljs-keyword">type</span> UserWithoutEmailUsername = Omit&lt;User, <span class="hljs-string">'email'</span> | <span class="hljs-string">'username'</span>&gt;
<span class="hljs-comment">// Result:</span>
<span class="hljs-comment">// type UserWithoutEmailUsername = {</span>
<span class="hljs-comment">//   last_name: string;</span>
<span class="hljs-comment">//   first_name: string;</span>
<span class="hljs-comment">// }</span>
</code></pre>
<p>In this example, we use <code>Omit</code> to exclude <code>email</code> and <code>username</code> from the <code>User</code> type.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> User = {
  email: <span class="hljs-built_in">string</span>;
  first_name: <span class="hljs-built_in">string</span>;
  last_name: <span class="hljs-built_in">string</span>;
  username: <span class="hljs-built_in">string</span>;
}

<span class="hljs-comment">// Create new type with only email and username:</span>
<span class="hljs-keyword">type</span> UserWithEmailUsername = Pick&lt;User, <span class="hljs-string">'email'</span> | <span class="hljs-string">'username'</span>&gt;
<span class="hljs-comment">// Result:</span>
<span class="hljs-comment">// type UserWithEmailUsername = {</span>
<span class="hljs-comment">//   email: string;</span>
<span class="hljs-comment">//   username: string;</span>
<span class="hljs-comment">// }</span>
</code></pre>
<p>Here, we use <code>Pick</code> to include only <code>email</code> and <code>username</code> from the <code>User</code> type.</p>
<p>So, when should you use <code>Pick</code> or <code>Omit</code>? It depends on the type you want to manipulate and how you want to modify it.</p>
<h3 id="heading-required-vs-partial"><strong>Required vs. Partial:</strong></h3>
<p>Let’s consider a real scenario: you’re building an API application with endpoints for creating and updating resources. You need different types for each service function. Your code might look like this:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> User = {
    email: <span class="hljs-built_in">string</span>;
    first_name: <span class="hljs-built_in">string</span>;
    last_name: <span class="hljs-built_in">string</span>;
    username: <span class="hljs-built_in">string</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span>(<span class="hljs-params">user: User</span>) </span>{
    <span class="hljs-comment">// ....</span>
    <span class="hljs-keyword">return</span> {
        email: user.email,
        username: user.username,
        id: <span class="hljs-number">10</span>
    }
}
<span class="hljs-keyword">type</span> OptionalUser = {
    email?: <span class="hljs-built_in">string</span>;
    first_name?: <span class="hljs-built_in">string</span>;
    last_name?: <span class="hljs-built_in">string</span>;
    username?: <span class="hljs-built_in">string</span>;
}
<span class="hljs-comment">// You need an ID to query the user that you want to update</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateUser</span>(<span class="hljs-params">user: OptionalUser, id: <span class="hljs-built_in">number</span></span>) </span>{
    <span class="hljs-comment">// ....</span>
    <span class="hljs-keyword">return</span> {
        email: user.email,
        username: user.username,
        id: <span class="hljs-number">10</span>
    }
}
</code></pre>
<p>But you can simplify this using <code>Partial</code>, making your code cleaner:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> User = {
    email: <span class="hljs-built_in">string</span>;
    first_name: <span class="hljs-built_in">string</span>;
    last_name: <span class="hljs-built_in">string</span>;
    username: <span class="hljs-built_in">string</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span>(<span class="hljs-params">user: User</span>) </span>{
    <span class="hljs-comment">// ....</span>
    <span class="hljs-keyword">return</span> {
        email: user.email,
        username: user.username,
        id: <span class="hljs-number">10</span>
    }
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateUser</span>(<span class="hljs-params">user: Partial&lt;User&gt;, id: <span class="hljs-built_in">number</span></span>) </span>{
    <span class="hljs-comment">// ....</span>
    <span class="hljs-keyword">return</span> {
        email: user?.email,
        username: user?.username,
        id: <span class="hljs-number">10</span>
    }
}
</code></pre>
<p>This makes your code easier to understand and more consistent.</p>
<p>What if you have a type with optional properties, and you want to clone this type but make all properties required? TypeScript provides the <code>Required</code> utility:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> OptionalUser = {
    email?: <span class="hljs-built_in">string</span>;
    first_name?: <span class="hljs-built_in">string</span>;
    last_name?: <span class="hljs-built_in">string</span>;
    username: <span class="hljs-built_in">string</span>;
}

<span class="hljs-keyword">type</span> RequiredUser = Required&lt;OptionalUser&gt;
</code></pre>
<h3 id="heading-readonly"><strong>Readonly:</strong></h3>
<p>When working with React, the props passed to a component shouldn’t be modified within the component to enforce immutability. TypeScript provides the <code>readonly</code> modifier to prevent reassignment of props, supporting this best practice. <a target="_blank" href="https://rules.sonarsource.com/typescript/RSPEC-6759/">React props should be read-only</a>.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-comment">// Define the props interface with readonly</span>
<span class="hljs-keyword">interface</span> UserCardProps {
  <span class="hljs-keyword">readonly</span> name: <span class="hljs-built_in">string</span>;
  <span class="hljs-keyword">readonly</span> age: <span class="hljs-built_in">number</span>;
}<span class="hljs-keyword">const</span> UserCard: React.FC&lt;UserCardProps&gt; = <span class="hljs-function">(<span class="hljs-params">{ name, age }</span>) =&gt;</span> {
  <span class="hljs-comment">// Attempting to modify name or age will cause a TypeScript error</span>
  <span class="hljs-comment">// name = "New Name"; // Error: Cannot assign to 'name' because it is a read-only property.  return (</span>
    &lt;div&gt;
      &lt;h2&gt;{name}&lt;/h2&gt;
      &lt;p&gt;Age: {age}&lt;/p&gt;
    &lt;/div&gt;
  );
};<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> UserCard;
</code></pre>
<h3 id="heading-record"><strong>Record:</strong></h3>
<p>The <code>Record</code> utility is a powerful tool to create objects with specific key-value pairs, ensuring that all keys are present and that each key's value matches a defined type. Let's consider a real-world scenario where using the <code>Record</code> utility type would be beneficial. Imagine you are developing an admin dashboard for a web application where users have different roles, such as <code>admin</code>, <code>editor</code>, and <code>viewer</code>. Each role has specific permissions associated with it, such as the ability to <code>create</code>, <code>read</code>, <code>update</code>, or <code>delete</code> data. You can use the <code>Record</code> type to define and manage these roles and their permissions in a type-safe way.</p>
<p><strong>Define User Roles and Permissions:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Define possible roles</span>
<span class="hljs-keyword">type</span> UserRole = <span class="hljs-string">'admin'</span> | <span class="hljs-string">'editor'</span> | <span class="hljs-string">'viewer'</span>;

<span class="hljs-comment">// Define possible permissions</span>
<span class="hljs-keyword">type</span> Permission = <span class="hljs-string">'create'</span> | <span class="hljs-string">'read'</span> | <span class="hljs-string">'update'</span> | <span class="hljs-string">'delete'</span>;
<span class="hljs-comment">// Use Record to map roles to their permissions</span>
<span class="hljs-keyword">const</span> rolePermissions: Record&lt;UserRole, Permission[]&gt; = {
  admin: [<span class="hljs-string">'create'</span>, <span class="hljs-string">'read'</span>, <span class="hljs-string">'update'</span>, <span class="hljs-string">'delete'</span>],
  editor: [<span class="hljs-string">'create'</span>, <span class="hljs-string">'read'</span>, <span class="hljs-string">'update'</span>],
  viewer: [<span class="hljs-string">'read'</span>],
};
</code></pre>
<p><strong>Creating a Function to Check Permissions:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hasPermission</span>(<span class="hljs-params">role: UserRole, permission: Permission</span>): <span class="hljs-title">boolean</span> </span>{
  <span class="hljs-keyword">const</span> permissions = rolePermissions[role];
  <span class="hljs-keyword">return</span> permissions.includes(permission);
}
</code></pre>
<p><strong>Using the Function in a Component:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">type</span> UserProps = {
  role: UserRole;
};
<span class="hljs-keyword">const</span> Dashboard: React.FC&lt;UserProps&gt; = <span class="hljs-function">(<span class="hljs-params">{ role }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;h1&gt;Dashboard&lt;/h1&gt;
      {hasPermission(role, <span class="hljs-string">'create'</span>) &amp;&amp; (
        &lt;button&gt;Create New Item&lt;/button&gt;
      )}
      {hasPermission(role, <span class="hljs-string">'read'</span>) &amp;&amp; (
        &lt;div&gt;Here are the items you can view.&lt;/div&gt;
      )}
      {hasPermission(role, <span class="hljs-string">'update'</span>) &amp;&amp; (
        &lt;button&gt;Update Item&lt;/button&gt;
      )}
      {hasPermission(role, <span class="hljs-string">'delete'</span>) &amp;&amp; (
        &lt;button&gt;Delete Item&lt;/button&gt;
      )}
    &lt;/div&gt;
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Dashboard;
</code></pre>
<p><strong>Example Usage of the Dashboard Component:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> Dashboard <span class="hljs-keyword">from</span> <span class="hljs-string">'./Dashboard'</span>;

<span class="hljs-keyword">const</span> App: React.FC = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;h1&gt;Admin View&lt;/h1&gt;
      &lt;Dashboard role=<span class="hljs-string">"admin"</span> /&gt;
      &lt;h1&gt;Editor View&lt;/h1&gt;
      &lt;Dashboard role=<span class="hljs-string">"editor"</span> /&gt;
      &lt;h1&gt;Viewer View&lt;/h1&gt;
      &lt;Dashboard role=<span class="hljs-string">"viewer"</span> /&gt;
    &lt;/div&gt;
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<h3 id="heading-nonnullable"><strong>NonNullable:</strong></h3>
<p>Let’s consider a scenario where you have a form for managing user profiles. This form can be used to create a new user or edit an existing one. We’ll use TypeScript to handle these scenarios safely, ensuring that certain fields are non-nullable when the form is submitted.</p>
<p><strong>Defining the User Data Type:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> User {
  id?: <span class="hljs-built_in">number</span>; <span class="hljs-comment">// Optional during creation, required during editing</span>
  username: <span class="hljs-built_in">string</span>;
  email: <span class="hljs-built_in">string</span>;
  age?: <span class="hljs-built_in">number</span>;
}
</code></pre>
<p><strong>Form State Type:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> UserFormState = {
  id?: <span class="hljs-built_in">number</span>;
  username: <span class="hljs-built_in">string</span> | <span class="hljs-literal">undefined</span>;
  email: <span class="hljs-built_in">string</span> | <span class="hljs-literal">undefined</span>;
  age?: <span class="hljs-built_in">number</span> | <span class="hljs-literal">undefined</span>;
};
</code></pre>
<p>This type allows fields like <code>username</code> and <code>email</code> to be <code>undefined</code> initially, especially when editing.</p>
<p><strong>Using</strong> <code>NonNullable</code> <strong>to Ensure Non-Nullable Fields</strong></p>
<p>When submitting the form, you want to make sure that <code>username</code> and <code>email</code> are always defined:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> ValidUser = Omit&lt;User, <span class="hljs-string">'id'</span>&gt; &amp; {
  id?: <span class="hljs-built_in">number</span>;
  username: NonNullable&lt;UserFormState[<span class="hljs-string">'username'</span>]&gt;;
  email: NonNullable&lt;UserFormState[<span class="hljs-string">'email'</span>]&gt;;
};
</code></pre>
<p>Here, <code>ValidUser</code> ensures that <code>username</code> and <code>email</code> are non-nullable, making sure</p>
<p>these fields are always present when processing the form submission.</p>
<p><strong>Form Component Implementation:</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> React, { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">const</span> UserForm: React.FC&lt;{ initialData?: User }&gt; = <span class="hljs-function">(<span class="hljs-params">{ initialData }</span>) =&gt;</span> {
  <span class="hljs-comment">// Initialize form state with optional initial data</span>
  <span class="hljs-keyword">const</span> [formState, setFormState] = useState&lt;UserFormState&gt;({
    id: initialData?.id,
    username: initialData?.username,
    email: initialData?.email,
    age: initialData?.age,
  });
  <span class="hljs-keyword">const</span> handleChange = <span class="hljs-function">(<span class="hljs-params">event: React.ChangeEvent&lt;HTMLInputElement&gt;</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { name, value } = event.target;
    setFormState(<span class="hljs-function">(<span class="hljs-params">prevState</span>) =&gt;</span> ({ ...prevState, [name]: value }));
  };
  <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// Assert that username and email are non-nullable</span>
    <span class="hljs-keyword">const</span> { id, username, email, age } = formState;
    <span class="hljs-keyword">if</span> (!username || !email) {
      alert(<span class="hljs-string">'Username and email are required.'</span>);
      <span class="hljs-keyword">return</span>;
    }
    <span class="hljs-keyword">const</span> validUser: ValidUser = {
      id,
      username,
      email,
      age,
    };
    <span class="hljs-comment">// Proceed with form submission logic</span>
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Submitting form:'</span>, validUser);
  };
  <span class="hljs-keyword">return</span> (
    &lt;form onSubmit={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> e.preventDefault()}&gt;
      &lt;div&gt;
        &lt;label&gt;
          Username:
          &lt;input
            <span class="hljs-keyword">type</span>=<span class="hljs-string">"text"</span>
            name=<span class="hljs-string">"username"</span>
            value={formState.username || <span class="hljs-string">''</span>}
            onChange={handleChange}
            required
          /&gt;
        &lt;/label&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;label&gt;
          Email:
          &lt;input
            <span class="hljs-keyword">type</span>=<span class="hljs-string">"email"</span>
            name=<span class="hljs-string">"email"</span>
            value={formState.email || <span class="hljs-string">''</span>}
            onChange={handleChange}
            required
          /&gt;
        &lt;/label&gt;
      &lt;/div&gt;
      &lt;div&gt;
        &lt;label&gt;
          Age:
          &lt;input
            <span class="hljs-keyword">type</span>=<span class="hljs-string">"number"</span>
            name=<span class="hljs-string">"age"</span>
            value={formState.age || <span class="hljs-string">''</span>}
            onChange={handleChange}
          /&gt;
        &lt;/label&gt;
      &lt;/div&gt;
      &lt;button <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span> onClick={handleSubmit}&gt;
        Submit
      &lt;/button&gt;
    &lt;/form&gt;
  );
};
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> UserForm;
</code></pre>
<h2 id="heading-for-functions"><strong>For Functions:</strong></h2>
<p>TypeScript gives us many utilities to work with functions, allowing us to infer types and avoid duplicating type definitions. Let’s dive into an example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> User = {
  email: <span class="hljs-built_in">string</span>;
  first_name: <span class="hljs-built_in">string</span>;
  last_name: <span class="hljs-built_in">string</span>;
  username: <span class="hljs-built_in">string</span>;
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser</span>(<span class="hljs-params">args: User</span>) </span>{
    <span class="hljs-comment">// ....</span>
    <span class="hljs-keyword">return</span> {
        id: <span class="hljs-number">12</span>,
        email: args.email,
        username: args.username
    }
}
</code></pre>
<p><strong>How can we infer the return type of this function?</strong></p>
<p>We have two methods to get the return type of this function:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> returnTypeOfCreateUser = Omit&lt;User, <span class="hljs-string">'last_name'</span> | <span class="hljs-string">'first_name'</span>&gt;
</code></pre>
<p>This approach works, but it doesn’t offer the flexibility or consistency needed if the return type of the function changes. To solve this, we can use <code>ReturnType</code> to directly infer the type:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> returnTypeOfCreateUser2 = ReturnType&lt;<span class="hljs-keyword">typeof</span> createUser&gt;
</code></pre>
<p><strong>What about async functions?</strong></p>
<pre><code class="lang-typescript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createUser2</span>(<span class="hljs-params">args: User</span>) </span>{
    <span class="hljs-comment">// ....</span>
    <span class="hljs-keyword">return</span> {
        id: <span class="hljs-number">12</span>,
        email: args.email,
        username: args.username
    }
}
</code></pre>
<p>The answer is yes! You can combine <code>Awaited</code> with <code>ReturnType</code> to handle async functions:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> returnTypeOfCreateUser3 = Awaited&lt;ReturnType&lt;<span class="hljs-keyword">typeof</span> createUser2&gt;&gt;
</code></pre>
<p><strong>What if I want to get the type of parameters that a function from an external library accepts?</strong></p>
<p>Imagine you’re using a third-party library, and you want to wrap one of its functions. You can use the <code>Parameters</code> utility to get the types of its parameters:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">type</span> paramsType = Parameters&lt;<span class="hljs-keyword">typeof</span> createUser&gt; <span class="hljs-comment">// type paramsType = [args: User]</span>
</code></pre>
<p>The <code>Parameters</code> utility returns an array of the parameter types that <code>createUser</code> accepts!</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>TypeScript’s utility types like <code>ReturnType</code>, <code>Parameters</code>, <code>Partial</code>, <code>Required</code>, <code>Omit</code>, <code>Pick</code>, <code>Readonly</code>, <code>Record</code>, and <code>NonNullable</code> make it easier to write clean, maintainable, and type-safe React code. By using these utilities, you can handle real-world scenarios effectively, reduce errors, and enhance the overall quality of your projects. Start leveraging these powerful features to take your React development to the next level. For more detailed information and advanced use cases, check out additional resources like the <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/utility-types.html">TypeScript Handbook</a> and the <a target="_blank" href="https://react.dev/reference/react">React Documentation</a>.</p>
]]></content:encoded></item><item><title><![CDATA[TypeScript for React Developers Series: How to Get Started]]></title><description><![CDATA[By adopting TypeScript, React developers take advantage of the language’s type safety, code analysis, and refactoring capabilities. In this article, we’ll explore the steps needed to adopt TypeScript.
Prerequisites:

Familiar with basic TypeScript ty...]]></description><link>https://blog.othmanekahtal.me/typescript-for-react-developers-series-how-to-get-started</link><guid isPermaLink="true">https://blog.othmanekahtal.me/typescript-for-react-developers-series-how-to-get-started</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[Front-end Development]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[ReactHooks]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Othmane Kahtal]]></dc:creator><pubDate>Thu, 09 Mar 2023 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729440652961/0885a417-c8c5-4a24-b6a0-67c70371a6f9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>By adopting TypeScript, React developers take advantage of the language’s type safety, code analysis, and refactoring capabilities. In this article, we’ll explore the steps needed to adopt TypeScript.</p>
<h2 id="heading-prerequisites"><strong>Prerequisites:</strong></h2>
<ul>
<li><p>Familiar with basic TypeScript types</p>
</li>
<li><p>Familiar with React.js</p>
</li>
<li><p>Reading the <a target="_blank" href="https://blog.othmane.me/typescript-for-react-developers-series-why-you-should-adopt-it">previous article</a></p>
</li>
</ul>
<h2 id="heading-naming-conventions"><strong>Naming Conventions:</strong></h2>
<ul>
<li><p>use PascalCase for class, component and Enum names.</p>
</li>
<li><p>use camelCase for variable and function names.</p>
</li>
<li><p>do not use <code>I</code> as prefix of the interface instead use <code>ComponentNameProps</code> or <code>FunctionNameParams</code>.</p>
</li>
<li><p>use <code>.generated.*</code> as suffix for the files are generated and you don't edit them.</p>
</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">interface</span> ComponentNameProps {
  position: <span class="hljs-string">'left'</span> | <span class="hljs-string">'right'</span>
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ComponentName</span>(<span class="hljs-params">{ position }: ComponentNameProps</span>) </span>{
  <span class="hljs-keyword">if</span> (position === <span class="hljs-string">'left'</span>) {
    <span class="hljs-keyword">return</span> &lt;div&gt;left&lt;/div&gt;
  }
  <span class="hljs-keyword">return</span> &lt;div&gt;right&lt;/div&gt;
}

<span class="hljs-keyword">interface</span> FunctionNameParams {
  args: <span class="hljs-built_in">string</span>[]
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">functionName</span>(<span class="hljs-params">{ args }: FunctionNameParams</span>) </span>{
  <span class="hljs-keyword">return</span> args.join(<span class="hljs-string">' '</span>)
}
</code></pre>
<h2 id="heading-types"><strong>Types:</strong></h2>
<ul>
<li><p>put the shared types in <code>types.ts</code> file.</p>
</li>
<li><p>use generics to avoid duplicating the components/functions.</p>
</li>
<li><p>do not export functions/types that not shared across multi-components.</p>
</li>
<li><p>in your component file, the type definitions must comes the first.</p>
</li>
<li><p>use <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/utility-types.html">Utility Types</a> to avoid duplicating of the type definitions.</p>
</li>
<li><p>use <code>readonly</code> to define the data coming from your API</p>
</li>
</ul>
<h2 id="heading-components"><strong>Components:</strong></h2>
<ul>
<li><p>don’t use <code>FunctionComponent&lt;T&gt;</code> or <code>FC&lt;T&gt;</code> to define your component, because it's explicit about the return type, while the normal function version is implicit, read more about this <a target="_blank" href="https://github.com/gndelia/codemod-replace-react-fc-typescript">codemod-replace-react-fc-typescript</a>.</p>
</li>
<li><p>use <code>ReactNode</code> instead <code>JSX.Element</code> to define your children when use the normal functions, because <code>JSX.Element</code> is the return value of <code>React.createElement</code>, while <code>ReactNode</code> is return value of a component</p>
</li>
<li><p>use generic components to make your component more flexible, <a target="_blank" href="https://www.developerway.com/posts/typescript-generics-for-react-developers">read more about this topic</a>.</p>
</li>
<li><p>use the <code>React.ComponentPropsWithoutRef&lt;T&gt;</code> as Wrapping/Mirroring a HTML Element when you want to make component that takes all the normal props of this Element.<a target="_blank" href="https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase">read more about this topic</a></p>
</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-comment">// bad</span>
<span class="hljs-keyword">interface</span> MyCustomLinkProps {
  href: <span class="hljs-built_in">string</span>
  children?: React.ReactNode
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyCustomLink</span>(<span class="hljs-params">props: MyCustomLinkProps</span>) </span>{
  <span class="hljs-keyword">const</span> { children, href } = props
  <span class="hljs-keyword">return</span> &lt;a href={href}&gt;{children}&lt;/a&gt;
}
<span class="hljs-comment">// most recommended</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyCustomLink</span>(<span class="hljs-params">props: React.ComponentPropsWithoutRef&lt;'a'&gt;</span>) </span>{
  <span class="hljs-keyword">return</span> &lt;a {...props}&gt;{children}&lt;/a&gt;
}
</code></pre>
<ul>
<li>use <code>Fragment</code> or use the shorthand <code>&lt;&gt;</code> instead <code>div</code> in wrapping the component.</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-comment">// bad</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ComponentName</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;section&gt;&lt;/section&gt;
      &lt;section&gt;&lt;/section&gt;
    &lt;/div&gt;
  )
}

<span class="hljs-comment">// most recommended</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ComponentName</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;section&gt;&lt;/section&gt;
      &lt;section&gt;&lt;/section&gt;
    &lt;/&gt;
  )
}
</code></pre>
<h2 id="heading-forms-and-events"><strong>Forms and Events:</strong></h2>
<ul>
<li>inline event handler:</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> InputComponent = <span class="hljs-function">() =&gt;</span> (
  &lt;input
    <span class="hljs-comment">// TypeScript automatically inffers it</span>
    onChange={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
      <span class="hljs-comment">// logic here</span>
    }}
  /&gt;
)
</code></pre>
<ul>
<li>separated event handler:</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> InputComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> onChnageInputHandler = (e: React.FormEvent&lt;HTMLInputElement&gt;): <span class="hljs-function"><span class="hljs-params">void</span> =&gt;</span> {
    <span class="hljs-comment">// your logic here</span>
  }
  <span class="hljs-keyword">return</span> &lt;input onChange={onChnageInputHandler} /&gt;
}
</code></pre>
<p>or:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> InputComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> onChnageInputHandler: React.ChangeEventHandler&lt;HTMLInputElement&gt; = <span class="hljs-function">(<span class="hljs-params">
    e
  </span>) =&gt;</span> {
    <span class="hljs-comment">// your logic here</span>
  }
  <span class="hljs-keyword">return</span> &lt;input onChange={onChnageInputHandler} /&gt;
}
</code></pre>
<ul>
<li><code>onSubmit</code> definition for uncontrolled form component</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> Form = <span class="hljs-function">() =&gt;</span> (
  &lt;form
    onSubmit={<span class="hljs-function">(<span class="hljs-params">e: React.SyntheticEvent</span>) =&gt;</span> {
      e.preventDefault()
      <span class="hljs-keyword">const</span> target = e.target <span class="hljs-keyword">as</span> <span class="hljs-keyword">typeof</span> e.target &amp; {
        email: { value: <span class="hljs-built_in">string</span> }
        password: { value: <span class="hljs-built_in">string</span> }
      }
      <span class="hljs-keyword">const</span> email = target.email.value <span class="hljs-comment">// typechecks!</span>
      <span class="hljs-keyword">const</span> password = target.password.value <span class="hljs-comment">// typechecks!</span>
      <span class="hljs-comment">// etc...</span>
    }}
  &gt;
    &lt;label&gt;
      Email:
      &lt;input <span class="hljs-keyword">type</span>=<span class="hljs-string">"email"</span> name=<span class="hljs-string">"email"</span> /&gt;
    &lt;/label&gt;
    &lt;label&gt;
      Password:
      &lt;input <span class="hljs-keyword">type</span>=<span class="hljs-string">"password"</span> name=<span class="hljs-string">"password"</span> /&gt;
    &lt;/label&gt;
    &lt;button <span class="hljs-keyword">type</span>=<span class="hljs-string">"submit"</span>&gt;Login&lt;/button&gt;
  &lt;/form&gt;
)
</code></pre>
<p>you can use formik or any other library for handling Forms, those libraries are built on the principles of controlled components</p>
<h2 id="heading-hooks"><strong>Hooks:</strong></h2>
<ul>
<li><code>useState</code> hook :</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> Component = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-comment">//1- inferred type:</span>
  <span class="hljs-comment">// `state` is inferred to be a number</span>
  <span class="hljs-keyword">const</span> [state, setState] = useState(<span class="hljs-number">0</span>)
  <span class="hljs-comment">// `stringState` is inferred to be a string</span>
  <span class="hljs-keyword">const</span> [stringState, setStringState] = useState(<span class="hljs-string">''</span>)
  <span class="hljs-comment">//2- implicit type:</span>
  <span class="hljs-keyword">const</span> [state1, setState2] = useState&lt;<span class="hljs-built_in">number</span> | <span class="hljs-literal">undefined</span>&gt;()
  <span class="hljs-keyword">return</span> &lt;&gt;&lt;/&gt;
}
</code></pre>
<ul>
<li><code>useCallback</code> hook :</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-comment">//   it's just like any other function</span>
<span class="hljs-keyword">interface</span> MemoizedFunctionParams {
params:<span class="hljs-built_in">string</span>[]
}
<span class="hljs-keyword">const</span> memoizedFunction = useCallback(
  <span class="hljs-function">(<span class="hljs-params">args:MemoizedFunctionParams</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-string">'text'</span>
  },
  [...],
);
</code></pre>
<ul>
<li><code>useReducer</code> hook:</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useReducer } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>

<span class="hljs-keyword">const</span> initialState = { count: <span class="hljs-number">0</span> }
<span class="hljs-keyword">type</span> ACTIONTYPE =
  | { <span class="hljs-keyword">type</span>: <span class="hljs-string">'increment'</span>; payload: <span class="hljs-built_in">number</span> }
  | { <span class="hljs-keyword">type</span>: <span class="hljs-string">'decrement'</span>; payload: <span class="hljs-built_in">string</span> }
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reducer</span>(<span class="hljs-params">state: <span class="hljs-keyword">typeof</span> initialState, action: ACTIONTYPE</span>) </span>{
  <span class="hljs-keyword">switch</span> (action.type) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">'increment'</span>:
      <span class="hljs-keyword">return</span> { count: state.count + action.payload }
    <span class="hljs-keyword">case</span> <span class="hljs-string">'decrement'</span>:
      <span class="hljs-keyword">return</span> { count: state.count - <span class="hljs-built_in">Number</span>(action.payload) }
    <span class="hljs-keyword">default</span>:
      <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>()
  }
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Counter</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// If you don't define return type, TypeScript automatically infers it.</span>
  <span class="hljs-keyword">const</span> [state, dispatch] = useReducer(reducer, initialState)
  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      Count: {state.count}
      &lt;button onClick={<span class="hljs-function">() =&gt;</span> dispatch({ <span class="hljs-keyword">type</span>: <span class="hljs-string">'decrement'</span>, payload: <span class="hljs-string">'5'</span> })}&gt;
        -
      &lt;/button&gt;
      &lt;button onClick={<span class="hljs-function">() =&gt;</span> dispatch({ <span class="hljs-keyword">type</span>: <span class="hljs-string">'increment'</span>, payload: <span class="hljs-number">5</span> })}&gt;
        +
      &lt;/button&gt;
    &lt;/&gt;
  )
}
</code></pre>
<ul>
<li><code>useRef</code> hook :</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { useRef, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-comment">// This is just an example, React recommended you to use Controlled components</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Component</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> inputRef = useRef&lt;HTMLInputElement&gt;(<span class="hljs-literal">null</span>)
  <span class="hljs-keyword">const</span> divRef = useRef&lt;HTMLDivElement&gt;(<span class="hljs-literal">null</span>)
  useEffect(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-keyword">if</span> (!inputRef) {
        <span class="hljs-keyword">throw</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'input not mounted !!!!'</span>)
      }
      inputRef.current.value = <span class="hljs-string">'assign value'</span>
    }, [])
    <span class="hljs-keyword">const</span> onChnageButtonHandler = <span class="hljs-function">() =&gt;</span> {
      divRef.current.innerHTML = <span class="hljs-string">'we change this text here'</span>
    }
  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;input ref={inputRef} /&gt;
      &lt;div ref={divRef}&gt;<span class="hljs-built_in">this</span> is div content&lt;/div&gt;
      &lt;button onClick={onChnageButtonHandler}&gt;change the content&lt;/button&gt;
    &lt;/div&gt;
  )
}
</code></pre>
<h1 id="heading-context"><strong>Context:</strong></h1>
<ul>
<li>with default value:</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { createContext, useState, useContext } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>

<span class="hljs-keyword">type</span> ThemeContextType = <span class="hljs-string">'light'</span> | <span class="hljs-string">'dark'</span>
<span class="hljs-keyword">const</span> ThemeContext = createContext&lt;ThemeContextType&gt;(<span class="hljs-string">'light'</span>)
<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [theme, setTheme] = useState&lt;ThemeContextType&gt;(<span class="hljs-string">'light'</span>)
  <span class="hljs-keyword">return</span> (
    &lt;ThemeContext.Provider value={theme}&gt;
      &lt;MyComponent /&gt;
    &lt;/ThemeContext.Provider&gt;
  )
}
<span class="hljs-keyword">const</span> MyComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> theme = useContext(ThemeContext)
  <span class="hljs-keyword">return</span> &lt;p&gt;The current theme is {theme}.&lt;/p&gt;
}
</code></pre>
<ul>
<li>without default value:</li>
</ul>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { createContext, useState, useContext } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>

<span class="hljs-keyword">type</span> userContextType = { name: <span class="hljs-built_in">string</span>; email: <span class="hljs-built_in">string</span> }
<span class="hljs-keyword">const</span> UserContext = createContext&lt;userContextType&gt;()
<span class="hljs-keyword">const</span> App = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [user, setUser] = useState&lt;userContextType&gt;({
    name: <span class="hljs-string">'name'</span>,
    email: <span class="hljs-string">'email@mail.com'</span>,
  })
  <span class="hljs-keyword">return</span> (
    &lt;ThemeContext.Provider value={...{ user, setUser }}&gt;
      &lt;MyComponent /&gt;
    &lt;/ThemeContext.Provider&gt;
  )
}
<span class="hljs-keyword">const</span> MyComponent = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> { user } = useContext(UserContext)
  <span class="hljs-keyword">return</span> &lt;p&gt;The current theme is {user?.name}.&lt;/p&gt;
}
</code></pre>
<h1 id="heading-conclusion"><strong>Conclusion:</strong></h1>
<p>In conclusion, using TypeScript with React can write safer, more maintainable code, and catch errors earlier in the development process. Additionally, TypeScript’s static typing can help improve developer productivity by providing better tooling and documentation.</p>
]]></content:encoded></item><item><title><![CDATA[TypeScript for React Developers Series: Why You Should Adopt It]]></title><description><![CDATA[TypeScript is a superset of JavaScript that adds static typing, which makes your code readable, maintainable and re-usable.
when you adopt TypeScript to React projects, you don’t need to add additional packages to validate props like prop-types, also...]]></description><link>https://blog.othmanekahtal.me/typescript-for-react-developers-series-why-you-should-adopt-it</link><guid isPermaLink="true">https://blog.othmanekahtal.me/typescript-for-react-developers-series-why-you-should-adopt-it</guid><category><![CDATA[TypeScript]]></category><category><![CDATA[TypeScript Tutorial]]></category><category><![CDATA[React]]></category><category><![CDATA[webdev]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Othmane Kahtal]]></dc:creator><pubDate>Tue, 10 Jan 2023 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1729439612818/b9ea780c-6500-46df-96fc-89e4e7a87a62.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>TypeScript is a superset of JavaScript that adds static typing, which makes your code readable, maintainable and re-usable.</p>
<p>when you adopt TypeScript to React projects, you don’t need to add additional packages to validate props like <code>prop-types</code>, also you can catching types errors in compile-time rather than runtime, and getting IntelliSense to your IDE, which can help you to write code quickly and correctly.</p>
<p>The freedom of JavaScript can be painful when you work in a team, when you don’t write tests for your application, and when the application grows.</p>
<p>Imagine you have a component that accepts <code>position</code> as a prop, this prop will be <code>left</code> or <code>right</code> to work well, with JavaScript you need to add a manual check</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//components/MyComponent.jsx</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params">{ position = <span class="hljs-string">"left"</span> }</span>) </span>{
  <span class="hljs-keyword">if</span> (position === <span class="hljs-string">"left"</span>) {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>left<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
  }
  <span class="hljs-keyword">if</span> (position === <span class="hljs-string">"right"</span>) {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>right<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
  }
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>position will be right or left<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}

<span class="hljs-comment">//App.js</span>
<span class="hljs-keyword">import</span> MyComponent <span class="hljs-keyword">from</span> <span class="hljs-string">"./components/MyComponent"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">MyComponent</span> <span class="hljs-attr">position</span>=<span class="hljs-string">"fddffd"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>you can use the <code>prop-types</code> to validate the props but it is still painful :</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//components/MyComponent.jsx</span>
<span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> PropTypes <span class="hljs-keyword">from</span> <span class="hljs-string">"prop-types"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params">{ position = <span class="hljs-string">"left"</span> }</span>) </span>{
  <span class="hljs-keyword">if</span> (position === <span class="hljs-string">"left"</span>) {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>left<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
  }
  <span class="hljs-keyword">if</span> (position === <span class="hljs-string">"right"</span>) {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>right<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
  }
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>position will be right or left<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}
MyComponent.propTypes = {
  <span class="hljs-attr">position</span>: PropTypes.oneOf([<span class="hljs-string">"right"</span>, <span class="hljs-string">"left"</span>]),
};

<span class="hljs-comment">//App.js</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"App"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">MyComponent</span> <span class="hljs-attr">position</span>=<span class="hljs-string">"fddffd"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>No warning or alert again 🥲:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729439428809/c57b9922-487d-4edb-8148-5ad79f30a5b6.webp" alt class="image--center mx-auto" /></p>
<p>To make sure is props are valid you must open the console to show up the errors and warnings:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729439473646/d99c9235-8f98-41ee-9a04-a5ad22326ecc.webp" alt class="image--center mx-auto" /></p>
<p>But when we use TypeScript you will get alerts and warnings anywhere :</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//components/MyComponent.tsx</span>
interface MyComponentProps {
  <span class="hljs-attr">position</span>: <span class="hljs-string">"right"</span> | <span class="hljs-string">"left"</span>;
}
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">MyComponent</span>(<span class="hljs-params">{ position }: MyComponentProps</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>{position}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MyComponent;
</code></pre>
<p>Your IDE tells you stop you have error 🛑:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729439541359/68bee3f6-ba75-4a5a-a866-2a733fbab531.webp" alt class="image--center mx-auto" /></p>
<p>Also your browser gives you alerts to correct your code</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1729439575044/7f562548-10ce-4c88-9139-d1c9029022b5.webp" alt class="image--center mx-auto" /></p>
<h2 id="heading-conclusion">Conclusion:</h2>
<p>TypeScript is a powerful tool that helps you to become a productive developer and to write clean and readable code, it’s not just a props validator, we can do with it a lot of things like <code>alias</code>, creating solid and re-usable components with <code>generics</code></p>
]]></content:encoded></item></channel></rss>