← Back to all posts

Using SVG in React Native: The Clean Way with Theme Support

EhsanBy Ehsan
6 min read
React NativeSVGSVG IconsDark ModeIcon ManagementThemingTheme SupportMobile DevelopmentcurrentColor

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 use react-native-svg and 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:

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.

Resources