SVG to React Native with react-native-svg
Go from SVG to React Native with react-native-svg: install it, map elements to imports, set size and color, load .svg files with Metro, and know its limits.
By SVG to React. Published . Updated .
A React component for the web renders lowercase svg and path elements, which the browser turns into DOM nodes. React Native has no DOM and no SVG renderer of its own, so that component cannot run there. The standard answer is react-native-svg. This library provides components with the same names and attributes as SVG elements and draws them with the native graphics APIs on iOS and Android. Moving from SVG to React Native therefore means rewriting the tree in terms of that library. This post walks through the rewrite and the differences that matter in practice.
Install react-native-svg
# Expo: installs the version that matches your SDK
npx expo install react-native-svg
# Bare React Native
npm install react-native-svg
cd ios && pod installThe library includes native code. In Expo, npx expo install picks the version tested with your SDK, which matters more than having the newest release. In a bare app, run pod install after adding the package, then rebuild the native app. A JavaScript reload alone is not enough the first time.
Map each element to an import
Every SVG element becomes a capitalized component imported from react-native-svg: svg becomes the default export Svg, and path, circle, g, defs, linearGradient, and stop become Path, Circle, G, Defs, LinearGradient, and Stop. Attribute names follow the same camelCase rules as JSX, so stroke-width becomes strokeWidth and stop-color becomes stopColor.
Here is a small two-color badge as a web SVG:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<defs>
<linearGradient id="sky" x1="0" x2="0" y1="0" y2="1">
<stop offset="0" stop-color="#38bdf8" />
<stop offset="1" stop-color="#0f766e" />
</linearGradient>
</defs>
<circle cx="12" cy="12" r="10" fill="url(#sky)" />
<path d="m8 12 3 3 5-6" stroke="#fff" stroke-width="2" stroke-linecap="round" />
</svg>And the same drawing as a React Native component:
import * as React from "react";
import Svg, { Circle, Defs, LinearGradient, Path, Stop } from "react-native-svg";
import type { SvgProps } from "react-native-svg";
export function BadgeCheck(props: SvgProps) {
return (
<Svg width={24} height={24} viewBox="0 0 24 24" fill="none" {...props}>
<Defs>
<LinearGradient id="sky" x1="0" x2="0" y1="0" y2="1">
<Stop offset="0" stopColor="#38bdf8" />
<Stop offset="1" stopColor="#0f766e" />
</LinearGradient>
</Defs>
<Circle cx={12} cy={12} r={10} fill="url(#sky)" />
<Path d="m8 12 3 3 5-6" stroke="#fff" strokeWidth={2} strokeLinecap="round" />
</Svg>
);
}The geometry did not change at all. Path data, coordinates, the gradient, and the url(#sky) reference carry over directly. The work is in the element names, the imports, and the attribute names. That is why the conversion is easy to automate. SVGR has a native mode that does exactly this mapping, and the SVG to React Native converter on this site runs SVGR with native mode on. The TypeScript output imports the SvgProps type from react-native-svg.
Two attributes from the web version have no purpose here. xmlns declares an XML namespace for a parser, and className refers to CSS classes, which React Native does not have. Both can be deleted from converted output.
Size and color
width and height on Svg set the size in density-independent pixels, and viewBox scales the drawing into that box, as on the web. Pass them as props so each screen can choose a size.
Color needs more care, because there is no CSS cascade. On the web, currentColor reads the CSS color of a parent element. In react-native-svg, it reads the color prop on the root Svg. A text style on a parent View does not reach it:
import Svg, { Path } from "react-native-svg";
import type { SvgProps } from "react-native-svg";
export function ChevronRight({ size = 20, ...props }: SvgProps & { size?: number }) {
return (
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" {...props}>
<Path d="m9 6 6 6-6 6" stroke="currentColor" strokeWidth={2} />
</Svg>
);
}
// The color prop on Svg is what currentColor resolves to
<ChevronRight color={theme.colors.textMuted} size={16} />This makes theming explicit. Read the color from your theme or from useColorScheme, and pass it down as a prop.
Styles and CSS inside the SVG
Web SVGs sometimes carry their styling in a <style> element or in class selectors. The default components of react-native-svg do not apply CSS rules. The library offers separate CSS-aware components, such as SvgCss, that parse inline CSS at runtime. A simpler fix is to move the styles into presentation attributes before converting. An optimizer pass can do this for many files. SVGO's preset-default includes inlineStyles, which moves rules from a <style> element onto the elements they match, where it can do so safely. The SVG optimizer runs that preset.
Importing .svg files with Metro
Converted components are plain code that you commit. If you would rather keep .svg files in the repository and import them, Metro needs a transformer, because it treats .svg as a static asset by default. react-native-svg-transformer is the common choice:
// metro.config.js (Expo), following the react-native-svg-transformer README
const { getDefaultConfig } = require("expo/metro-config");
module.exports = (() => {
const config = getDefaultConfig(__dirname);
const { transformer, resolver } = config;
config.transformer = {
...transformer,
babelTransformerPath: require.resolve("react-native-svg-transformer/expo"),
};
config.resolver = {
...resolver,
assetExts: resolver.assetExts.filter((ext) => ext !== "svg"),
sourceExts: [...resolver.sourceExts, "svg"],
};
return config;
})();
// Then, in a component file
import Logo from "./assets/logo.svg";
<Logo width={120} height={40} />Bare React Native projects use the same idea with the @react-native/metro-config defaults. Check the transformer's README for the exact file, because the Expo and bare setups differ. In TypeScript, add a declaration for *.svg that types the default export as a component taking SvgProps.
SVG strings at runtime
Sometimes the SVG is data rather than code, for example an icon sent by an API. SvgXml renders an XML string, and SvgUri fetches and renders a file from a URL:
import { SvgXml } from "react-native-svg";
const markup = `<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" fill="#0f766e"/></svg>`;
export function RemoteBadge() {
return <SvgXml xml={markup} width={24} height={24} />;
}Both parse at runtime, which costs time on every mount and fails quietly on malformed markup. Use them for content that really is dynamic, and ship converted components for your own icons. Treat markup from users as untrusted, as you would on the web.
Accessibility
Screen readers on iOS and Android read the accessibility props of React Native views. An SVG <title> element is not a reliable label. For a meaningful icon, wrap it in a view that has a role and a label. Leave decorative icons unlabeled inside a control that has its own label:
import { View } from "react-native";
<View accessible accessibilityRole="image" accessibilityLabel="Payment verified">
<BadgeCheck />
</View>Known differences from the browser
- Rendering is done by
react-native-svg, not by a browser engine. Support for filters, masks, patterns, and text layout depends on the library version, so check its documentation for each feature your artwork uses. foreignObject, scripts, and CSS animations from the file do not behave as they do on the web. Animate with React Native's animation APIs or a library that supportsreact-native-svgprops.- Very large drawings with many nodes cost memory and render time on low-end devices. Simplify them, or use a PNG for complex illustrations that never change.
For one file, paste it into the SVG to React Native converter. For a folder, the batch tool has a React Native format that runs the same native mode for each file. The web side of the same icon set is covered in SVG to React Component: A Practical Guide.
Questions
Can I render a web SVG component in React Native?
No. React Native has no DOM, so lowercase svg and path elements do not exist there. The tree has to use the components exported by react-native-svg, such as Svg and Path. The web component and the native component can share path data, but not the element tree.
Does currentColor work in react-native-svg?
Yes. Set fill or stroke to currentColor on the shapes and pass a color prop to the root Svg component. There is no CSS cascade in React Native, so the value comes from that prop rather than from a parent's color style.
Can I import .svg files directly in a React Native app?
Not out of the box. Metro treats .svg as an asset. The react-native-svg-transformer package changes the Metro config so an .svg import returns a component. The alternative is to convert each file to a component ahead of time and commit the result.
What about an SVG string loaded at runtime?
react-native-svg exports SvgXml, which parses an XML string, and SvgUri, which fetches a remote file. Both parse at runtime, so treat the content as untrusted if it comes from users, and prefer converted components for icons you ship with the app.