<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: RapidNative</title>
    <description>The latest articles on DEV Community by RapidNative (rapidnative-ai).</description>
    <link>https://dev.to/rapidnative-ai</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.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F14099%2Fdceef4b9-6cec-42d3-8992-e0b83e1fc562.png</url>
      <title>DEV Community: RapidNative</title>
      <link>https://dev.to/rapidnative-ai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rapidnative-ai"/>
    <language>en</language>
    <item>
      <title>How to Build a HIPAA-Compliant App: A Founder's Guide for 2026</title>
      <dc:creator>Angel Rose</dc:creator>
      <pubDate>Wed, 12 Aug 2026 07:09:30 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/how-to-build-a-hipaa-compliant-app-a-founders-guide-for-2026-ha0</link>
      <guid>https://dev.to/rapidnative-ai/how-to-build-a-hipaa-compliant-app-a-founders-guide-for-2026-ha0</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;There is no HIPAA certification.&lt;/strong&gt; No stamp, no badge. It's a continuous state you maintain through contracts, controls, and documentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Six technical pillars carry most of the weight:&lt;/strong&gt; encryption at rest and in transit, unique auth with MFA, server-enforced RBAC, tamper-evident audit logs, automatic session logoff, and a signed BAA with every vendor that touches PHI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing BAAs are the most common root cause of HIPAA breaches.&lt;/strong&gt; They're also the easiest thing for an auditor to check.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can use LLMs with PHI in 2026&lt;/strong&gt;, but only with providers that offer a BAA, on the right plan, with identifiers redacted before the call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The $60k-$300k agency number is real but not the only number.&lt;/strong&gt; A React Native and Supabase stack can hit a compliant beta in eight to twelve weeks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every healthcare founder eventually hits the same wall. You have a real idea, sometimes even paying pilot customers, and then a hospital CTO or a payer's legal team asks the question that stops the conversation: "Is your app HIPAA-compliant?"&lt;/p&gt;

&lt;p&gt;Suddenly the six-week MVP plan collides with a body of law from 1996, agencies quoting $150,000 to $300,000 for a "compliant build," and a vendor stack full of tools you love that quietly cannot legally touch patient data.&lt;/p&gt;

&lt;p&gt;Here's how you'd actually do it in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "HIPAA-compliant" actually means
&lt;/h2&gt;

&lt;p&gt;HIPAA-compliant app development means every point where Protected Health Information (PHI) is created, stored, transmitted, or accessed is covered by administrative, physical, and technical safeguards defined by the HIPAA Security Rule. And every third-party vendor that touches PHI has signed a Business Associate Agreement (BAA) with you.&lt;/p&gt;

&lt;p&gt;Compliance is not a certification you buy. There is no "HIPAA-certified" stamp issued by HHS. What exists is the HIPAA Security Rule, the Privacy Rule, and the Breach Notification Rule, plus the Office for Civil Rights that investigates breaches and complaints. Your job is to demonstrate, with documentation, contracts, and technical controls, that you meet every applicable requirement on the day someone asks.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three rules that shape your architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The Privacy Rule&lt;/strong&gt; governs how PHI can be used and disclosed. It introduces the concept every product designer bumps into first: the &lt;strong&gt;minimum necessary standard&lt;/strong&gt;. Your app collects, displays, and transmits only the PHI required to do its job. If your telehealth app doesn't need a full address to run a video visit, don't ask for it. It also defines patient rights (access, amendment, accounting of disclosures) and every one of those maps to a screen or an endpoint you have to build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Security Rule&lt;/strong&gt; is the one engineers live in. It covers electronic PHI and requires three safeguard categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Administrative:&lt;/strong&gt; risk assessments, workforce training, access management policies, incident response.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Physical:&lt;/strong&gt; facility access controls, workstation security, device and media disposal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technical:&lt;/strong&gt; access controls, audit logs, integrity controls, transmission security. This is the layer your code implements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Breach Notification Rule&lt;/strong&gt; gives you a legal duty to notify affected individuals within 60 days, notify HHS, and in some cases notify media. "Unsecured" is the operative word. Properly encrypted PHI that gets stolen is generally treated as a much lower-risk event, which is the strongest practical argument that encryption is not optional.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 6 technical requirements that actually matter
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Encryption at rest and in transit.&lt;/strong&gt; AES-256 server-side and on device. TLS 1.2+ with modern ciphers in flight. Hardware-backed key storage for anything the client persists.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unique user authentication with MFA.&lt;/strong&gt; Unique identifier per user, no shared logins, MFA for anyone accessing PHI. Biometrics count as a second factor on the client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Role-based access control.&lt;/strong&gt; Patient sees their own record. Nurse sees their unit. Billing clerk sees charge codes but not clinical notes. Enforced on the server, never only in the UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tamper-evident audit logging.&lt;/strong&gt; Every read and write of PHI logged with who, what, when, and from where. Logs protected from modification, retained six years.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automatic logoff and session controls.&lt;/strong&gt; Inactive sessions terminate. On mobile this means a background timer that clears the session and forces re-auth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signed BAAs with every vendor that touches PHI.&lt;/strong&gt; The one most first-time healthcare founders miss.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On mobile, requirement 1 mostly comes down to not persisting PHI in the wrong place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// DON'T: AsyncStorage is plaintext on disk.&lt;/span&gt;
&lt;span class="c1"&gt;// On Android it's SharedPreferences XML, readable with adb.&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;AsyncStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;patient_mrn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mrn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// DO: hardware-backed keychain/keystore, device-bound.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;SecureStore&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expo-secure-store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;SecureStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItemAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;session_token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;keychainAccessible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SecureStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WHEN_UNLOCKED_THIS_DEVICE_ONLY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;keychainService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;com.yourcompany.health.auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;requireAuthentication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// gates read behind Face ID / biometric&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better still: don't persist PHI on the device at all. Keep it in memory, fetch it per session, and let the server be the only durable store. That turns a lost-phone incident into a non-event.&lt;/p&gt;

&lt;p&gt;Requirement 5 is the one people forget until an auditor asks. A minimal version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sessionTimer.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TIMEOUT_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 15 min inactivity&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;backgroundedAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;AppState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;background&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;backgroundedAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;backgroundedAt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;backgroundedAt&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;TIMEOUT_MS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;clearSession&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;       &lt;span class="c1"&gt;// wipe in-memory PHI&lt;/span&gt;
      &lt;span class="nf"&gt;navigateToLogin&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;    &lt;span class="c1"&gt;// force re-authentication&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;backgroundedAt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pair that with a foreground inactivity timer reset on user interaction, and log both the timeout and the re-auth to your audit trail.&lt;/p&gt;

&lt;h2&gt;
  
  
  The vendor stack that will actually sign a BAA
&lt;/h2&gt;

&lt;p&gt;This is where most guides fail founders. They say "sign a BAA with your vendors" without saying which vendors will sign one, on which plan, at what price.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;HIPAA-eligible option in 2026&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cloud infrastructure&lt;/td&gt;
&lt;td&gt;AWS (BAA free via AWS Artifact, ~150 eligible services), Google Cloud, Azure&lt;/td&gt;
&lt;td&gt;AWS is the most-used HIPAA cloud in 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database + auth + storage&lt;/td&gt;
&lt;td&gt;Supabase Team ($599/mo) + HIPAA add-on ($350/mo), or self-host&lt;/td&gt;
&lt;td&gt;Check current Supabase HIPAA docs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend (mobile)&lt;/td&gt;
&lt;td&gt;React Native / Expo, no PHI in the bundle&lt;/td&gt;
&lt;td&gt;Most common mobile framework for HIPAA apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email (transactional)&lt;/td&gt;
&lt;td&gt;AWS SES (BAA available), Paubox, LuxSci&lt;/td&gt;
&lt;td&gt;Not SendGrid on standard plans&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMS / voice&lt;/td&gt;
&lt;td&gt;Twilio (HIPAA-eligible products only)&lt;/td&gt;
&lt;td&gt;Sign a BAA and configure specifically for HIPAA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Push notifications&lt;/td&gt;
&lt;td&gt;OneSignal (HIPAA plan), or AWS SNS&lt;/td&gt;
&lt;td&gt;Never put PHI in the notification body&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error tracking&lt;/td&gt;
&lt;td&gt;Sentry (HIPAA tier), Datadog (HIPAA tier)&lt;/td&gt;
&lt;td&gt;Standard tiers do not qualify&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics&lt;/td&gt;
&lt;td&gt;Server-side only, PHI-free events&lt;/td&gt;
&lt;td&gt;Google Analytics does not sign BAAs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI / LLM&lt;/td&gt;
&lt;td&gt;Anthropic (BAA available), AWS Bedrock, Vertex AI, Azure OpenAI&lt;/td&gt;
&lt;td&gt;See below&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two rules of thumb. If a vendor's marketing site doesn't say "HIPAA eligible" and their sales team can't produce a template BAA within a day, assume they can't. And HIPAA eligibility is almost always plan-specific, so the free tier you prototyped on almost certainly doesn't qualify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can you use LLMs with PHI?
&lt;/h2&gt;

&lt;p&gt;Yes, but only with specific providers, on specific plans, under a signed BAA.&lt;/p&gt;

&lt;p&gt;The major model providers now offer HIPAA-eligible tiers. Anthropic offers BAAs for enterprise customers using Claude. AWS Bedrock, Google Cloud Vertex AI, and Azure OpenAI all extend the underlying platform BAA to the models hosted on them. What you cannot do is ship PHI to a consumer API endpoint with no BAA. That's a breach the moment the request is sent, regardless of what the model does with the data.&lt;/p&gt;

&lt;p&gt;Even under a BAA, minimize what you send:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Redact identifiers server-side BEFORE the model call.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SAFE_FIELDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ageBand&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;conditionCodes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;medications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;toModelPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;patient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;SAFE_FIELDS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;patient&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;patient&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Allow-list, not deny-list. A deny-list silently leaks&lt;/span&gt;
&lt;span class="c1"&gt;// every new field someone adds to the patient record.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Allow-list beats deny-list every time here. A deny-list quietly leaks whatever field a teammate adds next sprint. And log every prompt and response as part of your audit trail. If your AI features are purely non-PHI (educational content, appointment reminders with no diagnosis), you can often keep them on your regular non-BAA stack, as long as you can prove PHI never crosses that boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The build checklist
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Data classification.&lt;/strong&gt; List every data element. Tag each PHI, non-PHI, or de-identified. Remove fields and prefer tokens over raw values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Written risk assessment.&lt;/strong&gt; A Security Rule requirement, not a nice-to-have. Threats, likelihoods, mitigations. Update annually and on material architecture changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick your HIPAA-eligible stack.&lt;/strong&gt; Sign BAAs before a single byte of PHI reaches any vendor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implement the six pillars.&lt;/strong&gt; Encryption, MFA, server-side RBAC, audit logs with six-year retention, session logoff, BAAs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure SDLC.&lt;/strong&gt; Code review, SAST/DAST, dependency pinning, supply chain controls. First thing a serious buyer's security team asks about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Train your workforce.&lt;/strong&gt; Security Awareness Training for anyone who could touch PHI. Keep records: dates, curricula, attendees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policies and incident response plan.&lt;/strong&gt; Breach notification, sanctions, contingency, business continuity. Start from a template, then customize.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third-party pen test before launch.&lt;/strong&gt; Not required by HIPAA, but every hospital and enterprise buyer will ask for a recent report.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy policy and Notice of Privacy Practices.&lt;/strong&gt; User-facing documents, not just legal artifacts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous compliance.&lt;/strong&gt; Quarterly access reviews, monthly log reviews, annual risk reassessment.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What it costs and how long it takes
&lt;/h2&gt;

&lt;p&gt;Every agency guide says $60,000 to $300,000 and six to twelve months. That's real for a bespoke, agency-built telemedicine app with EHR integration. It's not the only number.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Baseline:&lt;/strong&gt; whatever a non-HIPAA build of the same feature set would cost.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add 15-25%&lt;/strong&gt; for compliance-specific engineering: audit logging, RBAC, session management, encryption plumbing, admin console for access reviews.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vendor upcharge:&lt;/strong&gt; roughly $1,000-$3,000/month at MVP scale once you're on HIPAA tiers across the board.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance program:&lt;/strong&gt; policies, risk assessment, training, pen test, BAA legal review. Budget $10,000-$30,000 year one, roughly half that annually after.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Timelines compress the same way. A modern React Native and Supabase HIPAA stack, with the front-end scaffolded in an AI-assisted tool like &lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=how-to-build-hipaa-compliant-app" rel="noopener noreferrer"&gt;RapidNative&lt;/a&gt;, can get you from zero to a working compliant beta in eight to twelve weeks. Worth being precise about the boundary, though: a code generation tool accelerates your front-end and scaffolding, not your compliance program. No AI code tool signs a BAA with you, because it doesn't touch your production PHI. And you should not paste PHI into any prompt during development. Scaffold with realistic-but-synthetic data and wire the real backend in your own environment.&lt;/p&gt;

&lt;p&gt;What no tool can compress is the compliance program itself: writing your risk assessment, executing your BAAs, getting the pen test scheduled. Start those in parallel with engineering, not after.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is React Native suitable for HIPAA-compliant apps?&lt;/strong&gt; Yes. Single codebase to native iOS and Android, access to Keychain and Keystore for hardware-backed encryption, biometric APIs for MFA, same TLS stack as native. HIPAA compliance is a property of your architecture and vendor stack, not your mobile framework.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need a BAA with Apple or Google to publish?&lt;/strong&gt; No. They don't process PHI on your behalf when they distribute your app. You do need to follow their health data policies, and you need BAAs with every cloud, analytics, notification, and backend vendor that actually handles PHI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I use Firebase?&lt;/strong&gt; Partially. Firebase Auth, Cloud Firestore, and Cloud Functions on Blaze are covered under the Google Cloud BAA when configured correctly. Firebase Analytics, Crashlytics on defaults, and Cloud Messaging with PHI payloads are not. Confirm each service against Google Cloud's current list.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens in a breach?&lt;/strong&gt; Notify affected individuals within 60 days, notify HHS (immediately for 500+ people, otherwise annually), and in some cases notify media in the affected state. OCR fines range from $137 to over $2 million per violation depending on culpability. Encrypted PHI that's lost is generally a much lower-risk event.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need HIPAA if only patients use my app?&lt;/strong&gt; If you collect health data directly from consumers and aren't acting on behalf of a covered entity, you may not technically be subject to HIPAA. But you're almost certainly subject to the FTC Health Breach Notification Rule, state laws (California's CMIA, Washington's My Health My Data Act), and user expectations. Most direct-to-consumer health apps build to HIPAA-equivalent standards anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part most teams get wrong
&lt;/h2&gt;

&lt;p&gt;HIPAA has a reputation for being expensive and slow because most teams treat compliance as a phase at the end of the project. It isn't. It's an architectural constraint you build in from day one, and once it's in your foundation the incremental cost is much smaller than the guides suggest.&lt;/p&gt;

&lt;p&gt;Scaffold fast so you get to testable UI in days. Choose a HIPAA-eligible stack from the start. Get BAAs signed while engineers are still building. Write the risk assessment early, not the week before your first customer's security review.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;What's your stack?&lt;/strong&gt; Drop it in the comments and I'll flag which pieces will fail an audit. And if you've shipped a HIPAA app already, I want to know which vendor surprised you most by refusing (or agreeing) to sign a BAA.&lt;/p&gt;

</description>
      <category>hipaa</category>
      <category>reactnative</category>
      <category>healthcare</category>
      <category>startup</category>
    </item>
    <item>
      <title>The State of Mobile AI in 2026 — From Code Generation to Full App Creation</title>
      <dc:creator>Famitha M A</dc:creator>
      <pubDate>Thu, 06 Aug 2026 07:55:07 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/the-state-of-mobile-ai-in-2026-from-code-generation-to-full-app-creation-37ig</link>
      <guid>https://dev.to/rapidnative-ai/the-state-of-mobile-ai-in-2026-from-code-generation-to-full-app-creation-37ig</guid>
      <description>&lt;p&gt;In 2023, AI could complete a line of code. In 2026, AI can build the entire mobile app — screens, backend, auth, camera access, and a signed IPA ready for the App Store.&lt;/p&gt;

&lt;p&gt;That's not marketing copy, it's a specific technical shift, and it happened faster than most of us in mobile expected. This post walks through the four generations of AI coding tools, why mobile lagged the web by three years, and what specifically broke open in 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four generations, briefly
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gen 1 (2021-2023): Autocomplete.&lt;/strong&gt; Copilot. AI completes the next line. Human owns the file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gen 2 (2023-2024): Single-file generation.&lt;/strong&gt; Cursor, Claude Code. AI generates entire files. Human is the integrator.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gen 3 (2024-2025): Web full-stack.&lt;/strong&gt; Lovable, v0, Bolt. Prompt to deployed web app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gen 4 (2026): Mobile end-to-end.&lt;/strong&gt; Prompt to submission-ready native React Native + Expo app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every generation compressed the space between "I have an idea" and "users are running my app." Gen 4 is the first one where a non-engineer can complete that arc entirely for native mobile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why mobile lagged the web
&lt;/h2&gt;

&lt;p&gt;Web AI moved fast because the web is forgiving. Mobile is not. Each of the following cost the ecosystem roughly a year:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two platforms, not one.&lt;/strong&gt; React Native shares ~80% of code across iOS and Android. The other 20% is where the pain lives: permissions, keyboard behavior, safe areas, gestures, notches. A model has to reason about platform divergence explicitly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Native APIs are gated.&lt;/strong&gt; Camera on web is &lt;code&gt;getUserMedia&lt;/code&gt;. Camera on mobile is &lt;code&gt;expo-camera&lt;/code&gt; + permission strings in &lt;code&gt;app.json&lt;/code&gt; + runtime prompts + a config plugin. Any wrong step and it doesn't build.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preview is genuinely hard.&lt;/strong&gt; Web preview is an iframe. Mobile preview needs Metro, a matching JS engine (Hermes), and streaming updates without a full rebuild.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The App Store is a wall.&lt;/strong&gt; Apple's Guideline 4.2 rejects "wrapped websites." Web-first tools with a "mobile export" button hit this and bounced. Gen 4 had to output real native code from day one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fullstack means fullstack.&lt;/strong&gt; Real mobile means auth that survives app suspend/resume, secure token storage in the OS keychain, offline sync, and a data layer that handles being genuinely offline. Gen 3 tools papered over most of this — they targeted the web, which rarely needs it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "full app creation" actually produces in 2026
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Screens&lt;/td&gt;
&lt;td&gt;React Native + navigation, gestures, safe areas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;Redux / Zustand / Context, chosen by complexity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Provisioned Supabase project + schema + RLS + types&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;Keychain-backed session, deep links, sign-in flows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data&lt;/td&gt;
&lt;td&gt;Migrations, seed data, offline-first where relevant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device&lt;/td&gt;
&lt;td&gt;Camera, GPS, push, biometrics with correct config plugins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Preview&lt;/td&gt;
&lt;td&gt;QR-code preview on real iOS and Android devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Export&lt;/td&gt;
&lt;td&gt;Clean React Native + Expo source, runs anywhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ship&lt;/td&gt;
&lt;td&gt;Signed IPA/AAB, submitted to App Store + Play&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every row above is a place a Gen 3 "mobile export" tool fails. Every row is now Gen 4 table stakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four unlocks that made 2026 different
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Frontier models got good at multi-file reasoning.&lt;/strong&gt; The current generation of frontier models can hold an entire codebase in context and reason about change propagation. Prerequisite for full app gen.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Streaming output made iteration feel instant.&lt;/strong&gt; SSE + progressive component mounting turns "AI generation" from a batch job into a conversation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LLM-native tooling replaced ported IDEs.&lt;/strong&gt; Virtual filesystems the AI writes into, sandboxed runners that typecheck and lint before feedback loops back to the model, multi-step LLM pipelines. The model is the CPU; the surrounding system is the OS.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Someone had to actually build the mobile-specific infrastructure.&lt;/strong&gt; This is the moat. Producing an Expo project that installs, builds, previews on a real device, submits to a store, and comes back with a valid receipt is a stack of ten problems requiring ten specialists.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Checklist: is a tool actually Generation 4?
&lt;/h2&gt;

&lt;p&gt;If you're evaluating tools in 2026, ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the output run on a real phone via QR code, or just in a browser iframe?&lt;/li&gt;
&lt;li&gt;Does the tool generate backend + auth in the same prompt as the frontend?&lt;/li&gt;
&lt;li&gt;Can you export the code and run it locally without the vendor?&lt;/li&gt;
&lt;li&gt;Does the tool handle App Store submission or stop at "here's your code"?&lt;/li&gt;
&lt;li&gt;Does the tool support device features — camera, push, GPS, biometrics — out of the box?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most tools calling themselves "mobile AI app builders" in 2026 fail at least two. The ones that pass are the ones defining what AI mobile app development means for the rest of the decade.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for developers
&lt;/h2&gt;

&lt;p&gt;Senior mobile engineers have not been displaced — the ceiling of what you can build solo has moved up, not down. The mechanical work (screen scaffolding, auth boilerplate, Expo config, navigation setup) collapses to a prompt. The architectural work (data modeling, performance profiling, complex state machines, offline sync semantics) is still where craftsmanship compounds.&lt;/p&gt;

&lt;p&gt;The developers who ship the most in 2026 are the ones who treat AI as a code CPU and spend their time on the parts that matter.&lt;/p&gt;




&lt;p&gt;Try it out: &lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=mobile-ai-2026-the-shift-from-code-generation-to-full-app-creation" rel="noopener noreferrer"&gt;rapidnative.com&lt;/a&gt; — describe an app in plain English, get a real React Native + Expo app on your phone in minutes. Free tier is 20 credits, no credit card.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>ai</category>
      <category>mobile</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Build a Pet Care App with React Native (2026)</title>
      <dc:creator>Angel Rose</dc:creator>
      <pubDate>Fri, 31 Jul 2026 09:29:50 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/how-to-build-a-pet-care-app-with-react-native-2026-69i</link>
      <guid>https://dev.to/rapidnative-ai/how-to-build-a-pet-care-app-with-react-native-2026-69i</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pet care is a well-scoped React Native project: bounded MVP, cross-platform users, forgiving early adopters.&lt;/li&gt;
&lt;li&gt;Stack: Expo SDK 52+, TypeScript, Expo Router, Supabase, NativeWind, &lt;code&gt;expo-notifications&lt;/code&gt;, FlashList.&lt;/li&gt;
&lt;li&gt;Store &lt;code&gt;next_due_at&lt;/code&gt; on the vaccination row, not derived — vaccination schedules vary by jurisdiction.&lt;/li&gt;
&lt;li&gt;Store UTC in the DB, render local with &lt;code&gt;date-fns-tz&lt;/code&gt;, and reschedule reminders on &lt;code&gt;AppState.change&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Honest timeline: 4–6 weeks solo, most of it boilerplate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pet care market keeps growing, and mobile apps are eating an increasing share of it — vet booking, vaccination reminders, feeding schedules. If you've been looking for a well-scoped React Native project to ship, a pet care app is genuinely one of the best options: bounded feature set, cross-platform relevance, and a user base that's forgiving of a rough v1 as long as it solves a real problem.&lt;/p&gt;

&lt;p&gt;Here's the practical build guide, from stack decisions through App Store submission.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why React Native fits pet care apps
&lt;/h2&gt;

&lt;p&gt;Pet care apps need cross-platform reach (pet owners split evenly between iOS and Android), rich UI (photo-heavy profiles), and native features like push notifications and camera — the exact sweet spot for React Native with Expo. They also have a naturally scoped MVP: pet profiles, health records, appointments, reminders. You can ship in weeks, not months.&lt;/p&gt;

&lt;h2&gt;
  
  
  The MVP feature set
&lt;/h2&gt;

&lt;p&gt;Ship these five features first. Adding a sixth before nailing edge cases (multi-species schedules, deleted pets, timezone-sensitive reminders) is the fastest way to never launch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pet profile&lt;/strong&gt; — name, species, breed, DOB, weight, photo, microchip ID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Health record&lt;/strong&gt; — vaccinations with next-due dates, medications, allergies&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Appointment tracker&lt;/strong&gt; — vet visits, grooming, boarding with reminders&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Feeding log&lt;/strong&gt; — food type, portion, schedule&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Push notifications&lt;/strong&gt; — vaccinations, medications, appointments&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;React Native&lt;/strong&gt; with &lt;strong&gt;Expo SDK 52+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript&lt;/strong&gt; (non-negotiable for anything you ship)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expo Router&lt;/strong&gt; for file-based routing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase&lt;/strong&gt; for hosted Postgres + auth&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NativeWind&lt;/strong&gt; for Tailwind-style styling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;expo-notifications&lt;/strong&gt; for local push&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FlashList&lt;/strong&gt; (not &lt;code&gt;FlatList&lt;/code&gt;) for photo-heavy timelines&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scaffold it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-expo-app@latest pet-care-app &lt;span class="nt"&gt;--template&lt;/span&gt;
&lt;span class="c"&gt;# choose "Navigation (TypeScript)"&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;pet-care-app
npx expo &lt;span class="nb"&gt;install &lt;/span&gt;expo-notifications expo-image-picker expo-file-system
npm &lt;span class="nb"&gt;install &lt;/span&gt;nativewind zustand @supabase/supabase-js date-fns
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Route structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
  (tabs)/
    _layout.tsx           # Pets | Schedule | Records | Settings
    index.tsx             # pet list
    schedule.tsx
    records.tsx
    settings.tsx
  pet/
    [id].tsx
    [id]/health.tsx
    [id]/appointments.tsx
  appointment/new.tsx
  vaccination/new.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Decide the route tree before writing screens. Nesting everything under &lt;code&gt;(tabs)/pets/[id]/...&lt;/code&gt; will bite you when you need to push a full-screen modal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data model
&lt;/h2&gt;

&lt;p&gt;Four entities cover the MVP:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entity&lt;/th&gt;
&lt;th&gt;Key fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pets&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;id, owner_id, name, species, breed, date_of_birth, weight_kg, photo_url&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;vaccinations&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;id, pet_id, name, administered_at, next_due_at&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;appointments&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;id, pet_id, type, starts_at, location, notes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;feedings&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;id, pet_id, food_name, portion_grams, scheduled_at, given&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things matter more than you'd expect:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Store &lt;code&gt;next_due_at&lt;/code&gt; on the row.&lt;/strong&gt; Rabies is annual in some jurisdictions, triennial in others. Don't hardcode.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store UTC in the DB, render local with &lt;code&gt;date-fns-tz&lt;/code&gt;.&lt;/strong&gt; Get this wrong and a reminder set for 8 AM triggers at 3 AM after a flight.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Push notifications the right way
&lt;/h2&gt;

&lt;p&gt;The single feature that separates useful pet apps from forgettable ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Notifications&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expo-notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scheduleNotificationAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'s &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;vaccination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is due`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Book a vet appointment to stay on schedule.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;petId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;vaccinationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;vaccination&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nextDueAt&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request permissions gracefully — show a "why" screen before the OS prompt.&lt;/li&gt;
&lt;li&gt;Reschedule on &lt;code&gt;AppState.change&lt;/code&gt; when the app foregrounds. Timezone/DST drift is real.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The 4–6 week problem
&lt;/h2&gt;

&lt;p&gt;Honest timeline for a solo developer: 4–6 weeks for a shippable MVP if you're comfortable with React Native. 10–12 weeks if you're learning Expo/TypeScript/Supabase alongside.&lt;/p&gt;

&lt;p&gt;Most of that time is boilerplate — layout, navigation wiring, spacing, empty states, forms. None of it is the interesting part of building a pet care app.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI-first shortcut
&lt;/h2&gt;

&lt;p&gt;Tools like &lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=how-to-build-a-pet-care-app-with-react-native" rel="noopener noreferrer"&gt;RapidNative&lt;/a&gt; generate a working React Native + Expo codebase from a natural-language description. The workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Describe the app: &lt;em&gt;"A pet care app with pet profiles, vaccination tracking, appointment reminders, and a schedule tab across all pets."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Get a working app in a few minutes.&lt;/li&gt;
&lt;li&gt;Scan a QR code, preview on your real phone.&lt;/li&gt;
&lt;li&gt;Click any element to describe changes ("make these cards bigger, add a soft green tint when a task is done").&lt;/li&gt;
&lt;li&gt;Export the full React Native + Expo source, or publish directly to the stores.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Same output — a real React Native app — different path. Worth trying on the boring 80% of the build so you can spend time on the interesting 20%.&lt;/p&gt;

&lt;h2&gt;
  
  
  App Store gotchas specific to pet apps
&lt;/h2&gt;

&lt;p&gt;Three things reviewers catch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Health claims.&lt;/strong&gt; Track vaccinations/meds without positioning as a medical device. Be explicit in the description.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Background location.&lt;/strong&gt; If you add walk tracking, justify the permission concretely: &lt;em&gt;"To log your dog's walk route while your phone is in your pocket."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screenshots.&lt;/strong&gt; Empty-state screenshots underperform dramatically. Show real pets with real data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use &lt;code&gt;eas submit&lt;/code&gt; — it handles both stores and eliminates most rejection loops.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;The features are the easy part. Reliable reminders, offline-first data, and fast photo handling are what separate 5-star pet apps from abandoned ones. Nail those, ship a focused MVP, and let owners tell you what's missing.&lt;/p&gt;

&lt;p&gt;If you've shipped a pet app — or any app where reminders are the core value — how did you handle timezone drift on scheduled notifications? Drop a comment, I'd like to hear what actually held up in production.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>mobile</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Build a News Aggregator App with AI Recommendations (React Native + Expo)</title>
      <dc:creator>Angel Rose</dc:creator>
      <pubDate>Wed, 29 Jul 2026 06:43:33 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/build-a-news-aggregator-app-with-ai-recommendations-react-native-expo-207n</link>
      <guid>https://dev.to/rapidnative-ai/build-a-news-aggregator-app-with-ai-recommendations-react-native-expo-207n</guid>
      <description>&lt;p&gt;The average reader engages with about 4% of the news they scroll past. The remaining 96% is duplication, noise, or algorithmically boosted outrage. That gap is the whole reason a well-built AI news aggregator still has room in the market — and thanks to modern tooling, it's now a weekend project instead of a quarter-long roadmap.&lt;/p&gt;

&lt;p&gt;Here's the architecture that keeps showing up in successful indie news apps, plus a shortcut for shipping one fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four layers you actually need
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Ingestion&lt;/strong&gt; — NewsAPI, RSS, publisher APIs, GDELT.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understanding&lt;/strong&gt; — embeddings, categorization, dedup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ranking&lt;/strong&gt; — hybrid: content + collaborative + recency decay + diversity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation&lt;/strong&gt; — reader mode, saves, offline, push, deep links.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Skip any layer and you have a directory nobody opens. Nail all four and you have a daily habit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference tech stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Client&lt;/td&gt;
&lt;td&gt;React Native + Expo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feed API&lt;/td&gt;
&lt;td&gt;NewsAPI + curated RSS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;Postgres (Supabase)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Embeddings&lt;/td&gt;
&lt;td&gt;OpenAI &lt;code&gt;text-embedding-3-small&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vector search&lt;/td&gt;
&lt;td&gt;pgvector&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Push&lt;/td&gt;
&lt;td&gt;Expo Notifications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offline&lt;/td&gt;
&lt;td&gt;AsyncStorage&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Nothing exotic. The hard part is the product decisions, not the pieces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building it fast
&lt;/h2&gt;

&lt;p&gt;The traditional way: &lt;code&gt;npx create-expo-app&lt;/code&gt;, set up navigation, build 12 screens by hand, wire the API, add state management, style everything, then start on the ranker.&lt;/p&gt;

&lt;p&gt;The 2026 way: describe the app in a prompt to an AI app builder (I've been using &lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=build-news-aggregator-app-ai-recommendations" rel="noopener noreferrer"&gt;RapidNative&lt;/a&gt; for this kind of thing) and get a working Expo project as output — real code, exportable, no lock-in. Then hand-tune the parts that actually matter (the ranker, the sources, the cold-start UX).&lt;/p&gt;

&lt;p&gt;A prompt that produces a strong first version:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build a news aggregator app called Skim. Vertical feed of article cards with hero image, source logo, headline, summary. Top pill row for categories: For You, Top, Tech, Business, Science, Sports. Tap opens a reader-mode view with share, save, and "More like this." Bottom tabs: Feed, Search, Saved, Profile. Dark theme, pull-to-refresh, shimmer loading.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You get a working multi-screen app in about a minute, previewable on your phone via QR.&lt;/p&gt;

&lt;h2&gt;
  
  
  The recommendation engine
&lt;/h2&gt;

&lt;p&gt;Once you have a live feed, add three ranking signals:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Content-based.&lt;/strong&gt; Embed each article. Store a rolling user "taste vector" as the mean of embeddings of articles they read/saved/lingered on. Cosine-similarity a new article against the user vector. Solves cold-start well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaborative.&lt;/strong&gt; Once you have enough interaction data, "users who read X also read Y" is often stronger than pure text similarity — it captures fun, authority, contrarianism, things the text doesn't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recency + diversity re-ranking.&lt;/strong&gt; Multiply scores by &lt;code&gt;exp(-age_hours / half_life)&lt;/code&gt; with a 12–24h half-life. Then, when selecting the top N, penalize each candidate by its similarity to already-selected items. This is what stops the feed from becoming five versions of the same story.&lt;/p&gt;

&lt;p&gt;Rough weights I've seen work: &lt;code&gt;0.4 * content + 0.3 * collab + 0.2 * recency + 0.1 * diversity&lt;/code&gt;. Tune with real data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cold-start UX matters more than ranker quality
&lt;/h2&gt;

&lt;p&gt;The first five swipes decide whether a new user comes back tomorrow. Default to a strong editorial mix (top stories across categories) on session 1–2. Bring personalization online after session 3. Every good news app I've studied does some version of this.&lt;/p&gt;

&lt;h2&gt;
  
  
  The retention features people underinvest in
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Save-for-later with offline caching (people read on the subway)&lt;/li&gt;
&lt;li&gt;"Why am I seeing this?" per-story explainer&lt;/li&gt;
&lt;li&gt;Explicit topic mute/follow controls&lt;/li&gt;
&lt;li&gt;Weekly digest push or email&lt;/li&gt;
&lt;li&gt;Notifications capped at 3–5/day, gated on the user's taste vector&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Copyright, briefly
&lt;/h2&gt;

&lt;p&gt;Show headline + short summary + hero image + link back. Don't cache full article bodies at scale without a license. Reader mode is fine if fetched on-device (Instapaper pattern).&lt;/p&gt;

&lt;h2&gt;
  
  
  The full workflow
&lt;/h2&gt;

&lt;p&gt;If you want to see this in action end-to-end, RapidNative turns the prompt above into a real Expo project you can extend. Free tier is 20 credits, no card. The exported code is standard Expo — you own it.&lt;/p&gt;

&lt;p&gt;Happy to answer questions on the ranker specifics in the comments.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>ai</category>
      <category>mobile</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>A Design System for Push Notifications: 12 Components, 3 States, One Figma File</title>
      <dc:creator>Angel Rose</dc:creator>
      <pubDate>Wed, 29 Jul 2026 06:18:29 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/a-design-system-for-push-notifications-12-components-3-states-one-figma-file-408f</link>
      <guid>https://dev.to/rapidnative-ai/a-design-system-for-push-notifications-12-components-3-states-one-figma-file-408f</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Push is the surface users see 20x a week — it belongs in the design system, not in a marketing sprint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;12 notification archetypes&lt;/strong&gt; cover almost every app: social-mention, social-activity, DM, group-message, transaction-confirm, transaction-update, reminder-user-set, reminder-streak, security-alert, content-drop, system-status, promotional.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3 states get skipped every time:&lt;/strong&gt; grouped, expanded, and the lock-screen/Notification-Center/banner size variants.&lt;/li&gt;
&lt;li&gt;The handoff artifact is a &lt;strong&gt;YAML file per component&lt;/strong&gt; that maps 1:1 to an &lt;code&gt;expo-notifications&lt;/code&gt; payload. If code diverges from the YAML, code review fails.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;weekly 30-minute notification review&lt;/strong&gt; catches copy drift before users do.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Most design systems stop at the app icon. Push notifications live outside the app, on the lock screen, in the notification shade, in the app-switcher preview — and they get designed ad-hoc, by whoever is writing the marketing send that week. Then a user sees seven notifications from your brand and none of them look like they came from the same company.&lt;/p&gt;

&lt;p&gt;Six months ago I built a push-notification design system for a React Native app the team was scaling from 20k to 200k users. Twelve components, three states each, one Figma file, and a handoff spec that maps every component to a specific expo-notifications payload shape. Here's the whole thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Push Notifications Belong in the Design System
&lt;/h2&gt;

&lt;p&gt;Push is the most visible surface of your product for users who aren't in the app. If your in-app UI is polished but your notifications look like they were written by a bot, the notification is what users remember — it's the surface they see 20 times a week, versus the in-app UI they see three times.&lt;/p&gt;

&lt;p&gt;When push isn't in the design system, three things break:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Copy voice drifts.&lt;/strong&gt; Marketing writes chirpy, engineering writes terse, and the same user gets both.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Icon and color usage is inconsistent.&lt;/strong&gt; Some notifications use the brand accent, some use system defaults, some use whatever emoji the sender liked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rich media (images, actions, replies) get added case-by-case,&lt;/strong&gt; with no consistent affordance for users to know what interactions are available.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A design system fixes all three by making the notification a first-class component with reviewable specs, not an afterthought at the end of a marketing sprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 12 Components
&lt;/h2&gt;

&lt;p&gt;After working across four apps, the same twelve notification archetypes keep showing up. Design one component per archetype:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Social-mention&lt;/strong&gt; — someone tagged/mentioned/replied to the user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Social-activity&lt;/strong&gt; — someone the user follows did something.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct-message&lt;/strong&gt; — a 1:1 message from another user.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Group-message&lt;/strong&gt; — a message in a group, showing group name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction-confirm&lt;/strong&gt; — order placed, payment received.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transaction-update&lt;/strong&gt; — delivery moved, ride arrived, booking changed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reminder-user-set&lt;/strong&gt; — the alarm/reminder the user configured.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reminder-streak&lt;/strong&gt; — 'you're one workout from your streak' style.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security-alert&lt;/strong&gt; — new login, fraud check, 2FA code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content-drop&lt;/strong&gt; — new episode, new article from a followed source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System-status&lt;/strong&gt; — app update available, service outage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Promotional&lt;/strong&gt; — offer, discount, event (use sparingly).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each component gets its own frame in Figma with a filled-in example, a spec sheet, and links to the two engineers who own the code path that sends it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three States Designers Forget
&lt;/h2&gt;

&lt;p&gt;Designers default to designing the single-notification-on-lock-screen state. Then production shows three other states that were never designed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Grouped state (iOS + Android).&lt;/strong&gt; When your app sends 3+ notifications in a session, the OS collapses them into a stack. The stack header is 'YourApp' — but if you set &lt;code&gt;threadIdentifier&lt;/code&gt; (iOS) or &lt;code&gt;channelId&lt;/code&gt; + &lt;code&gt;group&lt;/code&gt; (Android), you get semantic grouping like '4 messages from Sarah.' Design what those group headers look like. Design what happens at 15 notifications (do they collapse further?). Design the tap-to-expand animation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expanded state.&lt;/strong&gt; Long-press on iOS or swipe-down on Android reveals the expanded notification, which can show images, quick-reply fields, action buttons. Most apps ship the default (a bigger version of the collapsed view). You can do better: this is prime real estate for a mini-interaction that doesn't require opening the app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lock-screen preview vs Notification Center vs Banner.&lt;/strong&gt; Same content, three different sizing constraints. The banner (top of screen when phone is unlocked) truncates at ~40 chars. The lock-screen preview shows 2-3 lines. Notification Center shows 4-5 lines and stacks. Design the copy for the tightest constraint first, then progressively enhance for the roomier surfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Figma-to-Code Handoff That Actually Survives
&lt;/h2&gt;

&lt;p&gt;The handoff spec I ship looks like this — one file per component, machine-readable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;social-mention&lt;/span&gt;
&lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;user_mentioned_in_comment&lt;/span&gt;
&lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{actor.display_name}}&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;mentioned&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;you"&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{excerpt}}"&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;deep_link&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;app://comments/{{comment_id}}"&lt;/span&gt;
    &lt;span class="na"&gt;actor_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{actor.id}}"&lt;/span&gt;
    &lt;span class="na"&gt;thread_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{parent_id}}"&lt;/span&gt;
&lt;span class="na"&gt;ios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;category_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SOCIAL_MENTION&lt;/span&gt;
  &lt;span class="na"&gt;thread_identifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mentions_{{parent_id}}"&lt;/span&gt;
  &lt;span class="na"&gt;interruption_level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;active&lt;/span&gt;
  &lt;span class="na"&gt;relevance_score&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.7&lt;/span&gt;
&lt;span class="na"&gt;android&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;channel_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;social_mentions&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mentions&lt;/span&gt;
  &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
&lt;span class="na"&gt;copy_rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;excerpt max 60 chars, ellipsize&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;never include reactor emojis in title&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;if actor is verified, prepend U+2713&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The React Native code that sends it maps 1:1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Notifications&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expo-notifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendMentionNotification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;actorName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;actorVerified&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;commentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;parentId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actorVerified&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`\u2713 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actorName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; mentioned you`&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actorName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; mentioned you`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scheduleNotificationAsync&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;excerpt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;categoryIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SOCIAL_MENTION&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;deep_link&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`app://comments/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commentId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;actor_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actorName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;thread_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parentId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The yaml file is the contract. If the code diverges from the yaml, code review fails. If a designer wants to change the notification, they change the yaml, which triggers a code PR. Everyone's working from the same spec, and copy can't drift silently.&lt;/p&gt;

&lt;p&gt;Wiring the channels, categories, and deep-link routing by hand is the part that eats a sprint — the &lt;a href="https://rapidnative.com/?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=push-notification-design-system" rel="noopener noreferrer"&gt;RapidNative&lt;/a&gt; starters ship with the expo-notifications scaffolding and channel config already in place, so you can go straight to writing the specs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Weekly Notification Design Review
&lt;/h2&gt;

&lt;p&gt;Even with a design system, notification copy drifts. Marketing writes a one-off. Engineering hardcodes a fallback. QA never sees it because notifications only fire in production. Six months in, you have 40 slightly-off notifications your team can't remember shipping.&lt;/p&gt;

&lt;p&gt;Run a weekly 30-minute notification design review. The agenda:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Screenshot every notification the app sent in the last 7 days (via the log I mentioned above — instrument every send).&lt;/li&gt;
&lt;li&gt;Compare each against its yaml spec. Flag drift.&lt;/li&gt;
&lt;li&gt;Look at delivery + open rates per component. Retire any component with under 5% open rate for two weeks running.&lt;/li&gt;
&lt;li&gt;Discuss the copy of any new components proposed this week.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thirty minutes, weekly. It's the single highest-leverage design meeting I've ever run — small enough that engineering shows up, focused enough that decisions actually get made, and it catches the drift before users notice.&lt;/p&gt;

&lt;p&gt;When we ship this notification design system in RapidNative starters, teams stop treating push as marketing exhaust and start treating it as a product surface. The difference shows up in retention within a quarter.&lt;/p&gt;




&lt;p&gt;How many of the 12 archetypes does your app actually send — and how many of them were designed on purpose? Drop a comment with the notification you know is drifting.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>expo</category>
      <category>design</category>
      <category>mobile</category>
    </item>
    <item>
      <title>How to Build a Booking App for Service Businesses with AI (2026 Guide)</title>
      <dc:creator>Angel Rose</dc:creator>
      <pubDate>Wed, 29 Jul 2026 05:17:09 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/how-to-build-a-booking-app-for-service-businesses-with-ai-2026-guide-2791</link>
      <guid>https://dev.to/rapidnative-ai/how-to-build-a-booking-app-for-service-businesses-with-ai-2026-guide-2791</guid>
      <description>&lt;p&gt;If you've ever tried to spec out a booking app for a service business — salon, cleaning company, mobile mechanic, personal trainer — you know the "obvious" features hide 40 nasty edge cases. Buffer time. Timezones. Double-booking races. No-show economics. Reminders that don't wake people up at 3 am.&lt;/p&gt;

&lt;p&gt;This post walks through the actual engineering shape of a service-business booking app in 2026, and how AI app builders (&lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=how-to-build-a-booking-app-for-service-businesses" rel="noopener noreferrer"&gt;RapidNative&lt;/a&gt;, in the examples below) collapse the 8–14 week custom build into 1–3 days. The generated output is real React Native + Expo you own and export, not a wrapped webview.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 8 must-have features (skip the rest for v1)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Service catalog (name, duration, price)&lt;/li&gt;
&lt;li&gt;Real-time availability keyed by staff or resource&lt;/li&gt;
&lt;li&gt;Buffer time between appointments&lt;/li&gt;
&lt;li&gt;Deposits or full prepayment (single biggest no-show reducer)&lt;/li&gt;
&lt;li&gt;Push + SMS reminders (24h push, 2h SMS)&lt;/li&gt;
&lt;li&gt;One-tap reschedule / cancel with a policy window&lt;/li&gt;
&lt;li&gt;Customer profile with past appointments&lt;/li&gt;
&lt;li&gt;Staff / admin view for daily calendar and blocking time&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Gift cards, packages, loyalty, in-app chat — all v2. Ship v1 first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five availability checks nobody documents
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;is_bookable(slot_time T, staff S, service_duration D, buffer B)&lt;/code&gt; returns true iff:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;[T, T + D + B]&lt;/code&gt; sits inside &lt;code&gt;S.working_hours&lt;/code&gt; for that day&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;S&lt;/code&gt; has no other appointment overlapping &lt;code&gt;[T − B, T + D + B]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;S&lt;/code&gt; has not personally blocked that window&lt;/li&gt;
&lt;li&gt;Date is not on the business's holiday list&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;T &amp;gt;= now() + min_lead_time&lt;/code&gt; (typically 60m)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Miss any one of these and a customer will discover it for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow (works for any AI builder)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Write a one-page spec.&lt;/strong&gt; Services, staff, hours, buffer, deposit, cancellation window, min-lead-time. This is the real bottleneck — not the code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Paste it as a prompt.&lt;/strong&gt; Example for a hair salon:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build a booking app for "Wave &amp;amp; Co." salon. Services: Cut $45/45m, Cut+Color $150/120m, Blowout $60/45m. Stylists: Alex, Sam, Jamie. $10 Stripe deposit. Push reminder 24h out. My Bookings screen with cancel-if-&amp;gt;24h. Staff view per stylist.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3. Preview on a real phone via QR.&lt;/strong&gt; Never trust a booking flow you haven't tapped through on-device. Calendar UX, tap targets, and keyboard behavior on a phone-number field only make sense on real hardware.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Point-and-edit instead of re-prompting.&lt;/strong&gt; Click an element in the preview and describe the change ("default the date picker to today"). &lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=how-to-build-a-booking-app-for-service-businesses" rel="noopener noreferrer"&gt;RapidNative's point-and-edit mode&lt;/a&gt; preserves everything you already got right, which full regeneration does not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Wire real integrations.&lt;/strong&gt; Stripe &lt;code&gt;PaymentIntents&lt;/code&gt; with &lt;code&gt;capture_method: manual&lt;/code&gt; for deposits held until no-show, plus Expo Push and Twilio (~$0.008/SMS in the US).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Ship it.&lt;/strong&gt; &lt;code&gt;eas build --platform all&lt;/code&gt; — Expo handles the code signing pain and publishes to both stores under your developer account.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traditional dev vs AI-built (2026 numbers)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Time to v1&lt;/th&gt;
&lt;th&gt;Upfront&lt;/th&gt;
&lt;th&gt;You own code&lt;/th&gt;
&lt;th&gt;Your brand on stores&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agency custom build&lt;/td&gt;
&lt;td&gt;8–14 weeks&lt;/td&gt;
&lt;td&gt;$10k–$25k&lt;/td&gt;
&lt;td&gt;Depends on contract&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SaaS (Fresha/Booksy)&lt;/td&gt;
&lt;td&gt;1 day&lt;/td&gt;
&lt;td&gt;$0–$60/mo&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI app builder (&lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=how-to-build-a-booking-app-for-service-businesses" rel="noopener noreferrer"&gt;RapidNative&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;1–3 days&lt;/td&gt;
&lt;td&gt;Credit-based&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Production gotchas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Timezones&lt;/strong&gt;: store every appointment in UTC, render in user's local, never trust the client clock for availability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End-of-day buffer&lt;/strong&gt;: a 5:15pm 45m cut + 10m buffer ends 6:10pm — don't offer that slot if the shop closes at 6.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Race conditions&lt;/strong&gt;: booking creation must be transactional. Lock, verify, insert or fail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reminders&lt;/strong&gt;: never schedule a push for 3 am local. Clamp to &lt;code&gt;max(now+24h, 9am_local_appointment_day)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No-show deposits&lt;/strong&gt;: default deposit to a real amount ($10–$25 consumer, more for high-ticket). Overriding it manually is fine; defaulting to zero is not.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prompt templates for 6 niches
&lt;/h2&gt;

&lt;p&gt;Personal trainer, cleaning company, mobile mechanic, tutor, dog groomer, massage therapist — the full library is in the canonical post. They all share the same skeleton; the details (pricing, buffer, intake fields) are what generate different apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you want to skip straight to it: &lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=how-to-build-a-booking-app-for-service-businesses" rel="noopener noreferrer"&gt;rapidnative.com&lt;/a&gt; — free tier, 20 credits, paste a prompt, get an app on your phone. Full walkthrough with all six prompt templates and the comparison table is on the &lt;a href="https://www.rapidnative.com/blogs/how-to-build-a-booking-app-for-service-businesses" rel="noopener noreferrer"&gt;canonical post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy building.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>ai</category>
      <category>mobile</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>React Native Performance Monitoring: Tools and Techniques for 2026</title>
      <dc:creator>Russel Dsouza</dc:creator>
      <pubDate>Tue, 28 Jul 2026 13:42:21 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/react-native-performance-monitoring-tools-and-techniques-for-2026-dh4</link>
      <guid>https://dev.to/rapidnative-ai/react-native-performance-monitoring-tools-and-techniques-for-2026-dh4</guid>
      <description>&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flipper is gone&lt;/strong&gt; (removed as default in RN 0.76). React Native DevTools is the sanctioned replacement.&lt;/li&gt;
&lt;li&gt;Track &lt;strong&gt;eight metrics&lt;/strong&gt; — the two most under-monitored are &lt;strong&gt;slow frames %&lt;/strong&gt; and &lt;strong&gt;frozen frames %&lt;/strong&gt;, the closest proxy for "feels laggy."&lt;/li&gt;
&lt;li&gt;The pragmatic 2026 production default: &lt;strong&gt;Sentry&lt;/strong&gt; for crashes + performance, optionally layered with Firebase Crashlytics (free redundancy) or Datadog RUM (if you're already on Datadog).&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;New Architecture&lt;/strong&gt; shifts the culprits: TurboModule calls blocking the JS thread, and Fabric commit contention on the UI thread.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The React Native performance monitoring landscape looks nothing like it did two years ago. Flipper is gone as the default debugger. The New Architecture — Fabric, TurboModules, and the bridgeless runtime — is on by default in every fresh app. Hermes is the assumed engine.&lt;/p&gt;

&lt;p&gt;If you're shipping in 2026, you need a stack that reflects those changes: dev-time profilers that speak Perfetto and Hermes, production observability that captures slow frames per screen, and a workflow for turning that data into fixes before your App Store rating quietly drops from 4.6 to 4.1.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "performance" actually means in React Native
&lt;/h2&gt;

&lt;p&gt;React Native has a two-thread execution model, and every performance conversation lives inside it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The JS thread&lt;/strong&gt; — business logic, Redux reducers, &lt;code&gt;Animated&lt;/code&gt; orchestration, most third-party code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The UI (main) thread&lt;/strong&gt; — layout, drawing, gesture recognition, native module execution, &lt;code&gt;useNativeDriver&lt;/code&gt; animations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A janky animation is almost always a UI thread problem. A slow list scroll under load is usually a JS thread problem. A screen that takes three seconds to appear after a tap is likely JS thread — &lt;em&gt;unless&lt;/em&gt; it's blocking on a network call, in which case it's a waterfall issue. A frozen screen after login is often a JSI serialization stall.&lt;/p&gt;

&lt;p&gt;Monitoring means tracking both threads, tying the data to specific user journeys, and being able to answer three questions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is it slow?&lt;/strong&gt; — objective metrics on frame rate, TTI, startup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where is it slow?&lt;/strong&gt; — which screen, component, native module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why is it slow?&lt;/strong&gt; — flame graphs, network waterfalls, memory pressure.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If your setup only answers question one, you don't have monitoring — you have a smoke alarm.&lt;/p&gt;

&lt;h2&gt;
  
  
  The eight metrics that matter in 2026
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;What it measures&lt;/th&gt;
&lt;th&gt;Healthy target&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JS FPS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Frame rate on the JS thread&lt;/td&gt;
&lt;td&gt;60 fps sustained (120 on ProMotion)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;UI FPS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Frame rate on the native main thread&lt;/td&gt;
&lt;td&gt;60 fps sustained (120 on ProMotion)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cold start time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Icon tap to first interactive frame&lt;/td&gt;
&lt;td&gt;&amp;lt; 2.0s on mid-tier Android&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Warm start time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Time to interactive when backgrounded&lt;/td&gt;
&lt;td&gt;&amp;lt; 400ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TTI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Screen mount → first tap-responsive frame&lt;/td&gt;
&lt;td&gt;&amp;lt; 1s per screen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Slow frames %&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Frames rendered in &amp;gt; 16.67ms&lt;/td&gt;
&lt;td&gt;&amp;lt; 5% per session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frozen frames %&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Frames rendered in &amp;gt; 700ms&lt;/td&gt;
&lt;td&gt;&amp;lt; 0.1% per session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JS bundle size&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Compressed bundle on first launch&lt;/td&gt;
&lt;td&gt;&amp;lt; 2MB gzip&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The two most under-monitored metrics are &lt;strong&gt;slow frames %&lt;/strong&gt; and &lt;strong&gt;frozen frames %&lt;/strong&gt; — the closest proxy for the qualitative "laggy." Sentry, Firebase, and Embrace expose them natively; if your stack doesn't, that's the first upgrade to make.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 2026 tooling stack
&lt;/h2&gt;

&lt;p&gt;Monitoring splits into two phases with different tools: &lt;strong&gt;dev-time profiling&lt;/strong&gt; (finding the problem locally) and &lt;strong&gt;production observability&lt;/strong&gt; (knowing it exists at all).&lt;/p&gt;

&lt;h3&gt;
  
  
  Development-time tools
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;React Native DevTools&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;JS profiling, network, component inspector&lt;/td&gt;
&lt;td&gt;Default since RN 0.76. Replaces Flipper.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hermes Sampling Profiler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Deep JS CPU flame graphs&lt;/td&gt;
&lt;td&gt;Export as &lt;code&gt;.cpuprofile&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Perfetto / Systrace&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;System-wide traces across all threads&lt;/td&gt;
&lt;td&gt;Android. Best for bridge/native contention.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Xcode Instruments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;iOS CPU, memory, energy, Core Animation&lt;/td&gt;
&lt;td&gt;Time Profiler + Animation Hitches.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Android Studio Profiler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Java/Kotlin allocations, native memory&lt;/td&gt;
&lt;td&gt;Pair with Perfetto.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;React DevTools Profiler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Render counts, wasted renders&lt;/td&gt;
&lt;td&gt;Ships with RN DevTools.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If a tutorial tells you to install Flipper, it's out of date. Flipper was removed as the default debugger in RN 0.76 and the community plugins are largely unmaintained. React Native DevTools is the replacement, and it's better for JS profiling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Production observability
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Strengths&lt;/th&gt;
&lt;th&gt;Watch out for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Sentry&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Crash reporting, integrated perf traces, native + JS stack merging&lt;/td&gt;
&lt;td&gt;Sampling costs add up at scale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Firebase Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free, Crashlytics integration, custom traces&lt;/td&gt;
&lt;td&gt;Sparse UI, no session replay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Datadog RUM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Backend APM correlation, cohort analysis&lt;/td&gt;
&lt;td&gt;Expensive at high MAU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Embrace&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Session-first, full journey replay&lt;/td&gt;
&lt;td&gt;Smaller ecosystem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Instabug&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Bug reporting + perf in one SDK&lt;/td&gt;
&lt;td&gt;Duplicates Sentry if you have it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;New Relic Mobile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enterprise-grade infra correlation&lt;/td&gt;
&lt;td&gt;Heavier SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pragmatic default for most teams: &lt;strong&gt;Sentry for crashes + performance + source-mapped traces&lt;/strong&gt;, optionally with &lt;strong&gt;Firebase Crashlytics&lt;/strong&gt; as a free redundant crash pipeline, or &lt;strong&gt;Datadog RUM&lt;/strong&gt; if you're already on Datadog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Minimum-viable production setup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Install Sentry with performance monitoring.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx @sentry/wizard@latest &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; reactNative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;tracesSampleRate: 0.2&lt;/code&gt; and enable &lt;code&gt;enableNativeFramesTracking: true&lt;/code&gt; and &lt;code&gt;enableAutoPerformanceTracing: true&lt;/code&gt;. Native frames tracking is what gives you slow/frozen frame percentages per screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Instrument navigation.&lt;/strong&gt; Wrap your app with Sentry's navigation integration so every screen transition becomes a labelled transaction. Without this, traces are anonymous and useless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Upload source maps on every release.&lt;/strong&gt; Untranslated stack traces are worthless. Bake source map upload into CI so every TestFlight or Play build has readable traces waiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Add custom spans around slow operations.&lt;/strong&gt; Any function over 100ms — cache hydration, a DB query, a large image decode — gets a manual span. This turns "screen X is slow sometimes" into "screen X is slow because recipe hydration takes 800ms on cold launch."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Set alerts for regressions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Crash-free session rate below 99.5%&lt;/li&gt;
&lt;li&gt;P75 cold start rising &amp;gt; 20% between releases&lt;/li&gt;
&lt;li&gt;Slow frame % above 8% on any top-10 screen&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A real diagnostic walkthrough: "the feed screen is laggy"
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Confirm in production data
&lt;/h3&gt;

&lt;p&gt;Filter to the screen, look at P75 and P95 of slow frames per session. If P75 is fine and P95 is bad, it's a device-tier issue — likely mid-tier Android under a specific data condition. Note the device and OS distribution of the worst sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Reproduce on the right hardware
&lt;/h3&gt;

&lt;p&gt;Never diagnose Android performance on an iPhone 15 Pro. Grab a Pixel 6a or a mid-tier Samsung and reproduce there. Enable the perf overlay for live JS FPS and UI FPS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Capture a trace
&lt;/h3&gt;

&lt;p&gt;For JS-thread issues: React Native DevTools → Performance tab → record the interaction. Look for long JS tasks (&amp;gt; 50ms bars), high re-render counts, serialization work.&lt;/p&gt;

&lt;p&gt;For UI-thread issues on Android: capture a Perfetto trace. Look for main-thread frames overflowing 16.67ms, and whether the overflow is in &lt;code&gt;Choreographer#doFrame&lt;/code&gt; or in native module calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Isolate the offender
&lt;/h3&gt;

&lt;p&gt;Common culprits, ranked by frequency in 2026 codebases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A &lt;code&gt;FlatList&lt;/code&gt; with heavy &lt;code&gt;renderItem&lt;/code&gt; work&lt;/strong&gt; — no memoization, inline arrows. Fix: memoize the row, stabilize props, add &lt;code&gt;getItemLayout&lt;/code&gt; if heights are known.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A large state slice re-rendering the whole tree&lt;/strong&gt; — a selector returning new object identity every time. Fix: narrow the selector, add shallow comparison.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An image decode blocking the UI thread&lt;/strong&gt; — full-res images in small views. Fix: server-side resize, &lt;code&gt;expo-image&lt;/code&gt; with &lt;code&gt;contentFit&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A synchronous &lt;code&gt;AsyncStorage&lt;/code&gt; migration on cold start&lt;/strong&gt; — reading 5MB before render. Fix: defer via &lt;code&gt;InteractionManager.runAfterInteractions&lt;/code&gt;, or MMKV with lazy hydration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A third-party analytics SDK spawning threads on startup&lt;/strong&gt; — 200–400ms blocks. Fix: initialize lazily after first frame.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 5: Verify in a canary
&lt;/h3&gt;

&lt;p&gt;Ship behind a flag or to a beta cohort. Compare P75 slow-frame % on the screen before and after. If the number doesn't move, the fix isn't the fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Architecture changes what you monitor
&lt;/h2&gt;

&lt;p&gt;Under the old bridge, the biggest sinkholes were JSON serialization between JS and native, visible in traces as huge queues. The New Architecture's synchronous, typed JSI calls mostly remove that class of problem — but introduce two new ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Synchronous TurboModule calls that block the JS thread.&lt;/strong&gt; A native module doing 50ms of I/O now blocks JS directly, where it used to buffer through the async bridge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fabric commit contention on the UI thread.&lt;/strong&gt; Shadow-tree commits are more efficient on average but spike sharply when a large tree diff lands — watch navigation transitions where the whole screen tree re-mounts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modern Sentry SDKs instrument both Fabric commits and TurboModule calls when auto-performance tracing is on. On an older SDK, upgrade — you'll see spans you didn't have before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bundle size and startup — the fixes that compound
&lt;/h2&gt;

&lt;p&gt;Startup time is the metric most correlated with day-1 retention, and the one most engineers stop optimizing after the first release.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Enable Hermes.&lt;/strong&gt; If you're not on it, you're leaving 30–50% of cold-start performance on the table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Precompile Hermes bytecode.&lt;/strong&gt; Ship &lt;code&gt;.hbc&lt;/code&gt;, not &lt;code&gt;.js&lt;/code&gt;. RN does this for release builds — verify it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ship inline requires on Android.&lt;/strong&gt; &lt;code&gt;inlineRequires: true&lt;/code&gt; in Metro defers module evaluation until first use, cutting 200–500ms on large apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Split third-party SDKs.&lt;/strong&gt; Anything not needed in the first second — analytics, remote config, feature flags — initializes after &lt;code&gt;InteractionManager.runAfterInteractions&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure with production RUM, not local.&lt;/strong&gt; Cold start on your dev machine is meaningless. Trust P75 from real devices.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Track JS bundle size release-over-release. A 100KB regression per release compounds fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best free monitoring tool in 2026?&lt;/strong&gt; Firebase Performance + Crashlytics. Automatic startup and HTTP traces, custom traces, native crash reporting, all free up to a generous tier. Limitations: sparse UI, no session replay — most teams add Sentry for deeper diagnostics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is Flipper still usable?&lt;/strong&gt; No — removed as default in RN 0.76, plugins unmaintained. Use React Native DevTools, plus Perfetto (Android) and Xcode Instruments (iOS) for native profiling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I measure startup time in production?&lt;/strong&gt; Sentry's &lt;code&gt;enableAppStartTracking: true&lt;/code&gt;, or Firebase's automatic &lt;code&gt;_app_start&lt;/code&gt; trace. Track &lt;strong&gt;P75 cold start&lt;/strong&gt; — the average hides regressions behind a few fast flagship sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What FPS should an app maintain?&lt;/strong&gt; 60 fps sustained on both threads (120 on ProMotion). Track slow frames (&amp;gt; 16.67ms) as a percentage and aim under 5% per session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need separate monitoring for the New Architecture?&lt;/strong&gt; Same metrics, different culprits — TurboModule calls and Fabric commit contention. Any modern SDK instruments both automatically; just don't run a pre-GA-era SDK.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The dev-time story is React Native DevTools plus platform-native profilers (Perfetto, Instruments). The production story is Sentry as the default, with Firebase Crashlytics as free redundancy or Datadog RUM for existing Datadog teams.&lt;/p&gt;

&lt;p&gt;The bigger shift is philosophical: performance is a metric you monitor continuously, not a project you do once. The teams shipping the smoothest apps in 2026 have slow-frame alerts wired to Slack, source maps on every release, and a P75 startup dashboard someone actually looks at each Monday.&lt;/p&gt;

&lt;p&gt;I write more about how we handle this in &lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=rn-performance-monitoring" rel="noopener noreferrer"&gt;AI-generated React Native apps at RapidNative&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What's your current monitoring stack — and what's the worst "laggy screen" bug you've tracked down? Mine was a 5MB AsyncStorage migration running on every cold start.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>performance</category>
      <category>mobile</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How to Prototype a Mobile App in Hours (Wireframe React Native)</title>
      <dc:creator>Angel Rose</dc:creator>
      <pubDate>Fri, 24 Jul 2026 12:06:54 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/how-to-prototype-a-mobile-app-in-hours-wireframe-react-native-1aei</link>
      <guid>https://dev.to/rapidnative-ai/how-to-prototype-a-mobile-app-in-hours-wireframe-react-native-1aei</guid>
      <description>&lt;h1&gt;
  
  
  How to Prototype a Mobile App in Hours (Wireframe → React Native)
&lt;/h1&gt;

&lt;p&gt;TL;DR:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The wireframe → clickable-prototype → coded-prototype pipeline is now collapsible into a single step&lt;/li&gt;
&lt;li&gt;Four phases: capture wireframe (20-40 min) → generate base app (2-3 min) → iterate visually (1-2 hrs) → install on device via QR (30 sec)&lt;/li&gt;
&lt;li&gt;First testable build: 1-3 hours vs. 2-4 weeks traditional&lt;/li&gt;
&lt;li&gt;Includes an hour-by-hour log of a real 5-screen food-delivery MVP shipped in one afternoon&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The three artifacts most guides conflate
&lt;/h2&gt;

&lt;p&gt;Before we get into the workflow, one clarification that trips up most prototyping conversations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Wireframe&lt;/strong&gt; — static layout. No interactivity.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clickable prototype&lt;/strong&gt; — hotspots wired between static screens (Figma, InVision). Can't run real logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Working prototype&lt;/strong&gt; — real React Native code, real navigation, real state, installable on device via QR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production app&lt;/strong&gt; — the working prototype plus backend, auth, analytics, store listing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most "prototyping" tutorials stop at rung two. The interesting compression is going from rung one straight to rung three.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four-phase workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Capture the wireframe (20-40 min)
&lt;/h3&gt;

&lt;p&gt;Any medium that captures screens + elements + tap behavior works: hand sketch, whiteboard photo, Excalidraw, Figma frame, competitor screenshot, or a written paragraph. Treat the wireframe as disposable — its job is to be input to the generator, not a deliverable.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Generate the base app (2-3 min)
&lt;/h3&gt;

&lt;p&gt;Feed the wireframe + a one-paragraph description into an AI mobile app builder (&lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=mobile-app-prototyping-wireframe-to-working-app" rel="noopener noreferrer"&gt;RapidNative&lt;/a&gt; is what we use — sketch/prompt/screenshot → React Native + Expo → QR preview in one editor, free tier with 20 credits and no card). You get back a live React Native + Expo app with routing, styled screens, and stubbed data. It's about 70% right — good enough to iterate against.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Iterate visually (1-2 hours)
&lt;/h3&gt;

&lt;p&gt;The base app has ~30% wrong. Point-and-edit is the accelerator here: click any element in the live preview, describe the change ("make cards rounder, move price to top-right"), and the underlying code updates. Product teams typically do 30-50 micro-iterations in the first hour.&lt;/p&gt;

&lt;p&gt;Two rules for this phase:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't accept the first version of any screen.&lt;/li&gt;
&lt;li&gt;Don't over-specify visuals before validating flow. Colors are cheap; navigation is expensive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Ship to real hardware (30 sec)
&lt;/h3&gt;

&lt;p&gt;Scan a QR code, install into a preview client on iPhone or Android, and use the app on real hardware. This is where the bugs Figma can't show you actually show up: thumb-tap zones, safe areas, keyboard behavior, dark mode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traditional vs AI-native — the timeline table
&lt;/h2&gt;

&lt;p&gt;For a 5-screen MVP:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Traditional&lt;/th&gt;
&lt;th&gt;AI-native&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Wireframing&lt;/td&gt;
&lt;td&gt;4-8 hours&lt;/td&gt;
&lt;td&gt;20-40 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hi-fi design&lt;/td&gt;
&lt;td&gt;2-5 days&lt;/td&gt;
&lt;td&gt;Skipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clickable prototype&lt;/td&gt;
&lt;td&gt;4-8 hours&lt;/td&gt;
&lt;td&gt;Skipped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coded prototype&lt;/td&gt;
&lt;td&gt;1-3 weeks&lt;/td&gt;
&lt;td&gt;2-3 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iteration cycle&lt;/td&gt;
&lt;td&gt;1-3 days per round&lt;/td&gt;
&lt;td&gt;30 sec to 2 min&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-device install&lt;/td&gt;
&lt;td&gt;4-8 hours (Xcode/TestFlight)&lt;/td&gt;
&lt;td&gt;30 sec (QR)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;First testable build&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2-4 weeks&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1-3 hours&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The compression is mostly the elimination of role handoffs. When one operator drives the whole loop, the "waiting for someone else" hours evaporate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which wireframe inputs convert best
&lt;/h2&gt;

&lt;p&gt;Ranked worst → best in signal-to-noise:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Napkin sketch photo&lt;/strong&gt; — works, but expect nuance loss. Pair with a written description.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whiteboard photo&lt;/strong&gt; — better; whiteboards force larger, more legible drawings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Excalidraw / FigJam&lt;/strong&gt; — excellent. Digital medium produces clean shapes and labels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Figma frame&lt;/strong&gt; — very good. Layout and hierarchy transfer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Written PRD paragraph&lt;/strong&gt; — underrated. A precise 3-4 sentence description often beats a bad sketch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Competitor screenshot&lt;/strong&gt; — highest signal. Already a mobile UI, so layout, typography, and interaction all transfer.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Hour-by-hour: food-delivery MVP, one afternoon
&lt;/h2&gt;

&lt;p&gt;Two-person team, five screens (restaurant list → detail → cart → checkout → order tracking):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0:00-0:30&lt;/strong&gt; — Whiteboard sketch, phone photo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0:30-0:38&lt;/strong&gt; — Upload + one-paragraph description → base app generated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0:38-1:20&lt;/strong&gt; — First iteration pass. Point-and-edit ~40 changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1:20-1:35&lt;/strong&gt; — QR install on both team phones. Two nav bugs surface; fix in 3 min.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1:35-2:20&lt;/strong&gt; — Send QR to three test users, collect feedback in shared doc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2:20-3:15&lt;/strong&gt; — Second iteration pass. Address top-10 UX notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3:15-3:30&lt;/strong&gt; — Publish shareable preview URL, drop in Slack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three-and-a-half hours end-to-end, iOS + Android, on real phones.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to NOT use this workflow
&lt;/h2&gt;

&lt;p&gt;The AI loop isn't universal. Stay traditional when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Novel interaction patterns with no precedent (generators pattern-match; no pattern = no output quality).&lt;/li&gt;
&lt;li&gt;Regulated industries (medical, aviation, financial trading) that need documented decision trails.&lt;/li&gt;
&lt;li&gt;Pixel-perfect exec-review demos with no budget for a "70%-then-iterate" opening state.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the other 90% — CRUD apps, marketplaces, social, dashboards, internal tools — the compressed loop wins on speed, iteration count, and cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;The whole compression comes down to one operator driving one loop: sketch it, generate it, poke at it on a real phone, repeat. Curious how the four phases feel end-to-end for others — drop a comment with what you're prototyping, and I'll answer questions for the next couple of days.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>ai</category>
      <category>mobile</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The Complete Guide to Offline Storage in React Native (2026)</title>
      <dc:creator>Famitha M A</dc:creator>
      <pubDate>Wed, 22 Jul 2026 09:28:13 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/the-complete-guide-to-offline-storage-in-react-native-2026-1n7j</link>
      <guid>https://dev.to/rapidnative-ai/the-complete-guide-to-offline-storage-in-react-native-2026-1n7j</guid>
      <description>&lt;p&gt;Most React Native apps treat the network as an always-on dependency, and it shows the moment a request stalls. Offline storage isn't one library — it's a stack: key-value stores for preferences, secure enclaves for tokens, structured DBs for domain data, and query caches for server state. This post walks through the whole stack and the offline-first pattern that ties it together.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five categories of RN offline storage
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Key-value&lt;/strong&gt; — AsyncStorage (compat) or MMKV (default). MMKV is ~30x faster and supports sync reads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure&lt;/strong&gt; — &lt;code&gt;expo-secure-store&lt;/code&gt; or &lt;code&gt;react-native-keychain&lt;/code&gt;. Backed by iOS Keychain / Android Keystore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured&lt;/strong&gt; — &lt;code&gt;expo-sqlite&lt;/code&gt;, &lt;code&gt;op-sqlite&lt;/code&gt; (JSI, synchronous), WatermelonDB (reactive + sync), Realm (Atlas Device Sync).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File system&lt;/strong&gt; — &lt;code&gt;expo-file-system&lt;/code&gt; for media. Never base64 blobs into KV storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-state cache&lt;/strong&gt; — TanStack Query + &lt;code&gt;persistQueryClient&lt;/code&gt;, backed by MMKV.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Comparison table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Perf&lt;/th&gt;
&lt;th&gt;Encryption&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AsyncStorage&lt;/td&gt;
&lt;td&gt;KV&lt;/td&gt;
&lt;td&gt;1x&lt;/td&gt;
&lt;td&gt;Community fork&lt;/td&gt;
&lt;td&gt;Legacy/compat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MMKV&lt;/td&gt;
&lt;td&gt;KV&lt;/td&gt;
&lt;td&gt;~30x&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;New default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;expo-secure-store&lt;/td&gt;
&lt;td&gt;Secure KV&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;OS keychain&lt;/td&gt;
&lt;td&gt;Tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;op-sqlite&lt;/td&gt;
&lt;td&gt;SQL&lt;/td&gt;
&lt;td&gt;JSI&lt;/td&gt;
&lt;td&gt;SQLCipher&lt;/td&gt;
&lt;td&gt;Structured data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WatermelonDB&lt;/td&gt;
&lt;td&gt;Reactive DB&lt;/td&gt;
&lt;td&gt;Lazy&lt;/td&gt;
&lt;td&gt;SQLCipher&lt;/td&gt;
&lt;td&gt;Large offline lists + sync&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Realm&lt;/td&gt;
&lt;td&gt;Object DB&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Atlas Device Sync&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The offline-first pattern
&lt;/h2&gt;

&lt;p&gt;Four moving parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Local-first reads&lt;/strong&gt; — UI always reads from local store. Network populates store; store renders UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Optimistic writes&lt;/strong&gt; — UI updates immediately, before the server acks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mutation queue&lt;/strong&gt; — Durable list (SQLite or MMKV) of pending server-side changes with client IDs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sync engine&lt;/strong&gt; — Background worker drains the queue with exponential backoff, reconciles conflicts.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;markMessageAsRead&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;readAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="na"&gt;pendingSync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mutationQueue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message.markRead&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;readAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;syncEngine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wake&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conflict resolution
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LWW&lt;/strong&gt; — timestamps, newest wins. Fine for most consumer apps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-authoritative&lt;/strong&gt; — reject stale writes with a version mismatch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CRDTs&lt;/strong&gt; — Yjs, Automerge. Overkill unless you're building a collaborative editor.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Decision cheat sheet
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Prefs / JWT? → MMKV + expo-secure-store&lt;/li&gt;
&lt;li&gt;Instant-open cached lists? → TanStack Query + &lt;code&gt;persistQueryClient&lt;/code&gt; on MMKV&lt;/li&gt;
&lt;li&gt;Thousands of offline records? → op-sqlite (or WatermelonDB for reactive)&lt;/li&gt;
&lt;li&gt;Cross-device sync? → WatermelonDB or Realm&lt;/li&gt;
&lt;li&gt;Media? → expo-file-system + metadata in SQLite&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pitfalls to skip
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Blocking JS thread with async AsyncStorage at startup&lt;/li&gt;
&lt;li&gt;Blobs in KV stores&lt;/li&gt;
&lt;li&gt;No cache-wipe on logout&lt;/li&gt;
&lt;li&gt;Infinite retry loops in the mutation queue&lt;/li&gt;
&lt;li&gt;Persisting Redux state without a schema version&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap-up
&lt;/h2&gt;

&lt;p&gt;Offline-first isn't a defensive posture — it's a performance strategy. Reading from local storage beats a round-trip every time, and a good sync engine hides the network from your users. Pick each layer deliberately and this becomes a solved problem, not a recurring one.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=content&amp;amp;utm_campaign=complete-guide-offline-storage-react-native" rel="noopener noreferrer"&gt;RapidNative&lt;/a&gt;, an AI mobile app builder that generates production-ready React Native code you can extend with any of these libraries. Would love feedback from anyone using MMKV + TanStack Query in production.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>mobile</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>5 Places Sensitive Data Leaks in a React Native App (and How to Plug Them)</title>
      <dc:creator>Russel Dsouza</dc:creator>
      <pubDate>Tue, 21 Jul 2026 07:49:01 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/5-places-sensitive-data-leaks-in-a-react-native-app-and-how-to-plug-them-3gf1</link>
      <guid>https://dev.to/rapidnative-ai/5-places-sensitive-data-leaks-in-a-react-native-app-and-how-to-plug-them-3gf1</guid>
      <description>&lt;ul&gt;
&lt;li&gt;The leaks that matter usually aren't in production code — they're in the workflow around it.&lt;/li&gt;
&lt;li&gt;Five common ones: &lt;strong&gt;AsyncStorage for tokens&lt;/strong&gt;, &lt;strong&gt;real user data in design mockups&lt;/strong&gt;, &lt;strong&gt;multi-stage AI pipelines&lt;/strong&gt;, &lt;strong&gt;logs and crash reports&lt;/strong&gt;, and &lt;strong&gt;bundled permissions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Two greps that find real bugs today: &lt;code&gt;AsyncStorage.setItem&lt;/code&gt; near &lt;code&gt;token&lt;/code&gt;, and &lt;code&gt;console.log(user&lt;/code&gt; / &lt;code&gt;console.log(token&lt;/code&gt; / &lt;code&gt;console.log(prompt&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Model consent by &lt;em&gt;purpose&lt;/em&gt;, not by OS permission. Camera consent is not AI-summarization consent.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Data protection posts usually focus on the shipped binary. Encrypt this, pin that certificate, wrap the API client. All correct, all necessary — and all too late if the leak happened three weeks earlier in a design mockup.&lt;/p&gt;

&lt;p&gt;We build AI-generated React Native apps at RapidNative, and the interesting security bugs almost never live in production code. They live in the space between code, design, and AI workflows. Here are five leak points we've watched teams miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. AsyncStorage for anything sensitive
&lt;/h2&gt;

&lt;p&gt;The classic. &lt;code&gt;AsyncStorage&lt;/code&gt; is convenient, unencrypted key-value storage. A user session token in AsyncStorage is one rooted device or one iCloud backup extraction away from being replayed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; use &lt;code&gt;expo-secure-store&lt;/code&gt; (Keychain on iOS, Keystore on Android) for anything that unlocks an account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;SecureStore&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expo-secure-store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;saveSessionToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;SecureStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItemAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;session_token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your codebase greps positive for &lt;code&gt;AsyncStorage.setItem&lt;/code&gt; and &lt;code&gt;token&lt;/code&gt;, that's your first PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Real user data in design prototypes
&lt;/h2&gt;

&lt;p&gt;A designer opens Figma, needs a realistic list, and pastes 20 rows from the production customer export. Now customer names sit in the design file, which sits in a Figma team folder, which was shared to a contractor's personal email nine months ago.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; a synthetic data script committed to the repo. &lt;code&gt;npx generate-mocks users 20&lt;/code&gt; should be faster than exporting production data. Make the fast path the safe path.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Multi-stage AI workflows
&lt;/h2&gt;

&lt;p&gt;Voice memo lands. Transcription service returns text. A second model extracts tasks. Teammates get pinged.&lt;/p&gt;

&lt;p&gt;That's four handoffs, and the security review probably only covers the API endpoint at step 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; draw the pipeline as boxes, list what data each box sees, and confirm each hop has access controls tied to the user's actual consent — not a bundle. Camera consent is not AI-summarization consent.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Logs and crash reports
&lt;/h2&gt;

&lt;p&gt;Sentry, Bugsnag, Datadog, or whatever log aggregator you use sees everything the app hands it. Session tokens in &lt;code&gt;Authorization&lt;/code&gt; headers. AI prompt bodies with user text. Full user IDs in breadcrumbs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; scrub before you send. Most SDKs support a &lt;code&gt;beforeSend&lt;/code&gt; hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Sentry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;beforeSend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grep your codebase for &lt;code&gt;console.log(user&lt;/code&gt;, &lt;code&gt;console.log(token&lt;/code&gt;, and &lt;code&gt;console.log(prompt&lt;/code&gt;. That's your second PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Bundled permissions
&lt;/h2&gt;

&lt;p&gt;You ask for camera on onboarding: "we need this for document capture." The user agrees. Six months later a new AI-summarization feature ships that also uses camera frames.&lt;/p&gt;

&lt;p&gt;Same permission, different purpose. Legally shaky (GDPR Article 25 asks for data minimization by design), and practically a betrayal of what the user thought they said yes to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; model consent as a first-class type, keyed on purpose rather than on OS permission:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ConsentEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;granted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;withdrawn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;policyVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;purpose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;document-capture&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ai-summary&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;marketing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;onboarding&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;settings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;feature-gate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ship a new feature, ask again for that purpose. A one-line inconvenience for the user beats the €7.1B in GDPR fines the EU has issued as of January 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;Notice what the five have in common: none of them are broken TLS, weak crypto, or a mangled JWT signature. They're all &lt;strong&gt;workflow leaks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Encryption and pinning are table stakes. The bugs that leak your customer's data in 2026 are the ones nobody put in the security review — because they happen in Figma, in a Slack DM, in a Sentry payload, or in an AI pipeline diagram that was never drawn.&lt;/p&gt;

&lt;p&gt;Draw the diagram. Grep the logs. Model consent. Ship.&lt;/p&gt;

&lt;p&gt;Full source with the OWASP and NIST references: &lt;a href="https://rapidnative.com/blogs/data-protection?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=data-protection" rel="noopener noreferrer"&gt;rapidnative.com/blogs/data-protection&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Which of the five would your codebase fail right now? Mine failed #4 the first time I checked.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>security</category>
      <category>mobile</category>
      <category>webdev</category>
    </item>
    <item>
      <title>You Don't Need 10 Refactoring Tools. Pick 2 From This Decision Tree.</title>
      <dc:creator>Russel Dsouza</dc:creator>
      <pubDate>Tue, 21 Jul 2026 07:35:58 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/you-dont-need-10-refactoring-tools-pick-2-from-this-decision-tree-2cho</link>
      <guid>https://dev.to/rapidnative-ai/you-dont-need-10-refactoring-tools-pick-2-from-this-decision-tree-2cho</guid>
      <description>&lt;ul&gt;
&lt;li&gt;Answer three questions — &lt;strong&gt;scope of change&lt;/strong&gt;, &lt;strong&gt;how type-heavy your codebase is&lt;/strong&gt;, and &lt;strong&gt;who runs it&lt;/strong&gt; — and the tool choice collapses to two.&lt;/li&gt;
&lt;li&gt;Solo TS-heavy repo: &lt;strong&gt;VS Code + &lt;code&gt;ts-morph&lt;/code&gt;&lt;/strong&gt;. Small team, one repo: &lt;strong&gt;WebStorm + &lt;code&gt;jscodeshift&lt;/code&gt;&lt;/strong&gt;. Multi-repo org: &lt;strong&gt;&lt;code&gt;ast-grep&lt;/code&gt; + Sourcegraph Batch Changes&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You can skip Babel, Comby, OpenRewrite/Moderne, and ReSharper unless you're in their specific ecosystem.&lt;/li&gt;
&lt;li&gt;Tool sprawl is a symptom: if you're debating 10 tools, the real gap is that nobody's defined what's worth automating.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Every "best refactoring tools" post in 2026 gives you the same 10 names and lets you sort them out.&lt;/p&gt;

&lt;p&gt;But 51% of developers now use AI coding tools daily, and the refactoring-tools market is on track from $1.74B to $5.82B by 2033. That growth means more tools, not clearer choices.&lt;/p&gt;

&lt;p&gt;Here's a smaller frame. Answer three questions, pick two tools, ignore the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 3 questions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Scope of the change?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One file, minutes of work: your IDE&lt;/li&gt;
&lt;li&gt;Same rewrite across many files: a codemod&lt;/li&gt;
&lt;li&gt;Same rewrite across many repos: a batch orchestrator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How type-heavy is your codebase?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loose JS or mixed languages: AST / pattern tools&lt;/li&gt;
&lt;li&gt;Strict TypeScript: type-aware transforms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Who runs it?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solo: CLI is fine&lt;/li&gt;
&lt;li&gt;Team: you need a shared recipe and review flow&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The 2-tool stacks
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Solo dev, TypeScript-heavy React Native repo.&lt;/strong&gt; Pick VS Code (rename, quick fixes, everyday cleanup) and &lt;code&gt;ts-morph&lt;/code&gt; (type-aware programmable rewrites for the times VS Code's rename gives up). Two tools, both free, both scriptable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Small team, one repo, repetitive structural changes.&lt;/strong&gt; Pick WebStorm (symbol-aware preview so nobody merges a broken rename) and &lt;code&gt;jscodeshift&lt;/code&gt; (AST codemods for the "convert 200 class components to hooks" problem). WebStorm covers day-to-day, jscodeshift covers migrations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-repo org, needs consistency.&lt;/strong&gt; Pick &lt;code&gt;ast-grep&lt;/code&gt; (structural search-and-replace fast enough to run in CI) and Sourcegraph Batch Changes (turns a codemod into PRs across every repo). One writes the change, the other ships it everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can skip
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Babel&lt;/strong&gt;, if you're not building a plugin. It's a compiler with a refactoring side-quest, not a refactoring tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comby&lt;/strong&gt;, if your repo is mostly one language. Its edge is cross-language pattern matching.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenRewrite and Moderne&lt;/strong&gt;, if you're not on the JVM. Excellent recipes, wrong ecosystem.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JetBrains ReSharper&lt;/strong&gt;, if you're not shipping .NET backends. If you are, you already own it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The real lesson
&lt;/h2&gt;

&lt;p&gt;Tool sprawl is a symptom.&lt;/p&gt;

&lt;p&gt;If your team is debating which of 10 refactoring tools to standardize on, the actual problem is that nobody has defined what kinds of changes are worth automating.&lt;/p&gt;

&lt;p&gt;Pick a scope, pick a scale, pick two tools. Ship a codemod. Learn what breaks. That's the loop.&lt;/p&gt;




&lt;p&gt;If you want the full 10-tool breakdown with when-to-use-each, we wrote it up &lt;a href="https://rapidnative.com/blogs/code-refactoring-tools?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=refactoring-tools-decision-tree" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What's your two-tool stack? Drop it in the comments — curious what people actually settle on.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why 77% of Your App's Users Are Gone in 3 Days (and the real fix isn't another tactic)</title>
      <dc:creator>Famitha M A</dc:creator>
      <pubDate>Mon, 20 Jul 2026 09:19:50 +0000</pubDate>
      <link>https://dev.to/rapidnative-ai/why-77-of-your-apps-users-are-gone-in-3-days-and-the-real-fix-isnt-another-tactic-201n</link>
      <guid>https://dev.to/rapidnative-ai/why-77-of-your-apps-users-are-gone-in-3-days-and-the-real-fix-isnt-another-tactic-201n</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;77% of users who install your app stop using it within 72 hours. Most of that churn happens &lt;em&gt;inside the first session&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;2026 cross-industry medians: Day-1 = 26%, Day-7 = 11%, Day-30 = 5.4%. Subscription apps retain ~2.5x better at D30.&lt;/li&gt;
&lt;li&gt;The five real churn causes: no "aha moment", ad/product mismatch, no external trigger, crashes/slow loads, and one-and-done intent (which is fine).&lt;/li&gt;
&lt;li&gt;The uncomfortable part: you probably already know the tactics. The real bottleneck is that your experiment cycle takes 3 weeks while your cohort churns in 3 days.&lt;/li&gt;
&lt;li&gt;Fix the cycle time, not the tactics list.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The number
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;77% of the users who install your app stop using it within 72 hours.&lt;/strong&gt; Not a month. Not a week. Three days.&lt;/p&gt;

&lt;p&gt;That stat traces back to Andrew Chen's analysis at a16z, and every benchmark report since (Adjust, AppsFlyer, Sensor Tower) lands in the same neighborhood. The 2026 numbers, by vertical:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Vertical&lt;/th&gt;
&lt;th&gt;Day 1&lt;/th&gt;
&lt;th&gt;Day 7&lt;/th&gt;
&lt;th&gt;Day 30&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Social &amp;amp; Communication&lt;/td&gt;
&lt;td&gt;30%&lt;/td&gt;
&lt;td&gt;15%&lt;/td&gt;
&lt;td&gt;8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gaming (Casual)&lt;/td&gt;
&lt;td&gt;27%&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finance &amp;amp; Fintech&lt;/td&gt;
&lt;td&gt;33%&lt;/td&gt;
&lt;td&gt;18%&lt;/td&gt;
&lt;td&gt;12%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health &amp;amp; Fitness&lt;/td&gt;
&lt;td&gt;24%&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E-commerce &amp;amp; Retail&lt;/td&gt;
&lt;td&gt;22%&lt;/td&gt;
&lt;td&gt;8%&lt;/td&gt;
&lt;td&gt;4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Productivity&lt;/td&gt;
&lt;td&gt;29%&lt;/td&gt;
&lt;td&gt;14%&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subscription Apps (all)&lt;/td&gt;
&lt;td&gt;35%&lt;/td&gt;
&lt;td&gt;22%&lt;/td&gt;
&lt;td&gt;14%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cross-category median&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;26%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;11%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.4%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things worth noticing: subscription apps retain 2.5x better at Day 30 (paywalls are a commitment device), and fintech beats casual gaming 3x at D30 (utility beats entertainment on retention, almost always).&lt;/p&gt;

&lt;h2&gt;
  
  
  Make sure you're measuring the same thing as the benchmarks
&lt;/h2&gt;

&lt;p&gt;Retention has three variants, and teams routinely fool themselves by measuring one internally and comparing against another:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Classic&lt;/strong&gt; — user came back on &lt;em&gt;exactly&lt;/em&gt; day N. Punitive, but it's what the benchmarks above use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rolling&lt;/strong&gt; — user came back on day N &lt;em&gt;or later&lt;/em&gt;. More forgiving.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bracket&lt;/strong&gt; — user came back anywhere in days N–M. Useful for weekly cohorts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to sanity-check your own numbers, classic Day-N retention is one query away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Classic Day-N retention per install cohort&lt;/span&gt;
&lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;installs&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;install_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;cohort_day&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;app_installs&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="n"&gt;activity&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;DATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;active_day&lt;/span&gt;
  &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;app_events&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cohort_day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;cohort_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;active_day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cohort_day&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'1 day'&lt;/span&gt;
        &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;d1_retention&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="k"&gt;CASE&lt;/span&gt; &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;active_day&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cohort_day&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'7 day'&lt;/span&gt;
        &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;DISTINCT&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;d7_retention&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;installs&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;activity&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Segment this by acquisition channel before you conclude anything. A cohort with 15% D7 overall might be 30% from organic and 4% from one bad Facebook creative. That's not a retention problem — that's an ad-relevance problem wearing a retention costume.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the 77% actually dies: the first session
&lt;/h2&gt;

&lt;p&gt;The churn isn't spread evenly across 72 hours. Roughly 60–70% of it happens &lt;strong&gt;inside the first session&lt;/strong&gt;. Users don't drift away over three opens — they open once, hit friction, close, never return.&lt;/p&gt;

&lt;p&gt;The consistent offenders, with rough measured costs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Signup wall before showing value: &lt;strong&gt;–30-40%&lt;/strong&gt; of installers on that screen alone&lt;/li&gt;
&lt;li&gt;Phone verification: another &lt;strong&gt;–15-20%&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Push permission prompt on Day 0 (before any value): &lt;strong&gt;~4x lower&lt;/strong&gt; opt-in vs. asking after value&lt;/li&gt;
&lt;li&gt;More than five onboarding screens: &lt;strong&gt;~20% more&lt;/strong&gt; churn vs. three or fewer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The cheapest fix in mobile is deferring everything. Guest mode instead of a signup wall:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Instead of gating the app behind auth, persist a local guest identity&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;AsyncStorage&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@react-native-async-storage/async-storage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;v4&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;uuid&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uuid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getOrCreateGuestId&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;AsyncStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;guest_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`guest_&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;AsyncStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;guest_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Later, when the user actually signs up, merge the guest state&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;upgradeGuestToUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;guestId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;AsyncStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;guest_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;guestId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mergeAccounts&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;guestId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;AsyncStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;guest_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same logic for push permissions — ask &lt;em&gt;after&lt;/em&gt; the user has experienced value, not on launch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Don't request on app open. Trigger contextually, e.g. after first "aha" action&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;requestPushAfterValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alreadyAsked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;AsyncStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;push_prompted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alreadyAsked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trigger&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;first_workout_completed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Pre-permission soft prompt first — protects your one real OS prompt&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;showSoftPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Want a reminder before your streak breaks?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wants&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;requestNotificationPermission&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;AsyncStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;push_prompted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The five real reasons users churn
&lt;/h2&gt;

&lt;p&gt;Skip the twenty-tactic listicles. Churn comes from a small set of root causes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;They never hit the "aha moment."&lt;/strong&gt; Duolingo: first lesson done. Instagram: first personalized feed. Fintech: first balance after bank link. Users who reach it retain at 3-5x the rate of users who don't. Your entire first session should be a speedrun to that moment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The ad promised something the app isn't.&lt;/strong&gt; Invisible in aggregate, obvious when you segment by channel (see the SQL above).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No external trigger.&lt;/strong&gt; Apps that survive past D7 almost always reach out — a push tied to a &lt;em&gt;real&lt;/em&gt; event (someone messaged you, your streak's about to break), not "hi, we miss you." Apps that rely on being remembered lose to apps that reach out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crashes and slow screens.&lt;/strong&gt; Users don't file bug reports; they uninstall. And crashes disproportionately hit your heaviest users, because they hit more code paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They got what they came for.&lt;/strong&gt; One-time intent (check a flight, convert a currency once) isn't a retention failure. Segment churn by intent before you panic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The part nobody writes: your bottleneck is cycle time, not tactics
&lt;/h2&gt;

&lt;p&gt;Here's the uncomfortable truth: you already knew most of the above. Everyone's read the same posts. Everyone has the Notion doc of retention experiments. So why is the median D30 still 5.4%?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Because most teams can't ship a retention experiment in under three weeks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Week 1: spec + Figma + design review. Week 2: implementation + new tracking event + QA. Week 3: ship to 20%, wait for the cohort to season, analyze. By the time you know whether the shorter signup form moved D1, the cohort you wanted to save has been gone for two and a half weeks. You're permanently fighting the last war — with eighteen more experiments queued behind this one.&lt;/p&gt;

&lt;p&gt;Duolingo runs hundreds of A/B tests a year. Netflix ships micro-experiments continuously. The gap isn't ideas — it's that they ship an experiment in a day and you ship one a month.&lt;/p&gt;

&lt;p&gt;This is where tooling actually changed the math. Building in an AI-native stack like &lt;a href="https://www.rapidnative.com/?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=mobile-app-retention-why-users-churn" rel="noopener noreferrer"&gt;RapidNative&lt;/a&gt; means the guest-mode experiment above is a prompt — "make signup optional, add continue-as-guest, persist state locally" — with a working build on a real device via QR code in under a minute, instead of a sprint. When iteration cost collapses that far, the strategy flips: you stop hoarding experiments and start running them in parallel.&lt;/p&gt;

&lt;h2&gt;
  
  
  A weekly experiment cadence that actually compounds
&lt;/h2&gt;

&lt;p&gt;If your iteration cost is low enough, this rhythm works:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Monday AM&lt;/strong&gt; — pull last week's cohorts, segmented by channel and onboarding variant. Find the biggest first-session drop-off.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monday PM&lt;/strong&gt; — one hypothesis, one line: "changing X to Y improves D1 by Z% for cohort A."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tuesday&lt;/strong&gt; — build the variant.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wednesday&lt;/strong&gt; — ship to 50% of new installs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next Monday&lt;/strong&gt; — cohort's seasoned. Ship the winner or kill it. Next experiment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's ~5 experiments a month, 60 a year. Even at a 1-in-5 hit rate you're compounding at a pace nobody on a three-week cycle can touch.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Retention in 2026 isn't a knowledge problem. The benchmarks are public and every founder can recite why users churn. The teams that win are the ones shipping retention experiments &lt;strong&gt;faster than their cohorts churn&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Stop asking "which tactic should we prioritize?" Start asking "what's the smallest experiment we can put in front of real users this week?"&lt;/p&gt;

&lt;p&gt;What's your team's actual experiment cycle time — days or weeks? And what's the one onboarding change you've been meaning to test forever? Drop it in the comments, I'm curious what's stuck in everyone's backlog. 👇&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>mobile</category>
      <category>startup</category>
      <category>analytics</category>
    </item>
  </channel>
</rss>
