Using SVG in React Native: The Clean Way with Theme Support
Introduction
Adding icons to React Native apps used to be painful. PNG files at multiple resolutions, manually switching colors for dark mode, or using icon fonts that don't scale well.
I've been using this method for quite a while and I'm very happy so far: all you need to do is make sure your SVG files are using currentColor, import them as components, and the rest will be handled automatically.
It's clean, scalable, and works perfectly with dark mode. Let me show you how.
Why SVG?
SVGs are vector graphics. They scale perfectly at any size, support theming, and can be imported directly as React components.
No more @2x, @3x files. No more icon fonts. Just clean, themeable icons.
Setup
Install the dependencies:
npm install react-native-svg
npm install --save-dev react-native-svg-transformer
Configure Metro to treat SVGs as components:
// metro.config.js
const { getDefaultConfig } = require('@expo/metro-config');
module.exports = (() => {
const config = getDefaultConfig(__dirname);
config.transformer.babelTransformerPath = require.resolve(
'react-native-svg-transformer'
);
config.resolver.assetExts = config.resolver.assetExts.filter(
(ext) => ext !== 'svg'
);
config.resolver.sourceExts.push('svg');
return config;
})();
That's it. Metro now understands SVG files.
Note: If you only have a few SVGs in your project, you don't need
react-native-svg-transformer. Just usereact-native-svgand convert your SVG files to TSX components using online tools like:I always try to keep dependencies minimal. The transformer becomes valuable when you have many SVGs and the convenience of direct imports outweighs adding another dependency.
Using SVG Files
Import SVG files directly as components:
import PlayIcon from '../assets/icons/play.svg';
import { useTheme } from '@react-navigation/native';
function MusicPlayer() {
const theme = useTheme();
return (
<PlayIcon
width={40}
height={40}
fill={theme.colors.primary}
/>
);
}
The SVG renders as a native component. Pass width, height, and fill as props.
The Key: currentColor
Here's the trick that makes theming work. Edit your SVG files to use currentColor instead of hard-coded colors.
Before:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="#000000" d="M8 5v14l11-7z"/>
</svg>
After:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor" d="M8 5v14l11-7z"/>
</svg>
Now the icon uses whatever color you pass to the fill prop. No more editing SVG files for every color change.
Building a Themed Icon Wrapper
Create a reusable wrapper that automatically picks the right color from your theme:
// components/ThemedIcon.tsx
import { useTheme } from '@react-navigation/native';
interface IconProps {
Icon: React.ComponentType<any>;
size?: number;
color?: string;
}
export function ThemedIcon({ Icon, size = 24, color }: IconProps) {
const theme = useTheme();
const finalColor = color ?? theme.colors.text;
return <Icon width={size} height={size} fill={finalColor} />;
}
Use it in your components:
import PlayIcon from '../assets/icons/play.svg';
import PauseIcon from '../assets/icons/pause.svg';
import { ThemedIcon } from '../components/ThemedIcon';
function MusicControls({ isPlaying }) {
return (
<View>
<ThemedIcon
Icon={isPlaying ? PauseIcon : PlayIcon}
size={32}
/>
</View>
);
}
Icons automatically use your theme colors. Switch to dark mode, and icons update instantly.
Organizing Your Icons
Keep icons organized in one folder:
src/assets/icons/
play.svg
pause.svg
next.svg
previous.svg
heart.svg
shuffle.svg
Create an index file to export everything:
// assets/icons/index.ts
export { default as PlayIcon } from './play.svg';
export { default as PauseIcon } from './pause.svg';
export { default as NextIcon } from './next.svg';
export { default as PreviousIcon } from './previous.svg';
export { default as HeartIcon } from './heart.svg';
export { default as ShuffleIcon } from './shuffle.svg';
Import icons from one place:
import { PlayIcon, PauseIcon, HeartIcon } from '@src/assets/icons';
Clean, organized, easy to maintain.
NativeWind Integration
If you're using NativeWind (Tailwind for React Native), you can use utility classes for even cleaner theming:
import PlayIcon from '../assets/icons/play.svg';
function MusicPlayer() {
return (
<PlayIcon
className="text-blue-500 dark:text-white"
width={40}
height={40}
/>
);
}
Icons automatically switch colors based on theme. No JavaScript needed. Learn more about setting up NativeWind with CSS variables for theming.
Here's a complete example with themed icons:
import { View, TouchableOpacity } from 'react-native';
import { useTheme } from '@react-navigation/native';
import {
PreviousIcon,
PlayIcon,
PauseIcon,
NextIcon,
ShuffleIcon,
RepeatIcon
} from '@src/assets/icons';
function PlayerControls({ isPlaying, onPlay, onPause, onNext, onPrevious }) {
const theme = useTheme();
return (
<View className="flex-row items-center justify-center gap-4 py-4">
<TouchableOpacity>
<ShuffleIcon
width={24}
height={24}
fill={theme.colors.text}
/>
</TouchableOpacity>
<TouchableOpacity onPress={onPrevious}>
<PreviousIcon
width={32}
height={32}
fill={theme.colors.primary}
/>
</TouchableOpacity>
<TouchableOpacity onPress={isPlaying ? onPause : onPlay}>
{isPlaying ? (
<PauseIcon
width={48}
height={48}
fill={theme.colors.primary}
/>
) : (
<PlayIcon
width={48}
height={48}
fill={theme.colors.primary}
/>
)}
</TouchableOpacity>
<TouchableOpacity onPress={onNext}>
<NextIcon
width={32}
height={32}
fill={theme.colors.primary}
/>
</TouchableOpacity>
<TouchableOpacity>
<RepeatIcon
width={24}
height={24}
fill={theme.colors.text}
/>
</TouchableOpacity>
</View>
);
}
All icons automatically adapt to your theme. Switch to dark mode, and everything updates.
Advanced: Multiple Colors in One SVG
Some icons need multiple colors. Use currentColor for the primary color and pass additional colors as props:
// Icon with two colors
import Logo from '../assets/logo.svg';
<Logo
width={60}
height={60}
fill={theme.colors.primary}
color={theme.colors.secondary}
/>
Your SVG needs to support this:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path fill="currentColor" d="..."/>
<path fill="var(--secondary)" d="..."/>
</svg>
This is rare, but useful for complex logos or illustrations.
TypeScript Support
Add type definitions for SVG imports:
// src/@types/svg.d.ts
declare module '*.svg' {
import React from 'react';
import { SvgProps } from 'react-native-svg';
const content: React.FC<SvgProps>;
export default content;
}
Now TypeScript understands SVG imports.
Finding Icons
Where do you get SVG icons?
Free sources:
- Heroicons - Beautiful, MIT licensed
- Lucide - Fork of Feather Icons, well maintained
- Phosphor Icons - Flexible, multiple weights
- Remix Icon - 2500+ icons, open source
Download the SVG, replace fill colors with currentColor, and drop it in your project.
Tips for Success
Use currentColor - Always replace hard-coded colors with currentColor for theme support.
Keep icons simple - Complex SVGs with gradients or filters don't always work well. Stick to solid shapes.
Size consistently - Keep viewBox consistent across your icon set (usually 0 0 24 24). Makes sizing predictable.
Optimize SVGs - Use SVGO or the web-based SVGOMG to clean up SVG files. Removes unnecessary metadata and reduces file size.
Cache imported icons - React Native caches imported modules. No performance hit from importing the same icon multiple times.
Don't overuse - For simple shapes (circles, squares), use React Native's built-in components. SVG is for complex icons only.
Why This Approach Works
No duplicate files - One SVG file, works at all sizes and themes
Type-safe - Full TypeScript support with proper types
Themeable - Automatic dark mode support with zero effort
Performant - Native rendering, no image loading delays
Maintainable - Change theme colors once, all icons update
Scalable - Vector graphics look perfect at any size
Final Thoughts
SVG support in React Native used to require complicated libraries or manual configuration. Now it's simple: install two packages, configure Metro, use currentColor, and you're done.
Icons automatically adapt to your theme, scale perfectly, and perform well. No PNG files, no icon fonts, no manual color management.
This is how icons should work in React Native. Set it up once, use it everywhere, and never think about it again.