RORK LABJP
TOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the roundTOOLING — Rork's developer repos keep moving: rork-xcode was updated on July 16, rork-device on July 15, and rork-plist on July 13OPUS46 — Claude Opus 4.6 is live in Rork, and Rork Max is built to assemble apps on top of Claude CodeSIM — A cloud iOS simulator runs in the browser, with one click to install on a device and two clicks to publish to the App StoreMAX — Rork Max emits pure Swift rather than React Native, reaching iPhone, iPad, Apple Watch, Apple TV, Vision Pro, and even iMessageNATIVE — That opens up HealthKit, ARKit and LiDAR, NFC, Dynamic Island, Live Activities, 3D through Metal, and on-device inference with Core MLSEED — Rork raised a $15M seed led by Left Lane Capital, with Peak XV and a16z Speedrun joining the round
Articles/Dev Tools
Dev Tools/2026-04-28Intermediate

Fixing Missing Bottom Tab Icons or Unresponsive Tabs in Rork Apps

When your Rork-built app suddenly loses its Bottom Tab icons or tabs stop responding to taps, the cause is usually one of three patterns. Here's how to triage the symptom and apply the right fix in minutes instead of hours.

Rork515expo-router2Bottom Tabsnavigation2troubleshooting65

A working bottom tab bar feels great, but the moment you tweak the code Rork generates, the icons sometimes disappear or tabs refuse to switch. After shipping a few Rork apps, I noticed I was hitting the same bug at least three times — that's why I sat down and turned my recovery process into this guide.

The painful thing about this bug is that knowing where to look first saves you half a day. So before you start editing _layout.tsx at random, sort the symptom into one of three buckets and work through the matching fix. The diagnosis takes about a minute and the rest of this article is structured around acting on that diagnosis.

Triage the symptom into one of three patterns

Rork's bottom tab issues almost always fall into these categories:

  • Pattern A: Icons don't render, but tabs still switch when tapped.
  • Pattern B: Icons render fine, but tapping a tab does nothing.
  • Pattern C: Everything works in development, but icons vanish in production builds.

A and B have completely different root causes, so trying the same fix on both will waste your time. Open the app in Expo Go, Rork Companion, or your simulator and confirm which bucket you're in. Tap each tab. Look at the icons. Then jump straight to the matching section below.

If you're working with someone else's report ("the tabs are broken"), make them send a screenshot first. Half the time, the screenshot alone tells you which pattern it is and you can skip a back-and-forth.

Pattern A: Icons don't render

In my experience this almost always comes down to either the return type of tabBarIcon or a missing icon library dependency. Rork's generated code typically uses lucide-react-native, and swapping it out for another library (or modifying the icon function signature) is where most regressions sneak in.

// app/(tabs)/_layout.tsx
import { Tabs } from "expo-router";
import { Home, Settings, User } from "lucide-react-native";
 
export default function TabLayout() {
  return (
    <Tabs
      screenOptions={{
        // BAD: passing a JSX element instead of a function
        // means size/color never reach the icon, and the
        // active/inactive tint stops working.
        // tabBarIcon: <Home />,
 
        // GOOD: ({ color, size }) => ReactNode
        tabBarActiveTintColor: "#0070f3",
        headerShown: false,
      }}
    >
      <Tabs.Screen
        name="index"
        options={{
          title: "Home",
          tabBarIcon: ({ color, size }) => <Home color={color} size={size} />,
        }}
      />
      <Tabs.Screen
        name="settings"
        options={{
          title: "Settings",
          tabBarIcon: ({ color, size }) => <Settings color={color} size={size} />,
        }}
      />
    </Tabs>
  );
}

Three things to verify: (1) tabBarIcon must be a function, not a pre-rendered JSX element. The function shape ({ color, size, focused }) => ReactNode is what react-navigation hands the active tint to. (2) The function must forward color and size so the active/inactive state and tab bar height can drive the icon's appearance. If you hard-code a color, the focused state stops being visible. (3) If you're using lucide-react-native, you also need react-native-svg installed — without it, the icons silently fail without throwing an error. A quick expo install react-native-svg followed by a fresh build usually brings them back instantly.

One subtle gotcha: if you upgrade lucide-react-native independently of the rest of the toolchain, you can land on a major version that requires a newer react-native-svg than what's pinned in your package.json. Run expo doctor after any icon-library upgrade — it catches this almost every time.

Pattern B: Tabs don't switch when tapped

If the tabs render but tapping them does nothing, suspect a mismatch between the route name in <Tabs.Screen name="..." /> and the actual file name in app/(tabs)/. expo-router maps file names directly to routes, so name="profile" paired with a file called Profile.tsx (capital P) will register as a non-matching route and the tap becomes a no-op. macOS and Linux file systems handle case differently, which is why this often passes locally and breaks in CI.

The fix flow is straightforward: list the contents of app/(tabs)/ and confirm every filename is lowercase with hyphens, matching the name= you pass to Tabs.Screen. While you're there, double-check that the order of <Tabs.Screen /> declarations doesn't accidentally skip a file. expo-router will route to a file even if it's not declared in _layout.tsx, but it can't fall back to a missing one.

The other common offender is nested stacks inside a tab. If you put a Stack inside a tab folder, you must specify initialRouteName so the stack knows what to mount when the tab is tapped. Without it, the user lands on an empty route and the screen looks frozen even though the navigation event fired correctly. You can confirm this by adding a console.log in the screen's component — if the log fires but nothing renders, you're chasing a stack mounting issue, not a tab switching issue. The full debugging path is covered in Fixing expo-router screen navigation errors, which is worth bookmarking.

A third, rarer cause: a parent gesture handler swallowing the tap. If you've wrapped your root layout in GestureHandlerRootView with custom pan responders, a misconfigured handler can intercept tab presses. Temporarily remove the gesture handler to confirm; if tabs start working, you've found the culprit.

Pattern C: Production-only disappearing icons

Everything works in dev, but TestFlight or release APKs ship with blank tab icons. This is almost always a Metro tree-shaking interaction with the icon library. Packages like lucide-react-native rely on dynamic imports under the hood, and aggressive production bundling can drop those references because the bundler doesn't realize they're being used at runtime.

Try the following in order, since each fix is increasingly invasive:

  • Open metro.config.js and check whether you've enabled resolver.unstable_enableSymlinks. Setting it to true frequently breaks module resolution in production; revert it to the default and rebuild.
  • Temporarily disable experiments.tsconfigPaths in app.json and rebuild. Path aliases combined with tree-shaking sometimes confuse the resolver into dropping icon entries.
  • Confirm react-native-svg is on a stable version. Pre-release builds occasionally regress the production rendering path.
  • If none of the above helps, swap to a more battle-tested icon set such as @expo/vector-icons or react-native-vector-icons. Switching libraries forces a different bundling path and almost always works.

I keep @expo/vector-icons as a fallback because it ships with the Expo SDK and almost never breaks under production bundling. Switching libraries means you're loading a different package entirely, which sidesteps the tree-shaking edge case. The visual difference is small enough that most users won't notice; the difference in stability is huge.

If you must keep lucide-react-native, one workaround is to import the icons explicitly at the top of your _layout.tsx instead of pulling them through a barrel file. Explicit imports tell the bundler exactly what to keep, even when tree-shaking is aggressive. It's verbose, but reliable.

Habits that prevent this bug entirely

A few practices that keep me out of this rabbit hole:

  • Don't assume "missing icon = misconfiguration." Roughly half the time, the real cause is a missing peer dependency like react-native-svg, or a Hermes-related quirk in production. Run expo doctor first; it catches more than people give it credit for.
  • Match Tabs.Screen name to file name 1:1. When asking Rork's AI to "add a tab," scan the new file tree before saving — the generator occasionally creates a name that doesn't exist as a route. A 5-second sanity check saves a 30-minute debugging session.
  • Always smoke-test the production build on TestFlight. Companion working ≠ production working. Add "verify tab bar visuals" as a one-line item in your release checklist; it's the cheapest insurance you can buy. The first time a missing icon ships to real users, you'll wish you had this habit.
  • Pin your icon library version. Don't use a ^ range for lucide-react-native or any peer-dependent icon package. A minor version bump that ships under ^ can quietly break production while leaving dev untouched.

If your symptom looks more like a state-update issue than a routing issue, Fixing state that won't update or trigger a rerender covers the adjacent failure modes worth ruling out before you start tearing apart your tab layout.

One thing to do after you've fixed it

After the bug is gone, extract app/(tabs)/_layout.tsx into its own component and leave a three-line comment at the top: "When adding a tab, install react-native-svg, match name= to the filename, and verify the production build via TestFlight." Future you (or a collaborator) will hit this same bug — that tiny checklist has saved me half a day more times than I'd like to admit.

The pattern of triaging-then-fixing also generalizes well. Most of the gnarly bugs in mobile development have two or three distinct failure modes wearing the same surface symptom. Spend the first minute classifying, and the next ten minutes are usually enough to fix it.

Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Dev Tools2026-06-12
Your Rork App's 'Documents & Data' Keeps Growing — Taming expo-image's Disk Cache
My wallpaper app's binary was 40 MB, yet 'Documents & Data' had ballooned to 2.4 GB. Here is how I diagnosed expo-image's unbounded disk cache and fixed it with cachePolicy tuning, thumbnail URLs, and generational cache clearing.
Dev Tools2026-05-26
iOS Foreground Notifications Disappear in Expo — A Practical Fix Guide
A focused walkthrough of why iOS silently drops push notifications when the app is in the foreground, and how to wire setNotificationHandler correctly for iOS 14+ with expo-notifications.
Dev Tools2026-05-09
5 Things to Check When Google Sign-In Throws redirect_uri_mismatch in Your Rork App
When your Rork app keeps throwing redirect_uri_mismatch on Google Sign-In, the gap is almost always between Google Cloud Console and your Expo config. Here are 5 checks that resolve it for good.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →