Fixing SVG Rendering Issues on Safari with Expo v51 and React Native


Introduction

When developing mobile applications with React Native using Expo, SVG files are used. However, What I encountered is that certain SVG elements may not render correctly on iOS devices or Safari on macOS.

The Problem

Safari and iOS have specific quirks in how they handle SVG files. While these SVGs work perfectly in most modern browsers and environments, Safari’s handling of them can sometimes lead to incorrect rendering, especially when SVG files are transformed into JSX components in React Native. The transformation process might introduce issues or exacerbate existing ones, making it critical to preprocess the SVG content before it reaches the rendering stage.

Custom SVG Transformer

The custom SVG transformer reads the SVG file, converts it to JSX, and processes it with React Native’s existing SVG transformer. Below is the implementation:

custom-svg-transformer.js
const fs = require("fs");
const svgToJsx = require("svg-to-jsx");
const transformer = require("react-native-svg-transformer");

module.exports.transform = async function ({ src, filename, options }) {
  if (filename.endsWith(".svg")) {
    const svgContent = fs.readFileSync(filename, "utf8");

    const jsxContent = await svgToJsx(svgContent);
    return transformer.transform({
      src: jsxContent,
      filename,
      options,
    });
  }

  return transformer.transform({ src, filename, options });
};
copied!

This custom transformer does the following:

  1. Read the SVG File: It reads the contents of the SVG file as a string.
  2. Convert to JSX: The SVG string is converted into JSX using the svgToJsx package.
  3. Transform for React Native

Configuring Metro

For Expo to use the custom transformer, you need to update the Metro bundler configuration.

metro.config.js
const { getDefaultConfig } = require("expo/metro-config");
const { resolve } = require("path");

/** @type {import('expo/metro-config').MetroConfig} */
const config = getDefaultConfig(__dirname);

const { transformer, resolver } = config;

config.transformer = {
  ...transformer,
  babelTransformerPath: resolve(__dirname, "./custom-svg-transformer.js"),
};
config.resolver = {
  ...resolver,
  assetExts: resolver.assetExts.filter((ext) => ext !== "svg"),
  sourceExts: [...resolver.sourceExts, "svg"],
};

module.exports = config;
copied!

Conclusion

With this approach, SVG files can be reliably used in React Native applications, even when rendering on iOS devices and Safari on macOS. By preprocessing SVGs and converting them into JSX components, you can avoid rendering issues, ensuring a consistent and reliable user experience across all platforms.

buy me a coffee