SVGs in React (almost everything you need to know)
Published: 2024-04-21 04:16:06

SVG stands for **Scalable Vector Graphic**. True to its name, SVGs are infinitely scalable because they use XML syntax. This means they're essentially a set of instructions for the browser (or any program that can interpret them) to render the image. Unlike raster images (like JPEGs), SVGs don't have a fixed number of pixels, so they can be scaled to any size without losing quality.

<aside>
💡 **In the pre-JSON era, XML (Extensible Markup Language)** was the primary way to store and transmit data on the web. It provides a set of rules for encoding and reconstructing various data formats. Interestingly, both HTML and SVG use XML syntax with tags and attributes. You can even style SVG elements with CSS just like you would style regular HTML elements!
</aside>

## **Real-world Scenario: Rendering SVGs in React**

Let's explore different ways to render SVGs in your React projects.

<aside>
💡 Code mentioned in this article, can be found in this repository
[GitHub - vavilov2212/render-svg-example](https://github.com/vavilov2212/render-svg-example)
</aside>

### **Rendering SVGs as Image Tags**

One approach is to treat SVGs as external resources. This way, you don't need any additional configuration, and you still benefit from SVG's scalability. Simply import the path to your SVG file and use it as the `src` attribute of an `img` tag (the file must be publicly accessible).

```jsx
import React, { Component } from "react";
import Logo from "../public/logo.svg";

import "./styles.css";

class App extends Component {
  render() {
    return (
      <div>
	    <h1> Hello, World! </h1>
        <img src="logo.svg" className="svg" alt="My SVG Image" />
      </div>
    );
  }
}

export default App;
```

![Rendering SVGs as Image Tags](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fg74a5vxa19xyeup8rpf.png)

**Important Note:** In this approach, only attributes applicable to the `img` tag itself will have any effect. You won't be able to directly style the underlying SVG elements within the image.

### **Rendering SVGs "as is" in React**

Imagine you want your React components to be fully built and ready to render at initial page load, without waiting for external images to download. While simply including the SVG code within your component's markup might seem like an option, it's inefficient. We want to store static resources like SVGs separately from our JavaScript code.

However, there's a catch. When you try to import an SVG file into your component, JavaScript module resolution expects valid JavaScript code, but SVGs are written in XML. Here's where build tools like Webpack come in to help!

![Import error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9gsabzjwvz8090qilrs8.png)

<aside>
💡 For deeper insights of what’s going to happen further, i recommend watching this 10 min video [Tech Talk: RegEx (SVG) & JSX: Adapting Vector Graphics for React](https://www.youtube.com/watch?v=HZcXHNrkZKw). In my dev practice, i’ve done exactly this a couple of times before switching to webpack automation.

</aside>

### **Webpack to the rescue!**

Webpack can be configured to preprocess SVGs during the build process, transforming them into React components that you can easily use within your application. To achieve this, we'll leverage a package called `@svgr/webpack`.

Let’s edit webpack.config file to automatically transform svg into react component during build time. Install [@svgr/webpack](https://github.com/gregberge/svgr) and follow [usage guide](https://react-svgr.com/docs/webpack/#usage).

```jsx
// webpack.config.js

module: {
  rules: [
    {
      test: /\.svg$/i,
      issuer: /\.[jt]sx?$/,
      resourceQuery: { not: [/url/] }, // exclude react component if *.svg?url
      use: ["@svgr/webpack"],
    },
  ]
}
```

**Explanation:**

- `test: /\.svg$/i`: This line tells Webpack to identify files ending with the `.svg` extension (case-insensitive).
- `issuer: /\.[jt]sx?$/`: This line ensures the rule applies only when the SVG is imported within a JavaScript or TypeScript file (`.js`, `.jsx`, `.ts`, or `.tsx`).
- `resourceQuery: { not: [/url/] }`: This line excludes SVGs used with the `url()` loader (used for embedding small SVGs directly in CSS).
- `use: ["@svgr/webpack"]`: This line specifies the `@svgr/webpack` loader to handle SVG processing.

### **Using SVG Components:**

Once Webpack processes your SVGs, you can import and use them as React components:

```jsx
// src/App.js

import Logo from "path/to/logo.svg";
import "path/to/styles.css";

...

return (
  ...
  <Logo className="svg" />
  ...
)
```

### **Styling SVG Components:**

By transforming SVGs into React components using SVGR, you gain the ability to style the underlying SVG elements directly within your CSS.

```jsx
// styles.css

.svg ellipse {
  stroke: red;
}
```

This CSS code will target all `ellipse` elements within the `Logo` component you imported, making their stroke red.

![Styling SVG Components](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9sfu3lfqrwy8pc30397s.png)


As you can see, SVGR processed our import making it possible to simple call it as function and pass props to it.

## SVG optimisation with SVGR (utilising SVGO)

SVGR package uses [SVGO](https://github.com/svg/svgo/) to optimise SVG code before transforming it into react component. Utilising this may help you to dramatically reduce bundle size.

Refer to the [configuration docs](https://react-svgr.com/docs/options/#svgo) for available options or use both packages separately.

## SVGs as Data URLs

This technique allows to store SVGs as strings using [Data URL scheme](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs), for example:

```jsx
.some-div {
  background: url("data:image/svg+xml,<svg-string>");
}
```

Refer to this helpful article [Rendering SVGs as images directly in React](https://medium.com/@ians/rendering-svgs-as-images-directly-in-react-a26615c45770)

## **Advanced Topics. SVG Sprites**

SVG sprites are a technique for combining multiple SVGs into a single image file. This can improve performance by reducing the number of HTTP requests needed to render multiple SVGs on a page. However, managing complex SVG sprites can become cumbersome.

Refer to this helpful article: [Complete guide to SVG sprites](https://medium.com/@hayavuk/complete-guide-to-svg-sprites-7e202e215d34)

With webpack we can use [svg-sprite-loader](https://github.com/JetBrains/svg-sprite-loader) and it can be used in conjunction with other loaders like so:

```jsx
// webpack.config.js v.4
const path = require("path");

module: {
  rules: [
    {
      test: /\\.svg$/,
      include: path.resolve(__dirname, "src"),
      loaders: [
        `svg-sprite-loader?${JSON.stringify({
          name: "[name].[hash]",
          prefixize: true,
        })}`,
        `svgo-loader?${JSON.stringify({
          plugins: [
            { removeTitle: true },
            { convertPathData: false },
            { removeUselessStrokeAndFill: true },
          ],
        })}`,
      ],
    },
 ]
}
```

### **Further Resources:**

- [SVGOMG](https://github.com/jakearchibald/svgomg) - Online tool for optimizing SVGs
- [SVG Path Visualizer](https://github.com/topics/svg-path) - Tool to visualize and understand SVG paths

## **Conclusion**

By understanding the different approaches to using SVGs in React and leveraging tools like SVGR and Webpack, you can effectively integrate scalable vector graphics into your React applications. This guide has equipped you with the knowledge to:

- Render SVGs as images or React components
- Style SVG elements directly within your components
- Optimize SVGs for better performance
- Consider best practices and explore advanced techniques

I hope this guide empowers you to create visually appealing and performant React applications using SVGs!