React & SVGs: 4 ways to use them

SVG image files are fun. They provide a lightweight alternative to bitmap files. An SVG file is a text representation of a vectorial image (SVG stands for Scalable Vector Graphics) and as such, it increases in size not with the resolution but with the amount of detail. A simple SVG file can have a file weight of 1 or 2 kb where a corresponding PNG file would be at least 10 times the size. (I've also seen cases where complex SVG files are over 1Mb in weight and where the corresponding PNG raster was 25% of the size, but that is another story).

SVGs files have a number of other advantages: They are scaleable (that is, they display nicely at any resolution). As they are text-based, they are easily editable and suited both to being indexed by search engines (and so to SEO) and to localization or translation. They also support fun features like CSS styling, and animation (via CSS or otherwise).

All that is cool, but it's nothing to get excited about as pertains to React. The fun starts with the fact that since SVG files are just text, they can be created in React JS just like any other text output.

Bearing that in mind, here are the four ways to integrate SVGs into your React output.

1 — Same as any other image

Just as you would do with any other image file, you can include SVG files in React JS using the image tag, e.g.

    <img src="http://files.cdn.com/nice-icon.svg" /> 

This has the advantage of being easy to implement and the inconvenience of requiring a network call which slows everything done. We can do better!

2 — Include the .svg file locally in the image

If you have initialized your React app with create-react-app (CRA) it is configured to allow you to import images files directly in the code and use them in the src attribute of the image tag like so:

    import React from 'react';
    import logo from './logo.svg';

    function App() {
     return (
       <div>
        <img src={logo} alt="logo" />
       </div>);
    }

This is cleaner to write. The generated HTML ends up looking something like this:

    <img src="/build/images/logo.6ce24c58.svg" alt="logo">

The CRA documentation states that when an image file is less than 10kb, is automatically inlined. However, this does not apply to SVG files (for more details this discussion). This means that we're still adding a network request and not getting the most out of our SVGs.

3 — Using SVGR to create Components

CRA version 2 (and up) comes with SVGR already included. This library allows you to include .svg files as if they were components.

    import React from 'react';
    import { ReactComponent as Logo } from './logo.svg';
    import './App.css';

    function App() {
      return (
        <div>
         <Logo />
        </div>);
    };

In this case, React renders the SVG directly without fetching a file, i.e. the output looks like this :

    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
      <g fill="#61DAFB">
        <path d="M666.3 296.5c0-32.5 (etc.)"></path>
        <circle cx="420.9" cy="296.5" r="45.7"></circle>
        <path d="M520.5 78.1z"></path>
      </g>
    </svg>

For a lot of use cases, this is sufficient. But as you can see, the SVG file has the same kind of (XML) structure as HTML does, and React can update that kind of data too.

Key Takeways

If we open up the logo SVG file in the text editor we can see that has the same contents as the content we saw in the render previously. We can easily create a component that emits the same content :

    import React from 'react';

    const Logo: React.FC = () => { 
      return(
       <svg viewBox="0 0 841.9 595.3">
        <g fill="#61DAFB">
         <path d="M666.3 296.5c0-32.5-40.7 (etc.)"/>
         <circle cx="420.9" cy="296.5" r="45.7"/>
         <path d="M520.5 78.1z"/>
        </g>
      </svg>);
    };

This allows us to change parts of the SVG render like we do any other content emitted by a React component, using the state or props for example :

    import React, { useState } from 'react';

    export const Logo: React.FC = () => {
      const [active, setActive] = useState(true);

      const click  = () => {
        setActive(!active);
      };

      return <svg onClick={click} viewBox="0 0 841.9 595.3">
        <g fill={active ? '#61DAFB' : '#99AABB'}>
          <path d="M666.3 296.5c0-32.5-40.7 (etc.)"/>
          <circle cx="420.9" cy="296.5" r="45.7"/>
          <path d="M520.5 78.1z"/>
          </g>
        </svg>;
    };

This is not of course the most complicated of containers, but it shows all the possibilities that are available to us.

0
Social
Made by kodaps · All rights reserved.
© 2023