◉RORK LABJP
●EXPO — EAS Observe now records native crashes (Oct 7). On SDK 57, update to 57.0.21 or later●SDK 58 — SDK 58 Beta has been out since Sep 15. The stable date is still unconfirmed●RN 0.88 — React Native 0.88.x is scheduled for Oct 12, 3 days left●Q&A — People are asking why expo-widgets render blank only in production builds●RORK — GPT-6.1 Sol was added on Sep 29, available on Pro and Max plans●NEW — Building an app for a client? Decide who owns the publishing account first●EXPO — EAS Observe now records native crashes (Oct 7). On SDK 57, update to 57.0.21 or later●SDK 58 — SDK 58 Beta has been out since Sep 15. The stable date is still unconfirmed●RN 0.88 — React Native 0.88.x is scheduled for Oct 12, 3 days left●Q&A — People are asking why expo-widgets render blank only in production builds●RORK — GPT-6.1 Sol was added on Sep 29, available on Pro and Max plans●NEW — Building an app for a client? Decide who owns the publishing account first
Articles/Dev Tools
⬡ Dev Tools/2026-07-08Intermediate

Your SVG Icon Shows in Expo Go but Vanishes in a Build — Making SVGs Render Reliably in Rork Apps

When a .svg import renders nothing, throws a resolve error, or ignores your theme colors in Rork and Expo apps, here is how to fix it with metro.config.js, react-native-svg-transformer, and currentColor — with working code.

Troubleshooting41SVGExpo213React Native238Icons

I was swapping the tab-bar icons in a wallpaper app for a set of thin, custom SVGs. They looked perfect in Expo Go, so I built a development build with confidence — and on the very same screen, the icons had quietly disappeared. No error. Just a transparent hole where a shape should have been. It took me the better part of an hour to trace it, so let me lay out what was going on for anyone stuck at the same spot.

The symptoms

Anything involving SVG icons tends to fail in one of these shapes:

  • Renders in Expo Go, but draws nothing in a development or production build
  • A red screen reading Unable to resolve module ./icon.svg
  • Renders, but the color stays black and never follows dark mode
  • Shows on iOS but not Android, or the other way around

The tricky part is that it usually fails silently as a transparent shape rather than an error. The layout box is still reserved, so the icon is invisible while occupying space — which sent me chasing CSS-style problems for far too long.

How to reproduce it

This happens with code Rork generated, or code you added yourself, that loads an SVG file like this:

import TabIcon from "../assets/icons/home.svg";
 
export function HomeTab() {
  return <TabIcon width={24} height={24} />;
}

It looks completely reasonable. But out of the box, the React Native / Expo Metro bundler treats .svg as an image asset. That means <TabIcon /> is an image-source object, not a component, so rendering it as a component produces nothing.

When it happens to work in Expo Go, that is usually an environment-specific accident — a stale cache, an old bundle, a different load path — and leaning on it will bite you in production.

Why the icon doesn't show

There are two root causes.

The first: there is no mechanism to import .svg as a component. React Native has no equivalent of the web's <img src="...svg">; drawing an SVG requires react-native-svg. To treat a .svg file directly as a JSX component, you also need react-native-svg-transformer wired into Metro. Without it, Metro keeps handling SVGs as static assets.

The second: the color is baked into the SVG file. Design tools often export SVGs with concrete fills like fill="#111111", which leaves your app unable to recolor them per theme. That black-that-stays-black-in-dark-mode look is exactly this case.

Fix 1: let Metro import .svg as a component

Install the packages first. Under Expo, npx expo install pulls versions matched to your SDK.

npx expo install react-native-svg
npm install --save-dev react-native-svg-transformer

Then edit metro.config.js at the project root. With Expo, building on top of getDefaultConfig is the safe path. Create the file if it doesn't exist yet.

// metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
 
const config = getDefaultConfig(__dirname);
 
// Let SVGs be treated as JSX components
config.transformer.babelTransformerPath = require.resolve(
  "react-native-svg-transformer"
);
 
// Move .svg out of "assets" and into "source code" resolution
config.resolver.assetExts = config.resolver.assetExts.filter(
  (ext) => ext !== "svg"
);
config.resolver.sourceExts = [...config.resolver.sourceExts, "svg"];
 
module.exports = config;

The key is removing svg from assetExts and adding it to sourceExts. Skip these two lines and Metro will keep treating SVGs as images even with the transformer installed.

If you use TypeScript, you'll get red squiggles without a type for importing .svg, so add one declaration file.

// declarations.d.ts
declare module "*.svg" {
  import type { SvgProps } from "react-native-svg";
  const content: React.FC<SvgProps>;
  export default content;
}

Finally, after changing config, clear the Metro cache and restart. Skip this and an old resolution lingers, making a fixed setup look broken.

npx expo start --clear

Now that same import TabIcon from "../assets/icons/home.svg" renders directly as a component.

Fix 2: follow the theme with currentColor

If it renders but the color never changes, replace the SVG's fixed fill with currentColor. react-native-svg-transformer bridges currentColor inside the SVG to the color prop you pass the component.

If your exported path looks like this,

<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path fill="#111111" d="M12 3l9 7v11h-6v-6H9v6H3V10z" />
</svg>

swap the fixed color for currentColor:

<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
  <path fill="currentColor" d="M12 3l9 7v11h-6v-6H9v6H3V10z" />
</svg>

On the call site, just pass color and it switches with your theme.

import { useColorScheme } from "react-native";
import HomeIcon from "../assets/icons/home.svg";
 
export function HomeTab() {
  const scheme = useColorScheme();
  const tint = scheme === "dark" ? "#F2F2F7" : "#1C1C1E";
  return <HomeIcon width={24} height={24} color={tint} />;
}

For multi-color illustrations, set only the parts you want to recolor to currentColor and leave accent colors fixed. For single-color icons, unifying every path to currentColor means a palette change later happens in one place.

Which approach to choose

SituationBest fit
Many designer-exported SVGsFix 1 (import via transformer)
A few icons, or shapes that change dynamicallyHand-write with Svg / Path from react-native-svg
Colors should follow the themeAdd Fix 2 (currentColor)

I settled on loading small tab-bar and list-row icons as .svg via the transformer, with colors unified to currentColor. Swapping a design becomes a matter of replacing the .svg file, which keeps day-to-day maintenance very light.

Prevention

To avoid the same trap, I now lock down three things up front.

First, add metro.config.js and the type declaration on day one of a project. Adding them later steals time on "why won't this show" investigations.

Second, whenever I add an SVG, I check it once in a development build (not Expo Go). Expo Go can behave differently because of its bundled libraries, so working there is no guarantee it works in production.

Third, I made --clear a reflex after touching config. Most "I fixed it but nothing changed" moments around SVG came down to Metro holding a stale resolution.

References

Even a single small icon becomes far easier to maintain once you understand how it renders. I hope this saves someone the hour it cost me. Thank you for reading.

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 $15 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-09-21
A build that stops with exit code 0 — the flag that silenced the log, and the check that let an empty file through
Two reports landed in the same week: eas build --local stopping with exit code 0 but produced no further output, and a zero-byte stub that never got embedded. Both came down to settings that reduce output. Here is how to audit your own scripts.
⬡ Dev Tools2026-09-10
I Added a Dim Screen Button, and iPhones Stayed Dark After the App Was Closed
In expo-brightness, setBrightnessAsync applies only to the current activity on Android, but changes the device brightness itself on iOS. Here is why the cleanup burden falls on one platform only, and a hook that restores brightness through AppState.
⬡ Dev Tools2026-09-04
EAS secret visibility does not keep a value out of your app — deciding prefix and visibility separately
The EXPO_PUBLIC_ prefix decides what ships inside your app; EAS visibility decides who can read it. Why stacking them blanks a value on OTA updates, and how to check your build.
📚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