<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Khue Pham</title>
    <description>The latest articles on Forem by Khue Pham (@khuepm).</description>
    <link>https://forem.com/khuepm</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1078012%2Fab02a1a2-8dd0-4df8-851f-2e23df1c8c38.jpeg</url>
      <title>Forem: Khue Pham</title>
      <link>https://forem.com/khuepm</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/khuepm"/>
    <language>en</language>
    <item>
      <title>Day 5/100: Integrating Payments for App and Web 💳</title>
      <dc:creator>Khue Pham</dc:creator>
      <pubDate>Thu, 17 Jul 2025 15:37:54 +0000</pubDate>
      <link>https://forem.com/khuepm/day-5100-integrating-payments-for-app-and-web-582h</link>
      <guid>https://forem.com/khuepm/day-5100-integrating-payments-for-app-and-web-582h</guid>
      <description>&lt;h3&gt;
  
  
  On Today's Agenda
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; Hitting the Limits of AI Tooling
&lt;/li&gt;
&lt;li&gt; The Core Question: Stripe vs. RevenueCat
&lt;/li&gt;
&lt;li&gt; The Verdict &amp;amp; A Local Twist
&lt;/li&gt;
&lt;li&gt; An Indie Developer's Philosophy in the AI Era
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Hitting the Limits of AI Tooling
&lt;/h3&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wow! A guide to making money with AI in 2025? 🤩&lt;/p&gt;

&lt;p&gt;Not quite. This is a deep dive into payment gateways and how to integrate them into the website I've built with AI so I can actually start selling.&lt;/p&gt;

&lt;p&gt;After getting a relatively complete interface, coded 100% by AI with a ton of prompts, I went through the loop of testing, committing, deploying... over and over. I started to find Bolt AI becoming extremely sluggish, with bugs that I just couldn't fix. It seems it only works well for simple landing pages. The moment I got to the Dashboard screens, Bolt ran into a lot of issues: fixing one thing would break another, fixing that would cause a new page error, fixing the new error would bring back the original bug... So, it's best to just use it to "farm" UIs for quantity during its one-month trial (from the hackathon). The internal logic will probably require Windsurf and Cursor.&lt;/p&gt;

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

&lt;p&gt;On the bright side, reviving old projects with a new interface is something it does in a snap. I had saved some old web designs that I loved but whose sites are now defunct. I found them on the Internet Archive, took screenshots, and had Bolt bring them back to life. Just like that, I had a one-of-a-kind website.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Core Question: Stripe vs. RevenueCat
&lt;/h3&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back to payment gateways. RevenueCat requires using Stripe or a Merchant of Record (MoR) with integrated billing, like Paddle. Paddle is great because it handles VAT and is a solution when you can't register for Stripe with a Vietnamese legal entity. In terms of fees, Stripe is cheaper, but the key requirement is having a legal entity in a supported country like the US or UK.&lt;/p&gt;

&lt;p&gt;This raises the question: if I can get Stripe, why do I even need RevenueCat?&lt;/p&gt;

&lt;p&gt;This is the crucial distinction. I had AI create a comparison table to break it down, but here's the summary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stripe&lt;/strong&gt; solves the problem of &lt;strong&gt;"getting paid"&lt;/strong&gt; on the web. It's the payment processor that handles the transaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RevenueCat (RC)&lt;/strong&gt; solves the problem of &lt;strong&gt;"what has the customer bought, and what should the app unlock?"&lt;/strong&gt; It manages the subscription lifecycle, provides analytics, and syncs purchases across multiple platforms.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, Stripe handles the cash; RevenueCat handles the customer's subscription status and entitlements. It's the management and growth tool that sits on top of your payment processor.&lt;/p&gt;

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




&lt;h3&gt;
  
  
  The Verdict &amp;amp; A Local Twist
&lt;/h3&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your product has (or will have) a mobile app, if you want to A/B test paywalls, or if you simply want to save on maintenance time, RC is definitely worth keeping alongside Stripe.&lt;/p&gt;

&lt;p&gt;The main reason for me is integrating payments on the App itself. Customers will buy through Apple/Google, and RC helps sync that user data, allowing them to &lt;strong&gt;buy once and use anywhere&lt;/strong&gt;. For this reason, I'm willing to pay the extra 1% fee to RC for now and consider cost-saving measures later.&lt;/p&gt;

&lt;p&gt;One more note: if my customers are primarily from Vietnam (&amp;gt;80%), I will redirect them to &lt;strong&gt;9Pay&lt;/strong&gt;. Using a local Vietnamese payment gateway will save quite a bit on fees.&lt;/p&gt;




&lt;h3&gt;
  
  
  An Indie Developer's Philosophy in the AI Era
&lt;/h3&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since the app is in its early development stage, I desperately need users to use it, give feedback, and request new features.&lt;/p&gt;

&lt;p&gt;For indie developers like me, the greatest joy is creating products that have &lt;strong&gt;real users&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So many products get thrown away. The code can be beautiful, the technology can be fancy, you can even register a company in some far-off land to dodge taxes... but the product has... no users. :D&lt;/p&gt;

&lt;p&gt;With the current wave of AI, creating new products has never been so fast—it's like making instant noodles.&lt;/p&gt;

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

</description>
    </item>
    <item>
      <title>Day 3/100: Assembling the Toolkit on a Shoestring Budget 🛠️💰</title>
      <dc:creator>Khue Pham</dc:creator>
      <pubDate>Tue, 08 Jul 2025 11:01:52 +0000</pubDate>
      <link>https://forem.com/khuepm/day-3100-assembling-the-toolkit-on-a-shoestring-budget-3a45</link>
      <guid>https://forem.com/khuepm/day-3100-assembling-the-toolkit-on-a-shoestring-budget-3a45</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/wlh"&gt;World's Largest Hackathon Writing Challenge&lt;/a&gt;: Building with Bolt.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Table of Contents: 100-Day Journey
&lt;/h1&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1/100&lt;/td&gt;
&lt;td&gt;Starting the 100-Day Journey &amp;amp; Introduction&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/khuepm/a-multi-part-series-architecting-a-giant-17-products-enhanced-by-ai-2cgm"&gt;View Post&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2/100&lt;/td&gt;
&lt;td&gt;AI Showdown: Which AI to choose for project analysis?&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/khuepm/day-2100-which-ai-to-choose-for-analyzing-investment-projects-35e7"&gt;View Post&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3/100&lt;/td&gt;
&lt;td&gt;Assembling the Toolkit on a Shoestring Budget&lt;/td&gt;
&lt;td&gt;This post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;99/100&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100/100&lt;/td&gt;
&lt;td&gt;Reflections on the 100-Day Journey&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  On Today's Agenda
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; The Hackathon Opportunity: Why I Jumped In
&lt;/li&gt;
&lt;li&gt; The Ultimate Low-Cost Tech Stack
&lt;/li&gt;
&lt;li&gt; Let the Building Begin! From Image to UI
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  The Hackathon Opportunity
&lt;/h3&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bolt announced its Hackathon two months ago, but I only decided to join near the end. Partly because I used to think AI tools were like v0—not great for building UIs. Plus, I was already happy with my setup of Cursor and Windsurf. A year or two ago, AI code assistants were riddled with bugs, but they've gradually evolved to the point where they can now code the entire frontend.&lt;/p&gt;

&lt;p&gt;However, after trying out Loveable and Bolt, I feel like the options for UI generation have multiplied. The GitHub integration is incredibly fast for pushing code. Just by registering for the Bolt hackathon, I was given &lt;strong&gt;1 million free tokens&lt;/strong&gt; to build with, along with tons of coupons for other tools. Most importantly, it gave me free access to the &lt;strong&gt;RevenueCat&lt;/strong&gt; payment gateway.&lt;/p&gt;

&lt;p&gt;I had also signed up for another gateway called Ruul, which has the advantage of accepting crypto (USDT). However, RevenueCat waives its fees for revenue under $2.5k/month. I decided to use RevenueCat for web payments, Bolt to build the new app, ChatGPT to help shape the idea, and Gemini to choose the app's name and write the Terms &amp;amp; Conditions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lys0v11evp4q37zu6vd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lys0v11evp4q37zu6vd.gif" alt="Let's do this GIF" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Ultimate Low-Cost Tech Stack
&lt;/h3&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the complete stack I've assembled for this project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logo &amp;amp; Banners:&lt;/strong&gt; Canva&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual UI Design:&lt;/strong&gt; Figma&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI Frontend Generation:&lt;/strong&gt; Bolt AI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Domain:&lt;/strong&gt; Namecheap (already purchased, so I didn't use the free one from the Hackathon).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; Supabase&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Server &amp;amp; Deployment:&lt;/strong&gt; Netlify&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN &amp;amp; DNS:&lt;/strong&gt; Cloudflare&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email:&lt;/strong&gt; Google Workspace (I'm lucky to have a grandfathered free Basic plan from 2011, as they no longer offer it. A good alternative is Lark by Bytedance—I've already registered a few accounts just in case they follow Google and drop their free tier ^^!).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App Store Account:&lt;/strong&gt; Apple Developer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll deal with other security considerations later on during the project. Today's task is to register for the Apple Developer account to push the app to the Apple Store. The cost is &lt;strong&gt;$99&lt;/strong&gt;, which came out to about 1.2 million VNĐ according to the developer site notification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Total Projected Cost:&lt;/strong&gt; &lt;strong&gt;$7 for the domain + $99/year for the Apple account.&lt;/strong&gt; Everything else is free.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Let the Building Begin!
&lt;/h3&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alright then, time to build the interface! Bolt's ability to convert an image into a web interface is excellent. You can find a design you like on a site like UI8, take a screenshot, and feed it to Bolt. Then you just ask it to replicate the image for your idea, swapping out this or that section.&lt;/p&gt;

&lt;p&gt;I'm determined to build an MVP with core functions, a smooth end-to-end flow, minimal bugs, and basic security. Therefore, I'm not stressing over the UI being perfect; as long as I think it looks good and isn't buggy, it's a win.&lt;/p&gt;

&lt;p&gt;I repeated these steps for the Login, Signup, Pricing, and Dashboard screens... and in a flash, I had dozens of commits. It's been a while since I've felt this productive.&lt;/p&gt;

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

</description>
      <category>devchallenge</category>
      <category>build24dottech</category>
      <category>wlhchallenge</category>
      <category>bolt</category>
    </item>
    <item>
      <title>Day 2/100 - Which AI to choose for analyzing investment projects?</title>
      <dc:creator>Khue Pham</dc:creator>
      <pubDate>Sat, 05 Jul 2025 13:39:10 +0000</pubDate>
      <link>https://forem.com/khuepm/day-2100-which-ai-to-choose-for-analyzing-investment-projects-35e7</link>
      <guid>https://forem.com/khuepm/day-2100-which-ai-to-choose-for-analyzing-investment-projects-35e7</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/wlh"&gt;World's Largest Hackathon Writing Challenge&lt;/a&gt;: Building with Bolt.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents: Architecting a Giant - 17 Products Enhanced by AI
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;(100-Day Journey)&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day&lt;/th&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Notes / Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1/100&lt;/td&gt;
&lt;td&gt;Starting the 100-Day Journey &amp;amp; Introduction&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/khuepm/a-multi-part-series-architecting-a-giant-17-products-enhanced-by-ai-2cgm"&gt;View&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2/100&lt;/td&gt;
&lt;td&gt;Which AI to choose for analyzing investment projects?&lt;/td&gt;
&lt;td&gt;This page&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3/100&lt;/td&gt;
&lt;td&gt;Assembling the Toolkit on a Shoestring Budget&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/khuepm/day-3100-assembling-the-toolkit-on-a-shoestring-budget-3a45"&gt;View&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;100/100&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h1&gt;
  
  
  🤖 AI Showdown: Gemini vs. ChatGPT vs. Grok for Project Analysis 🚀
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Part 1: The Initial Test - Methodology 🧠
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;(Continued from Day 1...)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;My first question to the AIs was simple: &lt;strong&gt;"What is your methodology?"&lt;/strong&gt; 🤔&lt;/p&gt;

&lt;p&gt;Every AI provides a pretty slick answer, but in my research, Gemini was the one that presented a clear, structured methodology right from the start. Before even touching the project details, Gemini laid out a strategic analysis framework based on &lt;strong&gt;FAST&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;F&lt;/strong&gt;easibility ✅&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;A&lt;/strong&gt;ccelerated cash flow generation 💸&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;S&lt;/strong&gt;ustainability &amp;amp; &lt;strong&gt;T&lt;/strong&gt;hreat (Risk) level 🛡️&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This wasn't something I prompted! It showed me that each AI has its own way of thinking. ChatGPT and Grok were similar, giving a general overview and objectives. ChatGPT did a bit better by listing evaluation criteria, but it wasn't nearly as detailed or systematic as Gemini.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scores for this round:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemini:&lt;/strong&gt; +1 🥇&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ChatGPT:&lt;/strong&gt; +0.5 👍&lt;/li&gt;
&lt;/ul&gt;

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




&lt;h2&gt;
  
  
  Part 2: The Data Wars - Who Has the Best Fuel? ⛽
&lt;/h2&gt;

&lt;p&gt;An AI is only as good as its data, right?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Google (Gemini):&lt;/strong&gt; Has a colossal data advantage. We're talking Google Search, Google Scholar 📚, Google Translate, YouTube, Google Docs... plus the entire internet that's constantly feeding its bots for SEO purposes 😅. This massive, diverse dataset is an advantage rivals can only dream of. 👑&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;OpenAI (ChatGPT):&lt;/strong&gt; Has the first-mover advantage. It has a gigantic user base feeding it endless questions, feedback, and prompts. Plus, it's backed by the financial muscle of Microsoft. 💪&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;xAI (Grok):&lt;/strong&gt; Has access to the real-time, chaotic data firehose of X (formerly Twitter) with its ~650 million users. However, for deep research, this isn't ideal. X is mostly short-form news, memes, and entertainment. The in-depth data just can't compete with the other two giants.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scores for this round:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemini:&lt;/strong&gt; +1.5 🥇&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ChatGPT:&lt;/strong&gt; +1.0 👍&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grok:&lt;/strong&gt; +0.5 🤷&lt;/li&gt;
&lt;/ul&gt;

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




&lt;h2&gt;
  
  
  Part 3: The Powerhouse - Context &amp;amp; Ecosystem 🔌
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How much information can they handle at once?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemini:&lt;/strong&gt; Supports up to a &lt;strong&gt;1 million token&lt;/strong&gt; context window. 🤯 This is massive, allowing it to analyze thousands of pages or an entire complex codebase in one go.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ChatGPT &amp;amp; Grok:&lt;/strong&gt; Limited to 128k tokens.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What about professional integration?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemini:&lt;/strong&gt; Deeply integrated into the Google Workspace ecosystem (Docs, Sheets, etc.), making it highly synchronized. 🔗&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ChatGPT:&lt;/strong&gt; Integrated into the Microsoft ecosystem as Copilot. While the MS ecosystem (Windows, Office) is huge, the integration feels less seamless since OpenAI and Microsoft are separate companies. 🤝&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grok:&lt;/strong&gt; Just has the Tesla/xAI ecosystem. At this point, I'm taking Grok out of the race. 👋&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scores for this round:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemini:&lt;/strong&gt; +1 🥇&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ChatGPT &amp;amp; Grok:&lt;/strong&gt; +0.5 👍&lt;/li&gt;
&lt;/ul&gt;

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




&lt;h2&gt;
  
  
  Part 4: The Real-World Challenge - Project Analysis 📊
&lt;/h2&gt;

&lt;p&gt;This is where it gets interesting.&lt;/p&gt;

&lt;p&gt;ChatGPT was very smart and immediately identified the project I was most passionate about. ❤️&lt;/p&gt;

&lt;p&gt;But Gemini did something incredible that ChatGPT completely missed (unless I specifically asked for it): &lt;strong&gt;it analyzed the legal risks.&lt;/strong&gt; ⚖️ Gemini independently found the project's GitHub repo, analyzed its open-source license, and gave me clear, serious warnings ⚠️ about potential policy violations and abuses. It even gave my projects cool names like "Project Phoenix" (for a project I wanted to revive) and "Navigating the Walled Garden" (for projects with hidden mandatory costs like a $99 Apple Developer fee). I absolutely loved this. ChatGPT just ignored this critical aspect.&lt;/p&gt;

&lt;p&gt;However, when it came to the &lt;em&gt;detailed project analysis&lt;/em&gt; (ignoring legalities), ChatGPT shined. ✨ It did the most thorough research on the technical feasibility. As a solo dev, this is the most crucial factor! I don't know why Gemini overlooked it. If you can't overcome the technical hurdles, the project is dead on arrival. 💀&lt;/p&gt;

&lt;p&gt;Furthermore, on my question about the &lt;strong&gt;fastest path to cash flow&lt;/strong&gt; 💰, ChatGPT provided a much more realistic timeline, considering development, app store reviews, payment gateway holding periods, etc. Gemini's analysis felt a bit superficial here; it would quickly dismiss a project if it hit any obstacle, without providing a deep, comparative breakdown.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  Part 5: The Final Verdict 🏆
&lt;/h2&gt;

&lt;p&gt;So, what's the conclusion?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Gemini is probably the best AI tool for initial, high-level strategic research.&lt;/strong&gt; It sets up a fantastic framework and catches critical things like legal risks that others miss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChatGPT seems to be better for deep, granular analysis, especially on the technical side.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;All AIs need good prompts to deliver great results, but Gemini seems to make the initial process a bit easier and safer.&lt;/p&gt;

&lt;p&gt;But in the end, why choose? 🤔 I'm a grown-up, I don't choose A or B... I choose them all! 😁 The best approach is to use both, compare their outputs, and synthesize the results yourself.&lt;/p&gt;

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

</description>
      <category>build24dottech</category>
      <category>devchallenge</category>
      <category>wlhchallenge</category>
      <category>bolt</category>
    </item>
    <item>
      <title>A Multi-Part Series: Architecting a Giant - 17 Products Enhanced by AI - Day 1</title>
      <dc:creator>Khue Pham</dc:creator>
      <pubDate>Fri, 04 Jul 2025 13:46:00 +0000</pubDate>
      <link>https://forem.com/khuepm/a-multi-part-series-architecting-a-giant-17-products-enhanced-by-ai-2cgm</link>
      <guid>https://forem.com/khuepm/a-multi-part-series-architecting-a-giant-17-products-enhanced-by-ai-2cgm</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/wlh"&gt;World's Largest Hackathon Writing Challenge&lt;/a&gt;: Building with Bolt.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here you go, my friend. Let me retell you the story of my "AI bake-off," just like we're chilling and chatting over a beer.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Day 1 - First Contact: The Initial AI Test / Picking AI (ChatGPT, Gemini, Grok) for a Deep Dive?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;So, here's the deal. My brain's been firing off ideas like a broken firework launcher lately. We're talking everything from Open-source projects, Chrome Extensions, Games, Web3, E-commerce, to Health apps, apps for kids... I've even thought about resurrecting old dead projects, making social media videos, or just straight-up cloning extensions and data. I did a quick count, and we're talking about 17 projects with a clear concept. For most of them, I've just dipped my toes in, you know, a little Proof of Concept (PoC) to check the technical feasibility.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;So, What's the Mission?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Since my brain is basically a messy browser with too many tabs open, I asked the AIs to pick a starter for me. The main rule? It had to be something that could make money ASAP. Cold, hard cash. The more the merrier (though let's be real, any amount is a good start). Of course, "generating cash flow" is a vague beast and depends on a million things, but I just threw that general request out there to see how the AIs would handle it.&lt;/p&gt;

&lt;p&gt;My AI squad is all top-tier stuff: Gemini 2.5 Pro, Grok 3 Pro, and ChatGPT o3-pro. To analyze the feasibility, I made them all use their strongest "Deep Research" features: For Gemini, it was "Deep research" on the Gemini 2.5 Pro model; for ChatGPT, it was o3-pro (the "best at reasoning" one) combined with its Research feature; and for Grok, it was "Deeper Research" with Grok3. To keep things fair, I fed them the exact same prompts in two different ways: first, a series of smaller prompts adding more details each time, and second, a version where I crammed all the questions into a single, massive prompt.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Round 1: The Face-Off&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Kicking things off with Gemini, the Google guy, it shot back with a whole research strategy for me to confirm. Meanwhile, ChatGPT wanted more deets, asking a bunch of follow-up questions. And Grok from xAI? Just jumped straight into the analysis. Ballsy.&lt;/p&gt;

&lt;p&gt;I gotta say, I liked ChatGPT's approach. It asked some really legit questions, hitting angles I hadn't even thought of. Honestly, this guy made me create new prompts just to feed the other two :)) By the end, even when I combined everything into one monster prompt, it &lt;em&gt;still&lt;/em&gt; managed to find more questions to ask @.@ Anyway, I'll admit, this thorough approach is solid. &lt;strong&gt;Score: ChatGPT (C): 1, Gemini (Ge): 0, Grok (Gr): 0.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;But... and it's a big but... OpenAI's golden boy has this super annoying bug where its 'thinking' process just... hangs. Forever. I'd heard a buddy complain about this before, so I wasn't shocked, but man, is it irritating. I had to F5 the page to see the results, which made me wonder: if I hadn't refreshed, would it still be 'thinking' right now? The UI not updating when the job is done is just awful! But hey, all I care about is the result, so I'll give the other two a little bonus point for not giving me a headache. &lt;strong&gt;Score: ChatGPT (C): 1, Gemini (Ge): 0.5, Grok (Gr): 0.5.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;While the other two dudes were cruising along, showing their progress as they thought, ChatGPT was noticeably slower. &lt;em&gt;What gives?&lt;/em&gt; Out of curiosity, I peeked into its thinking process and saw it was totally different. To analyze a project's real-world potential, ChatGPT spent ages searching and analyzing YouTube videos for a project I'd already shelved, then compared it to other similar projects to scrape for info.&lt;/p&gt;

&lt;p&gt;Obviously, this deep-dive method paid off in terms of application analysis—the results were super detailed and spot-on. I guess there's a tradeoff here: more research time for more detailed results. I really want to give ChatGPT points for this, but if it goes down the wrong rabbit hole, all that detail is useless, right? I'll put a pin in that for now and see how other factors play out.&lt;/p&gt;

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

</description>
      <category>build24dottech</category>
      <category>devchallenge</category>
      <category>wlhchallenge</category>
      <category>bolt</category>
    </item>
    <item>
      <title>React-Native + Web + Firebase (Part 02) — Implement React Native Firebase cross-app</title>
      <dc:creator>Khue Pham</dc:creator>
      <pubDate>Fri, 20 Jun 2025 18:38:12 +0000</pubDate>
      <link>https://forem.com/khuepm/react-native-web-firebase-part-02-implement-react-native-firebase-cross-app-2ike</link>
      <guid>https://forem.com/khuepm/react-native-web-firebase-part-02-implement-react-native-firebase-cross-app-2ike</guid>
      <description>&lt;h2&gt;
  
  
  Project Structure Overview and Login Flow
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;In &lt;a href="https://dev.to/khuepm/react-native-web-firebase-part-01-why-we-use-react-native-to-develop-both-native-and-web-fdg"&gt;part one&lt;/a&gt;, we gained an overview of the directory structure and the packages we will install for the project. Now, we’ll discuss the login flow, which will be implemented as follows:&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;h2&gt;
  
  
  Login Flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Opening the App:&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;If the user is not logged in:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Display the &lt;strong&gt;Login Screen&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The user taps “Login with Apple” to initiate the login process on their iPhone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Searching for the User based on the account:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the user does not exist:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a new user and proceed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the user exists and is valid:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Retrieve the valid user information and proceed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the user exists but is invalid:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Display an error.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Returning to the App:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store the login information in &lt;strong&gt;Context&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the user is valid, navigate to the &lt;strong&gt;Home Screen&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If the user is invalid, navigate to the &lt;strong&gt;Error Screen&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. When the User is already logged in:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Open the app and check for the latest user data.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the user does not exist:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create a new user and proceed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the user exists and is valid:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Retrieve the valid user information and proceed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the user exists but is invalid:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Display an error&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;App Structure:&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The app can have multiple &lt;strong&gt;Context Providers&lt;/strong&gt; serving different purposes, such as theme settings or authentication information.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An index file for the providers needs to be created to manage them.&lt;/p&gt;

&lt;p&gt;export function AppContextProvider({ children }: { children: React.ReactNode }): JSX.Element {&lt;/p&gt;

&lt;p&gt;return (&lt;br&gt;
   &lt;br&gt;
    &lt;br&gt;
     {children}&lt;br&gt;
    &lt;br&gt;
   &lt;br&gt;
  )&lt;br&gt;
}&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AppConfigProvider&lt;/strong&gt; is an example of other contexts, which we won’t discuss here.&lt;/p&gt;

&lt;p&gt;Now, let’s create the &lt;strong&gt;AuthProvider.tsx&lt;/strong&gt; file with the following content:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// import files
...
type LoginAction = {
  type: 'LOGIN',
  user: User
}
type LogoutAction = {
  type: 'LOGOUT'
}
type ChangeAction = {
  type: 'CHANGE',
  user: User
}
type OnUserChangedAction = {
  type: 'OnUserChangedAction',
  user: User
}
type AuthDispatchAction = LoginAction | LogoutAction | ChangeAction | OnUserChangedAction

export type AuthContextState = User | null
export const AuthContext = createContext&amp;lt;AuthContextState&amp;gt;(null);
export const AuthDispatchContext = createContext&amp;lt;Dispatch&amp;lt;AuthDispatchAction&amp;gt; | null&amp;gt;(null)
export function authReducers(preAuthState: AuthContextState, action: AuthDispatchAction): AuthContextState {
  switch (action.type) {
    case 'LOGIN':
    case 'CHANGE':
      return action.user
    case 'OnUserChangedAction':
      return action.user;
    case 'LOGOUT':
      return null
    default:
      throw Error('Unknown action: ' + JSON.stringify(action));
  }
}

export function AuthProvider({ children }: PropsWithChildren&amp;lt;any&amp;gt;) {
  const [initializing, setInitializing] = useState(true);
  const [authState, authDispatch] = useReducer&amp;lt;Reducer&amp;lt;AuthContextState, AuthDispatchAction&amp;gt;&amp;gt;(authReducer, defaultUserState,)
  const handleAuthStateChanged = useCallback(async (user: FirebaseAuthUser | null) =&amp;gt; {
    if (!user) {
      authDispatch({ type: 'LOGOUT' })
    }
    else {
      const authUser = await getAuthenticatedUser(user.uid);
      if (authUser)
        authDispatch({ type: 'CHANGE', user: authUser, });
    }
    if (initializing) {
      setInitializing(false);
    }
  }, [initializing])

  useEffect(() =&amp;gt; {
    // ... 
  }, [])

  return (
    &amp;lt;AuthContext.Provider value={authState}&amp;gt;
      &amp;lt;AuthDispatchContext.Provider value={authDispatch}&amp;gt;
        {!initializing &amp;amp;&amp;amp; children}
      &amp;lt;/AuthDispatchContext.Provider&amp;gt;
    &amp;lt;/AuthContext.Provider&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It’s important to note that in the useEffect, we will call the onAuthStateChanged service to update the state. When the app receives the user information, it will call the reducers to update the context:&lt;/p&gt;

&lt;p&gt;Add the following to the useEffect:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { onAuthStateChanged } from "../services/Firebase/users/userManagement";

//...

useEffect(() =&amp;gt; {
 return onAuthStateChanged(handleAuthStateChanged);
}, [handleAuthStateChanged])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the services/Firebase/users/ directory, we will create three files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;userManagement.ts&lt;/strong&gt;: Contains interfaces to call functions from outside, helping to define types so that other hooks/services can understand them. This is where we declare onAuthStateChanged as mentioned earlier.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;userManagement.native.ts&lt;/strong&gt; and &lt;strong&gt;userManagement.web.ts&lt;/strong&gt;: These files implement onAuthStateChanged for each platform. Since web and app use different libraries, they cannot share the same source code.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s continue:&lt;/p&gt;

&lt;p&gt;First, declare the interface for userManagement in the userManagement.ts file. When the user information is updated, Firebase will call this function, returning a callback with the user information for further processing:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface UserManagement {
  onAuthStateChanged: &amp;lt;T&amp;gt;(callback: (user: T) =&amp;gt; void) =&amp;gt; () =&amp;gt; void;
  // getUserByDevice: () =&amp;gt; Promise&amp;lt;User | undefined&amp;gt;;
  // getUserById: (userId: string) =&amp;gt; Promise&amp;lt;User | undefined&amp;gt;;
  // updateUser: (userId: string, userData: any) =&amp;gt; Promise&amp;lt;void&amp;gt;;
  // subscribeOnUserChanged: (userId: string, callback: (user: User | undefined) =&amp;gt; void) =&amp;gt; () =&amp;gt; void;
  // signOut: () =&amp;gt; Promise&amp;lt;void&amp;gt;;
  // getCurrentUser: () =&amp;gt; Promise&amp;lt;User | undefined&amp;gt;;
  // signInWithCredential: &amp;lt;T, G&amp;gt;(credential: T) =&amp;gt; Promise&amp;lt;G&amp;gt;;
  // signInAnonymously: &amp;lt;T&amp;gt;() =&amp;gt; Promise&amp;lt;T&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, we need to define where we call the function. In the same userManagement.ts file, add the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Platform } from "react-native";

const userManagement = Platform.select({
 web: () =&amp;gt; require('./userManagement.web'),
 default: () =&amp;gt; require('./userManagement.native'),
});

export const {
 onAuthStateChanged,

 // getUserByDevice,
 // getUserById,
 // updateUser,
 // subscribeOnUserChanged,
 // signOut,
 // getCurrentUser,
 // signInWithCredential,
 // signInAnonymously
} = userManagement as UserManagement;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;userManagement.native.ts&lt;/strong&gt; version uses the package @react-native-firebase/auth, while the web version calls the firebase/auth library to invoke the onAuthStateChanged function.&lt;/p&gt;

&lt;p&gt;This function is responsible for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Listening for changes in the user’s authentication state (logging in and out).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;This method returns an unsubscribe function to stop listening to events.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Always ensure you unsubscribe from the listener when it is no longer needed to prevent updates to components that are no longer in use.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;userManagement.native.ts&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;import auth, { FirebaseAuthTypes } from "@react-native-firebase/auth";&lt;/p&gt;

&lt;p&gt;export type FirebaseAuthUser = FirebaseAuthTypes.User;&lt;br&gt;
export function onAuthStateChanged(callback: (user: FirebaseAuthUser | null) =&amp;gt; void): () =&amp;gt; void {&lt;br&gt;
 return auth().onAuthStateChanged(callback);&lt;br&gt;
}&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The native version requires the configuration of the GoogleService-Info.plist file for the Apple app and/or the google-services.json file for the Android app to function properly.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;userManagement.web.ts&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { auth, db } from '../config';
import { User as FirebaseUser } from "firebase/auth";

export type FirebaseAuthUser = FirebaseUser;
export function onAuthStateChanged(
 callback: (user: FirebaseAuthUser | null) =&amp;gt; void
): () =&amp;gt; void {
 return auth.onAuthStateChanged(callback);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Unlike the native version, the web version requires a configuration to call Firebase through the initializeAuth function. Therefore, we need to add a config file to the Firebase service as follows:&lt;/p&gt;

&lt;p&gt;Folder Structure Updated:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
services/
 Firebase/
    config/
     index.ts
     index.native.ts
     index.web.ts
  users/
   index.ts
   userManagement.ts
   userManagement.native.ts
   userManagement.web.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Similar to the &lt;strong&gt;userManagement.ts&lt;/strong&gt; file, the &lt;strong&gt;index.ts&lt;/strong&gt; file for Firebase config only needs to export the functions from both web and native versions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;config/index.ts&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Platform } from "react-native";

const app = Platform.select({
 web: require('./index.web'),
 default: require('./index.native'),
});
export const { auth, firebaseApp } = app;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The files in the config are primarily intended for Firebase on the web; therefore, the content in &lt;strong&gt;index.native.ts&lt;/strong&gt; does not need to return any data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;config/index.native.ts&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let firebaseApp
let db
let auth
let analytics

export { analytics, auth, db, firebaseApp };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;index.web.ts&lt;/strong&gt; file contains the main logic that allows us to access Firebase services on the web. The content of the file is as follows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;config/index.web.ts&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { initializeApp } from 'firebase/app';
import { initializeAuth } from 'firebase/auth';

// Firebase JS SDK work with expo web
// for more information: https://docs.expo.dev/guides/using-firebase/#using-firebase-js-sdk

// Initialize Firebase
const firebaseConfig = {
 apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY,
 authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN,
 // databaseURL: process.env.EXPO_PUBLIC_FIREBASE_DATABASE_URL,
 projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID,
 storageBucket: process.env.EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET,
 messagingSenderId: process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
 appId: process.env.EXPO_PUBLIC_FIREBASE_APP_ID,
 measurementId: process.env.EXPO_PUBLIC_FIREBASE_MEASUREMENT_ID,
};

let firebaseApp: ReturnType&amp;lt;typeof initializeApp&amp;gt;;
let auth: ReturnType&amp;lt;typeof initializeAuth&amp;gt;;

try {
 firebaseApp = initializeApp(firebaseConfig);
 auth = initializeAuth(firebaseApp);
 auth.useDeviceLanguage();
} catch (error) {
 console.error("Error initializing Firestore:", (error as any).message);
 console.error("Error Stack:", (error as any).stack);
 console.error("Error Details:", error);
}

export { auth, firebaseApp };

// To apply the default browser preference instead of explicitly setting it.
// For more information on how to access Firebase in your project,
// see the Firebase documentation: https://firebase.google.com/docs/web/setup#access-firebase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;firebase/app&lt;/strong&gt; and &lt;strong&gt;firebase/auth&lt;/strong&gt; are separate releases of the Firebase JS SDK. TODO: Update from the Firebase blog to provide further explanation for this section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;firebase/app&lt;/strong&gt; and &lt;strong&gt;firebase/auth&lt;/strong&gt; are package dependencies of &lt;strong&gt;@react-native-firebase/app&lt;/strong&gt;. There is no need to install them separately, and they can be called directly.&lt;/p&gt;

&lt;p&gt;If you attempt to manually install these packages, you will encounter the following error:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RNFNFirestoreDocumentModule.getDocument got 4 arguments, expected 2 ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This error occurs due to a version conflict between the Firebase JS SDK and the Firebase JS SDK, which is a dependency of &lt;strong&gt;@react-native-firebase&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;About this snippet:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const firebaseConfig = {
 apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY,
 authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN,
 // databaseURL: process.env.EXPO_PUBLIC_FIREBASE_DATABASE_URL,
 projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID,
 storageBucket: process.env.EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET,
 messagingSenderId: process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
 appId: process.env.EXPO_PUBLIC_FIREBASE_APP_ID,
 measurementId: process.env.EXPO_PUBLIC_FIREBASE_MEASUREMENT_ID,
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Returning to the Firebase Config in the Google Console, create a new app for the web. Firebase will send you the JS SDK configuration.&lt;/p&gt;

&lt;p&gt;Add the configuration to the .env file of your project. In this example, I am using Expo, so the environment variables have the prefix EXPO_PUBLIC_. You can use a library like react-native-dotenv to manage these environment variables.&lt;/p&gt;

&lt;p&gt;Thank you for joining me on this comprehensive article. If you found this tutorial helpful, don’t forget to give this post *&lt;em&gt;50 claps👏 **and **follow *&lt;/em&gt;🚀 if you enjoyed this post and want to see more. Your enthusiasm and support fuel my passion for sharing knowledge in the tech community.&lt;/p&gt;

&lt;p&gt;You can find more such articles on my profile -&amp;gt; &lt;a href="https://medium.com/@khuepm" rel="noopener noreferrer"&gt;https://medium.com/@khuepm&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/khuepm" rel="noopener noreferrer"&gt;https://github.com/khuepm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stay tuned for more in-depth tutorials and insights on Javascript and React Native development.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;And that’s it! Place a console.log() statement in the callback handleAuthStateChanged within the &lt;strong&gt;AuthProvider.tsx&lt;/strong&gt; file to check the returned data. In the next part (if anyone purpose for this), I will return to the Login section. Happy coding! 🦄&lt;br&gt;
 Here the Part 01 of this story: &lt;a href="https://dev.to/khuepm/react-native-web-firebase-part-01-why-we-use-react-native-to-develop-both-native-and-web-fdg"&gt;https://dev.to/khuepm/react-native-web-firebase-part-01-why-we-use-react-native-to-develop-both-native-and-web-fdg&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>reactnative</category>
      <category>react</category>
      <category>firebase</category>
    </item>
    <item>
      <title>React Native + Web + Firebase (Part 01) — Why we use React Native to develop both Native and Web version</title>
      <dc:creator>Khue Pham</dc:creator>
      <pubDate>Fri, 20 Jun 2025 18:36:03 +0000</pubDate>
      <link>https://forem.com/khuepm/react-native-web-firebase-part-01-why-we-use-react-native-to-develop-both-native-and-web-fdg</link>
      <guid>https://forem.com/khuepm/react-native-web-firebase-part-01-why-we-use-react-native-to-develop-both-native-and-web-fdg</guid>
      <description>&lt;h2&gt;
  
  
  Introduction — Part 01
&lt;/h2&gt;

&lt;p&gt;You are building a cross-platform app using &lt;strong&gt;React Native&lt;/strong&gt;, which will target both &lt;strong&gt;mobile&lt;/strong&gt; (iOS and Android) and &lt;strong&gt;web&lt;/strong&gt; platforms. Leveraging &lt;strong&gt;Expo&lt;/strong&gt; provides an easy and efficient way to set up, develop, and deploy your app while React Native Web allows you to render the same React Native components in the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Technologies:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Expo&lt;/strong&gt;: A framework that helps developers build React Native applications quickly, with many built-in tools for managing the app lifecycle, testing, and deployment.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Managed workflow: Expo handles configurations and simplifies setup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quick testing: Easy to test and deploy on devices and simulators.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Universal development: Allows a unified codebase for mobile and web.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. React Native&lt;/strong&gt;: The core framework used for building cross-platform mobile apps with a single JavaScript codebase.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Native performance: It provides access to native UI components.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modular: Huge ecosystem with community-driven libraries.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. React Native Web&lt;/strong&gt;: A library that makes React Native components and APIs work on the web. It allows you to reuse your mobile app’s codebase to build a web version with the same UI and functionality.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Code reusability: Most of your code can be shared between web and mobile.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Easier web deployment: Supports popular web technologies like HTML and CSS, while keeping the React Native architecture.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. React Native Fỉrebase&lt;/strong&gt;: Combining &lt;strong&gt;Firebase&lt;/strong&gt; with &lt;strong&gt;React Native&lt;/strong&gt; allows for fast, cross-platform development with a fully managed backend solution. You get real-time data sync, secure authentication, push notifications, analytics, and more, without having to worry about managing your own server infrastructure. This combination results in faster development, easier scaling, and a smoother user experience on both iOS and Android platforms.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Create a Project on Firebase&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Firestore:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Firebase’s &lt;strong&gt;Real-Time Database&lt;/strong&gt; and &lt;strong&gt;Firestore&lt;/strong&gt; provide fast, real-time data synchronization between your app and the cloud. For React Native apps that need live updates (such as messaging apps or live feeds), this is a huge benefit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Authentication:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Firebase’s &lt;strong&gt;Authentication&lt;/strong&gt; service supports multiple sign-in methods like Google, Facebook, email/password, etc. This makes it easy to implement secure user authentication with minimal effort, which integrates smoothly with a React Native front end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Create an app in &lt;strong&gt;Firebase Settings&lt;/strong&gt; and download the following files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;GoogleService-Info.plist for the Apple app version.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;google-services.json for the Android app version.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this post, we will not dive deep into how to implement React Native Firebase into your app. Please refer to the React Native Firebase documentation for detailed instructions:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rnfirebase.io/" rel="noopener noreferrer"&gt;https://rnfirebase.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create files following the directory structure below:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;App.tsx&lt;br&gt;
.env&lt;br&gt;
context/&lt;br&gt;
  AuthProvider.tsx&lt;br&gt;
hooks/&lt;br&gt;
  useUserCredential.tsx&lt;br&gt;
screens/&lt;br&gt;
  Login.tsx&lt;br&gt;
  Home.tsx&lt;br&gt;
services/&lt;br&gt;
  Firebase/&lt;br&gt;
    users/&lt;br&gt;
      index.ts&lt;br&gt;
      management.ts&lt;br&gt;
      management.native.ts&lt;br&gt;
      management.web.ts&lt;br&gt;
  utils.ts&lt;br&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Explanation for each component in the directory tree:&lt;br&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;App.tsx&lt;/strong&gt;: This is the root component of the app. Here, we will wrap the ContextProvider for the routers, which contain screens like Login, Home, and logic to check if user data exists, loading the corresponding screens accordingly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;AuthProvider.tsx&lt;/strong&gt;: User information is stored in React Context. This file contains the context and reducers to update user information after receiving data from Firebase.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;hooks&lt;/strong&gt;: For user information that needs to be displayed on the UI or used for logic in components, we have a custom hook useUserCredential.ts, which returns this information.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;screens&lt;/strong&gt;: This folder contains the screens for displaying the UI.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Login.tsx&lt;/strong&gt;: The login screen with a single button “Login By Apple.” When the user taps this button, the app will initiate login using the Apple account on the iPhone.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Home.tsx&lt;/strong&gt;: After successful login, the data from Apple is recognized by Firebase Authentication and returned to the app. We will process this data and then display the Home screen.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;services&lt;/strong&gt;: This folder contains the business logic performed in the app, independent of the UI. In addition to displaying data on the native app, we also handle the display on the web. Therefore, there are separate files for each environment:&lt;/p&gt;

&lt;p&gt;Files with .web.ts are for the web version.&lt;/p&gt;

&lt;p&gt;Files with .native.ts are for both iOS and Android versions.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;services/utils.ts&lt;/strong&gt;: A collection of functions for processing data within the services. These are pure functions and are limited to the service layer of the app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thank you for joining me on this comprehensive article. If you found this tutorial helpful, don’t forget to give this post *&lt;em&gt;50 claps👏 **and **follow *&lt;/em&gt;🚀 if you enjoyed this post and want to see more. Your enthusiasm and support fuel my passion for sharing knowledge in the tech community.&lt;/p&gt;

&lt;p&gt;You can find more such articles on my profile -&amp;gt; &lt;a href="https://medium.com/@khuepm" rel="noopener noreferrer"&gt;https://medium.com/@khuepm&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/khuepm" rel="noopener noreferrer"&gt;https://github.com/khuepm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stay tuned for more in-depth tutorials and insights on Javascript and React Native development.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ok, so in the Part Two, we will continue implement the code. Check this out!&lt;br&gt;
 &lt;a href="https://dev.to/khuepm/react-native-web-firebase-part-02-implement-react-native-firebase-cross-app-2ike"&gt;https://dev.to/khuepm/react-native-web-firebase-part-02-implement-react-native-firebase-cross-app-2ike&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>reactnative</category>
      <category>webdev</category>
      <category>firebase</category>
    </item>
    <item>
      <title>Environment File (.env) Checker and Syncer for ReactJS / NodeJS Project</title>
      <dc:creator>Khue Pham</dc:creator>
      <pubDate>Fri, 20 Jun 2025 18:24:20 +0000</pubDate>
      <link>https://forem.com/khuepm/environment-file-env-checker-and-syncer-for-reactjs-nodejs-project-4m5h</link>
      <guid>https://forem.com/khuepm/environment-file-env-checker-and-syncer-for-reactjs-nodejs-project-4m5h</guid>
      <description>&lt;p&gt;This bash script provides functionality to check and sync environment files in your project.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29kr05ha0b1fk1zkna1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F29kr05ha0b1fk1zkna1o.png" alt="Image description" width="202" height="54"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Checks .env files against a template (.env.template)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Identifies missing variables in .env files&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Offers to sync .env files with the template&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Skips syncing content for specified files&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Source code in my Github repository here:&lt;br&gt;
&lt;a href="https://github.com/khuepm/misc/blob/main/check_env_files.sh" rel="noopener noreferrer"&gt;&lt;strong&gt;Check file here: misc/check_env_files.sh at main · khuepm/misc&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Place the script (check_env_files.sh) in your project root directory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure you have a .env.template file with all required environment variables.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make the script executable:&lt;/p&gt;

&lt;p&gt;chmod +x checkenv.sh&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the script:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;./checkenv.sh&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;(optional) Add to &lt;code&gt;package.json&lt;/code&gt; to check every start yarn&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;{&lt;br&gt;
  …&lt;br&gt;
  scripts: {&lt;br&gt;
    "prestart": "sh checkenv.sh",&lt;br&gt;
   }&lt;br&gt;
}&lt;/p&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Checking Environment Files&lt;/strong&gt;:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The script checks all .env* files in the current directory against .env.template.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It displays missing variables in each file.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A summary of errors (if any) is shown for each file.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Syncing Environment Files&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If errors are found, the script offers to sync .env files with .env.template.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you choose to sync (by entering ‘y’), the script will:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Comment out variables not present in the template&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add new variables from the template&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;keep existing variables: preserving the current value and adding a comment with the template value&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The script uses .env.template as the reference. Ensure this file contains all required variables for your project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Note
&lt;/h2&gt;

&lt;p&gt;After syncing, the script cleans the yarn cache. You may want to adjust or remove this step based on your project needs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Comment out yarn cache clean&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How do I created it?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Initial Setup:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

template=".env.template"

# Check if the template file exists
if [ ! -f "$template" ]; then
  echo "Error: $template not found"
  return 1
fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This section sets up the script and defines the template file. It also checks if the template file exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. check_env_files() function:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Use for later step, a flag to notice when we need to sync the .env
errors_found=false

check_env_files() {
  files_not_to_check=(".env.template")
  # Get all .env files in the current directory
  files_to_check=($(find . -maxdepth 1 -type f -name '.env*'))

  # Remove files that are in files_not_to_check
  for file in "${files_not_to_check[@]}"; do
    files_to_check=(${files_to_check[@]/$file})
  done

  # Remove any empty elements
  files_to_check=(${files_to_check[@]})

  # Get terminal height and width
  terminal_height=$(tput lines)
  terminal_width=$(tput cols)

  # Variables to store output and result
  output=""
  result=""

  # Function to update the screen, keep to notice text at the bottom of screen
  update_screen() {
    # Clear the screen
    tput clear

    # Print the output
    echo "$output"

    # Move cursor to the bottom and print the result
    tput cup $((terminal_height - 3)) 0
    printf '%*s\n' "$terminal_width" '' | tr ' ' '-'
    echo "$result"
    printf '%*s\n' "$terminal_width" '' | tr ' ' '-'

    # Move cursor back to the top
    tput cup 0 0
  }

  for file in "${files_to_check[@]}"; do
    if [ -f "$file" ]; then
      output+="\n"
      output+="Checking $file...\n\n"
      error_count=0
      while IFS= read -r line || [[ -n "$line" ]]; do
        key=$(echo "$line" | cut -d'=' -f1)
        if ! grep -q "^$key=" "$file"; then
          output+="\033[31m  💔 Missing in $file: $key\033[0m\n"
          ((error_count++))
          errors_found=true
        fi
        update_screen
      done &amp;lt; "$template"

      # Update final result
      output+="\n\n==========================================\n"

      if [ $error_count -gt 0 ]; then
        result+="\n\033[31m❌ Done checking $file with $error_count error(s)\033[0m\n"
        output+="$result"
      else
        result+="\n\033[32m✅ Done checking $file with no errors\033[0m\n"
        output+="$result"
      fi

      update_screen
      output+="\n"

      # Add a small delay to make the result visible at first check
      # Remove if you want to see the result immediately
      if [ "$file" = "${files_to_check[0]}" ]; then
        sleep 1
      fi

    else
      #output+="\033[33m🟠 Check $file: $file not found, skipping\033[0m\n"
      #result+="\033[33m⚠️ $file not found, skipping\033[0m\n"
      update_screen
    fi

  done

  # Clear the result at the end
  result=""
  update_screen

  # Reset terminal settings
  tput rmcup  # Exit alternate screen buffer
  tput cnorm  # Show cursor
  tput sgr0   # Reset all attributes

  # Clear the screen and move cursor to top-left
  clear
  tput cup 0 0

  # Display final output
  echo "$output"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This function is responsible for checking .env files against the template. It does the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Finds all .env files in the current directory&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Removes files that should not be checked&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sets up a dynamic terminal display&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checks each .env file against the template, highlighting missing keys&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Updates the screen with progress and results&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem when template has an empty line or a comment line, the checker will not work property. So I need to adjust it a little bit:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      ...
      while IFS= read -r line || [[ -n "$line" ]]; do
        # Skip empty lines and comment lines
        if [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]]; then
          continue
        fi
      ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;3. sync_env_files() function:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This function synchronizes .env files with the template. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Finds all .env files to sync&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For each file:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Comments out lines not in the template&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Updates existing keys with new values from the template&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Adds new keys from the template&lt;/p&gt;

&lt;p&gt;sync_env_files() {&lt;br&gt;
  # Get all .env files in the current directory&lt;br&gt;
  files_to_sync=($(find . -maxdepth 1 -type f -name '.env*'))&lt;/p&gt;

&lt;p&gt;# Exclude .env.template from files to sync&lt;br&gt;
  files_to_sync=(${files_to_sync[@]/$template})&lt;/p&gt;

&lt;p&gt;# Remove any empty elements&lt;br&gt;
  files_to_sync=(${files_to_sync[@]})&lt;/p&gt;

&lt;p&gt;# Sync files&lt;br&gt;
  for file in "${files_to_sync[@]}"; do&lt;br&gt;
    if [ -f "$file" ]; then&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # Check if the file is in the list of files not to sync

  echo "Syncing $file with $template..."

  # Comment lines not in template
  while IFS= read -r line || [[ -n "$line" ]]; do
    if [[ $line =~ ^[[:space:]]*# || -z $line ]]; then
      continue  # Skip comments and empty lines
    fi
    key=$(echo "$line" | cut -d'=' -f1)
    if ! grep -q "^$key=" "$template"; then
      sed -i '' "s|^$key=.*|# $line # Not in template|" "$file"
      echo "\033[31m  Commented: $key (not in template)\033[0m"
    fi
  done &amp;lt; "$file"

  while IFS= read -r line || [[ -n "$line" ]]; do
    if [[ $line =~ ^[[:space:]]*# || -z $line ]]; then
      continue  # Skip comments and empty lines
    fi
    key=$(echo "$line" | cut -d'=' -f1)
    value=$(echo "$line" | cut -d'=' -f2-)

    if grep -q "^$key=" "$file"; then
      # Key exists, check if value is different
      current_value=$(grep "^$key=" "$file" | cut -d'=' -f2-)
      if [ "$current_value" != "$value" ]; then
        # Value is different, update it
        sed -i '' "/# $key=.*# Updated from template/d" "$file"
        sed -i '' "s|^$key=.*|# $key=$value # Updated from template\n$key=$current_value|" "$file"
        echo "\033[33m  Updated: $key\033[0m"
      else
        echo "  Skipped: $key (value unchanged)"
      fi
    else
      # Key doesn't exist, add it
      echo "$key=$value # Added from template" &amp;gt;&amp;gt; "$file"
      echo "\033[32m  Added: $key\033[0m"
    fi

  done &amp;lt; "$template"

  echo "Finished syncing $file"
  echo

fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;done&lt;br&gt;
}&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Main execution flow:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Call the function
check_env_files

if [ "$errors_found" = true ]; then
  # Ask user if they want to sync files
  # If yes, call sync_env_files()
    # After sync successfully, do clean the cache
  # If no, cancel the operation
else
  echo "No errors found in .env files. Sync not needed."
fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This section calls the &lt;strong&gt;check_env_files()&lt;/strong&gt; function and then, based on whether errors were found, either prompts the user to sync the files or informs them that no sync is needed.&lt;/p&gt;

&lt;p&gt;Key features of this script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Dynamic terminal display for real-time progress updates&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Color-coded output for easy reading (red for errors, green for success, yellow for warnings)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Careful handling of existing values in .env files during sync&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Option to sync files only if errors are found&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This script is useful for maintaining consistency across multiple .env files in a project, ensuring that all required environment variables are present and up-to-date.&lt;/p&gt;

&lt;h1&gt;
  
  
  ReactJS #ReactNative #.env #.env.template #uptodate-env-file
&lt;/h1&gt;

</description>
      <category>frontend</category>
      <category>webdev</category>
      <category>backend</category>
      <category>fullstack</category>
    </item>
  </channel>
</rss>
