DEV Community

Joseph Buchma
Joseph Buchma

Posted on • Edited on

Export SVGs from Figma for React Native

This post is focused on React Native, but should be applicable to pretty much anything with little adjustment.

If you have your UI designed in Figma (or any other vector graphics tool), you likely want to export all the assets for your app from there. So here's what you do to get SVGs out ASAP.
In this particular example I want to export all my icons as SVGs

Export SVGs from Figma

  • Make sure your shapes do not use stroke. To get rid of it:
    • try using “outline stroke” and then merging with original shape.
  • Make sure all shapes are flat (flatten all groups)
  • Give all your shapes meaningful and unique names
  • Make all shapes black #000
  • Export SVGs using “Advanced SVG Export” plugin:
    • Select all shapes you want to export and then open "Advanced SVG Export" plugin
    • Make sure you selected “Prefer viewBox to width/height” in plugin settings
    • Click on "export layers"
    • Extract downloaded archive into assets/icons directory of your project

Generate React Component For Each SVG

I use TypeScript, so following generates app/components/icons/index.tsx file with component per each SVG.

  • Install hygen - follow docs
  • Run hygen generator new icons
  • Change _templates/icons/new/hello.ejs.t to:

---
to: app/components/icons/index.tsx
---

/**********************************************
 *    THIS IS AUTOGENERATED CODE              *
 *    GENERATOR: hygen icons new              *
 **********************************************/

<% 
  const dir = './assets/icons';
  const fs = h.require('fs');

  const files = fs.readdirSync(dir)
%>

<%- 
  files.map(v => `import ${v.split('.')[0]}SVG from "../../../assets/icons/${v}"`).join('\n')
%>

import {CreateIcon} from './createIcon';

<%- 
  files.map(v => `export const ${v.split('.')[0]}Icon = CreateIcon(${v.split('.')[0]}SVG)`).join('\n')
%>

It's nice to have CreateIcon function in separate file, so you can edit it without touching template. Here's what createIcon.tsx may look like:

import React from 'react';
import {View} from 'react-native';
import {SvgProps} from 'react-native-svg';

export interface IconProps {
  containerStyle?: object;
  color: string;
  size: number;
}

export function CreateIcon(Icon: React.FC<SvgProps>) {
  return ({color, size, containerStyle}: IconProps) => {
    return (
      <View
        style={[
          {
            width: size,
            height: size,
            justifyContent: 'center',
            alignItems: 'center',
          },
          containerStyle,
        ]}>
        <Icon fill={color} width={size} height={size} />
      </View>
    );
  };
}
  • Add "g:icons": "HYGEN_OVERWRITE=1 hygen icons new", to "scripts" in package.json

With above setup you extract all your svgs into assets/icons, then run yarn g:icons (or npm run g:icons) which will generate app/components/icons/index.tsx with one component per each SVG file. Yay!

RN Setup

  • Install following npm packages: react-native-svg, @svgr/core, @svgr/plugin-svgo, react-native-svg-transformer, svgo
  • Create .svgrrc file in the project root directory with following content:
{
  "replaceAttrValues": {
    "#000": "{props.color}"
  }
}
  • Adjust metro.config.js as following:
const {getDefaultConfig} = require('metro-config');

module.exports = (async () => {
  const {
    resolver: {sourceExts, assetExts},
  } = await getDefaultConfig();
  return {
    transformer: {
      babelTransformerPath: require.resolve('react-native-svg-transformer'),
      getTransformOptions: async () => ({
        transform: {
          experimentalImportSupport: false,
          inlineRequires: false,
        },
      }),
    },
    resolver: {
      assetExts: assetExts.filter((ext) => ext !== 'svg'),
      sourceExts: [...sourceExts, 'svg'],
    },
  };
})();
  • If you use Typescript, create/edit @types/declarations.d.ts:

     declare module '*.svg' {
       import {SvgProps} from 'react-native-svg';
       const content: React.FC<SvgProps>;
       export default content;
     }
    

That's It!

Hope that was helpful!
Any questions / suggestions are welcome :)

Top comments (0)