Transforming SVG Files into React Components

Kacper Cyranowski
August 1, 2025
5 min read

Introduction to SVGR

In the world of modern web and mobile application development, SVG (Scalable Vector Graphics) has become an essential asset format. SVGs provide crisp, resolution-independent graphics that look great on any device. However, working with SVGs in React and React Native applications can be challenging without the right tools.

Enter SVGR - a powerful utility that transforms SVG files into ready-to-use React components. SVGR simplifies the process of integrating vector graphics into your React applications by converting static SVG files into dynamic, customizable components.

This article will guide you through setting up and using SVGR in your React Native projects, exploring both basic configurations and advanced customization techniques. We'll cover different approaches to transform your SVGs, manage color schemes, handle size responsiveness, and implement custom templates for greater control.

Whether you're working with icons, illustrations, or complex graphics, SVGR provides a seamless workflow that integrates with your development process. By the end of this article, you'll have a solid understanding of how to leverage SVGR to enhance your application's visual elements while maintaining performance and flexibility.

Let's dive into the details of implementing SVGR in your React Native project, starting with the basic setup and moving toward more advanced customization options.

Quick Guide: Setting Up SVGR with Expo

This guide walks through the process of setting up SVGR in an Expo project to easily transform SVGs into React Native components.

Step 1: Create a new Expo project (if needed)

# Install the Expo CLI if you don't have it already
npm install -g expo-cli

# Create a new Expo project
expo init my-svg-app
cd my-svg-app

Step 2: Install required dependencies

npm install --save-dev react-native-svg @svgr/cli @svgr/babel-plugin-replace-jsx-attribute-value
npm install react-native-svg

Step 3: Create SVGR configuration

Create a svgr directory and a .svgrrc.js file inside it:

mkdir -p svgr/helpers
touch svgr/.svgrrc.js

On start let's use simple configuration:

module.exports = {
  outDir: './components/icons',
  native: true,
  typescript: true,
  exportType: 'default',
  expandProps: 'end',
};

Understanding the Configuration Options

The SVGR configuration provides several options to control how SVG files are transformed into React components:

  • outDir: Specifies the output directory for generated components
  • native: Generates React Native compatible components using react-native-svg
  • typescript: Generates TypeScript files
  • exportType: Sets the export type ('default' for default exports)
  • expandProps: Controls where additional props are spread ('end' places them at the end of props)

You can find all configuration options in the SVGR documentation.

Advanced Configuration with Babel Plugins

For more advanced transformations, you can use SVGR's Babel plugins. Here's an example of using these pluginsfor color manipulation and attribute cleanup:

module.exports = {
  // ... other options from above
  jsx: {
    babelConfig: {
      plugins: [
        [
          "@svgr/babel-plugin-replace-jsx-attribute-value",
          {
            values: [
              { value: "#000", newValue: "#fff" },
              { value: "#6c63ff", newValue: "props.primaryColor || \"#ff6363\"", literal: true },
              { value: "#ff6363", newValue: "props.secondaryColor || \"#6c63ff\"", literal: true }
            ]
          }
        ],
        [
          "@svgr/babel-plugin-remove-jsx-attribute",
          {
            elements: ["Svg"],
            attributes: ["xmlns", "className"]
          }
        ]
      ]
    }
  }
};

What's Happening in the Advanced Configuration?

1. Color Replacement:

The babel-plugin-replace-jsx-attribute-value plugin is used to dynamically replace color values:

  • #000 is replaced with #fff
  • #6c63ff is replaced with props.primaryColor (with a fallback to #ff6363)
  • #ff6363 is replaced with props.secondaryColor (with a fallback to #6c63ff)
  • The literal: true option means the new value will be treated as a JavaScript expression rather than a string

2. Attribute Removal:

The babel-plugin-remove-jsx-attribute plugin removes unnecessary attributes in this case it removes xmlns and className attributes from Svg elements.

The Result

The final result will look like this. It’s not perfect as we are missing type for our props.

import * as React from "react";
import Svg, { Path } from "react-native-svg";
import type { SvgProps } from "react-native-svg";
const SvgApiInterfaceIcon = (props: SvgProps) => (
 <Svg width={24} height={24} viewBox="0 0 1024 1024" {...props}>
 <Path fill="#FFF" d="m432.4 453.5-17 46.7h34.4z" />
 <Path
 fill="#FFF"
 d="M725.3 259.7H312.2c-16.5 0-30 13.5-30 30v413.1c0 16.5 13.5 30 30 30h413.1c16.5 0 30-13.5 30-30V289
 />
 <Path
 fill="#FFF"
 d="M569.4 479.2c3.4-1.3 6-3.4 7.9-6.2s2.9-6.1 2.9-9.8c0-4.6-1.3-8.4-4-11.3-2.7-3-6.1-4.8-10.2-5.6q-4.5-.9 />
 <Path
 fill={props.secondaryColor || "#6c63ff"}
 d="M648.4 677.5H352.6c-8.3 0-15.1-6.8-15.1-15.1v-295c0-5.5-4.5-10-10-10s-10 4.5-10 10v295c0 19.4 15.7 35
 />
 <Path
 fill={props.primaryColor || "#ff6363"}
 d="M865 386.5c11 0 20-9 20-20s-9-20-20-20h-69.7v-56.8c0-38.6-31.4-70-70-70h-27.8v-67.3c0-11-9-20-2
 />
 <Path
 fill={props.primaryColor || "#ff6363"}
 d="M407.6 521.4h50.3l11 28.6h27.6l-50.4-125.8h-26.9l-49 125.8h27zm24.8-67.9 17.3 46.7h-34.3zm103 49.1H
 />
 </Svg>
);

export default SvgApiInterfaceIcon;

Custom template

While SVGR's configuration options and Babel plugins provide significant customization, custom templates offerthe ultimate flexibility for tailoring your SVG components to specific project requirements. Custom templates allowyou to control exactly how the generated React components are structured, what props they accept, and howthey behave.

Creating Custom Templates

First, create these files in your svgr directory:

touch svgr/svgrTemplate.js
touch svgr/svgrIndexTemplate.js

Update your SVGR configuration to use these custom templates by adding these lines (and removing the previous plugin configuration):

module.exports = {
// ...previous configuration
 template: require('./svgrTemplate'),
 indexTemplate: require('./svgrIndexTemplate'),
};

Index Template

The index template controls how components are exported. This custom template ensures consistent naming conventions by:

  • Removing "Svg" prefix if exists
  • Ensuring all components have "Icon" phrase in their export names

const path = require('path');
function defaultIndexTemplate(filePaths) {
 const exportEntries = filePaths.map((filePath) => {
 const basename = path.basename(filePath.path, path.extname(filePath.path));
 const exportName = /^\d/.test(basename) ? `Svg${basename}` : basename;
 const finalName = exportName.includes('Icon') ? exportName : `${exportName}Icon`;
 return `export { ${finalName} } from './${basename}'`;
 });
 return exportEntries.join('\n');
}
module.exports = defaultIndexTemplate;

Component Template

The component template provides advanced control over how SVG components are generated:

const {
 getAttributeValue,
 removeProp,
 mapCustomAttributesToProps,
 replaceAttributeValue,
 cleanup
} = require('./helpers');
const template = (
 { imports, componentName, jsx },
 { tpl: template },
) => {
 // Extract the original width and height values from the SVG
const iconWidth = getAttributeValue(jsx, 'width')
 const iconHeight = getAttributeValue(jsx, 'height')
 // Calculate the original aspect ratio to maintain proportions when resizing
 const ratio = Number(iconWidth) / Number(iconHeight)
 // Standardize component naming - remove "Svg" prefix and ensure "Icon" suffix
 componentName = componentName.replace('Svg', '')
 componentName = componentName.includes('Icon') ? componentName : `${componentName}Icon`
 // Remove unnecessary SVG attributes that aren't needed in React Native
 removeProp(jsx)('xmlns') // XML namespace not needed in React
 removeProp(jsx)('role') // Accessibility role handled differently in React Native
 removeProp(jsx)('className') // CSS classes aren't used in React Native
 // Replace static width/height with dynamic values from component props
 replaceAttributeValue(jsx, 'width', {
 type: 'JSXExpressionContainer', expression: {
 type: 'Identifier',
 name: 'width'
 }
 }, false);
 replaceAttributeValue(jsx, 'height', {
 type: 'JSXExpressionContainer', expression: {
 type: 'Identifier',
 name: 'height'
 }
 }, false);

 // Make fill colors dynamic by using props.fill (supports indexed fills for multiple colors)
 replaceAttributeValue(jsx, 'fill', {
 type: 'JSXExpressionContainer', expression: {
 type: 'MemberExpression',
 object: { type: 'Identifier', name: 'props' },
 property: { type: 'Identifier', name: 'fill' },
 computed: false
 }
 }, true);
 // Generate TypeScript interface for custom props
 const customPropsString = mapCustomAttributesToProps();
 // Store original dimensions as constants
 const defaultWidthString = `const DEFAULT_WIDTH = ${iconWidth};`
 const defaultHeightString = `const DEFAULT_HEIGHT = ${iconHeight};`
 // Calculate dynamic size while preserving aspect ratio
 const widthString = `const width = size;`
 const heightString = `const height = size / ${ratio};`
 // Assemble the complete component using the template literal
 const result = template`
 ${imports}
 ${defaultWidthString}
 ${defaultHeightString}
 ${customPropsString}
export const ${componentName} = ({ size = DEFAULT_WIDTH, ...props }: SvgProps & Props) => {
 ${widthString};
 ${heightString};
 return (
 ${jsx}
 )};
 `;
 // Reset any state tracking for the next component
 cleanup();
 return result;
};
module.exports = template;

The Result

When you run SVGR with these custom templates, your SVG files will be transformed into React Nativecomponents with a consistent structure and intelligent defaults. Here's a complete example of what a generatedcomponent might look like:

import * as React from "react";
import Svg, { Path } from "react-native-svg";
import type { SvgProps } from "react-native-svg";
const DEFAULT_WIDTH = 24;
const DEFAULT_HEIGHT = 24;
interface Props {
 fill1?: string;
 fill2?: string;
 fill3?: string;
 fill4?: string;
 fill5?: string;
 fill6?: string;
}
export const ApiInterfaceIcon = ({ size = DEFAULT_WIDTH, ...props }: SvgProps & Props) => {
 const width = size;
 const height = size / 1;
 return (
 <Svg width={width} height={height} viewBox="0 0 1024 1024" {...props}>
 <Path fill={props.fill1 || "#FFF"} d="m432.4 453.5-17 46.7h34.4z" />
 <Path
 fill={props.fill2 || "#FFF"}
 d="M725.3 259.7H312.2c-16.5 0-30 13.5-30 30v413.1c0 16.5 13.5 30 30 30h413.1c16.5 0 30-13.5 30-30V
289.7c0-16.6-13.5-30-30-30m-98.8 164.5h25.4V550h-25.4zm-116.5 0h40.8c15.5 0 25.5.6 30.2 1.9 7.2 1.9 13.
2 6 18.1 12.3s7.3 14.5 7.3 24.5q0 11.55-4.2 19.5c-4.2 7.95-6.4 9.4-10.7 12.4q-6.45 4.5-13.2 6c-6.1 1.2-14.8 1.8-2
6.4 1.8h-16.6V550H510zm-90.7 0h26.9L496.5 550h-27.6l-11-28.6h-50.3L397.2 550h-27zm229.1 273.3H352.6
c-19.4 0-35.1-15.7-35.1-35.1v-295c0-5.5 4.5-10 10-10s10 4.5 10 10v295c0 8.3 6.8 15.1 15.1 15.1h295.8c5.5 0 10 4.5 10 10s-4.4 10-10 10"
 />
 <Path
 fill={props.fill3 || "#FFF"}
 d="M569.4 479.2c3.4-1.3 6-3.4 7.9-6.2s2.9-6.1 2.9-9.8c0-4.6-1.3-8.4-4-11.3-2.7-3-6.1-4.8-10.2-5.6q-4.
5-.9-18.3-.9h-12.3v35.7h13.9c10 .1 16.7-.6 20.1-1.9"
 />
 <Path
 fill={props.fill4 || "#6c63ff"}
 d="M648.4 677.5H352.6c-8.3 0-15.1-6.8-15.1-15.1v-295c0-5.5-4.5-10-10-10s-10 4.5-10 10v295c0 19.4 1
5.7 35.1 35.1 35.1h295.8c5.5 0 10-4.5 10-10s-4.4 10-10 10"
 />
 <Path
 fill={props.fill5 || "#ff6363"}
 d="M865 386.5c11 0 20-9 20-20s-9-20-20-20h-69.7v-56.8c0-38.6-31.4-70-70-70h-27.8v-67.3c0-11-920-20-20s-20 9-20 20v67.3H611v-67.3c0-11-9-20-20-20s-20 9-20 20v67.3h-46.5v-67.3c0-11-9-20-20-20s20 9-20 20v67.3H438v-67.3c0-11-9-20-20-20s-20 9-20 20v67.3h-85.8c-38.6 0-70 31.4-70 70v56.8h-69.7c11 0-20 9-20 20s9 20 20 20h69.7V433h-69.7c-11 0-20 9-20 20s9 20 20 20h69.7v46.5h-69.7c-11 0-20 9-20
20s9 20 20 20h69.7V606h-69.7c-11 0-20 9-20 20s9 20 20 20h69.7v56.8c0 38.6 31.4 70 70 70H343v72.5c0
11 9 20 20 20s20-9 20-20v-72.5h46.5v72.5c0 11 9 20 20 20s20-9 20-20v-72.5H516v72.5c0 11 9 20 20 20s2
0-9 20-20v-72.5h46.5v72.5c0 11 9 20 20 20s20-9 20-20v-72.5h82.8c38.6 0 70-31.4 70-70V646H865c11 0 2
0-9 20-20s-9-20-20-20h-69.7v-46.5H865c11 0 20-9 20-20s-9-20-20-20h-69.7V473H865c11 0 20-9 20-20s
-9-20-20-20h-69.7v-46.5zM755.3 702.7c0 16.5-13.5 30-30 30H312.2c-16.5 0-30-13.5-30-30v-413c0-16.5 1
3.5-30 30-30h413.1c16.5 0 30 13.5 30 30z"
 />
 <Path
 fill={props.fill6 || "#ff6363"}
 d="M407.6 521.4h50.3l11 28.6h27.6l-50.4-125.8h-26.9l-49 125.8h27zm24.8-67.9 17.3 46.7h-34.3zm103 4
9.1H552q17.25 0 26.4-1.8 6.75-1.5 13.2-6c4.3-3 7.9-7.1 10.7-12.4s4.2-11.8 4.2-19.5c0-10-2.4-18.2-7.3-24.5s-1
0.9-10.4-18.1-12.3c-4.7-1.3-14.8-1.9-30.2-1.9H510V550h25.4zm0-57.1h12.3c9.2 0 15.2.3 18.3.9 4.1.7 7.5 2.6 10.
2 5.6s4 6.8 4 11.3c0 3.7-1 7-2.9 9.8s-4.6 4.9-7.9 6.2c-3.4 1.3-10.1 2-20.1 2h-13.9zm91.1-21.3h25.4V550h-25.4
z"
 />
 </Svg>
 )
};

Table of Contents

Kacper Cyranowski
August 1, 2025
5 min read

Related Posts

Technical
August 1, 2025

Transforming SVG Files into React Components

In the world of modern web and mobile application development, SVG...
Kacper Cyranowski
5 min read
Technical
August 1, 2025

React Native vs Flutter

Mobile app developers often face a choice between two leading technologies – React...
Wiktor Kędzierawski
5 min read
Technical
August 1, 2025

How Many Videos Can Android TV Play Simultaneously?

A comprehensive overview of React Native, its benefits, and its impact on mobile app development.
Wiktor Kędzierawski
5 min read

Don’t risk a bad hire!

Onboarding a new developer costs you $8,000 and takes months
– we provide ready-to-go solutions immediately.