Animating complex SVG in react

Working with SVG in React

Dealing with SVG in React for me has always boiled down to importing the logo or an icon into component and using it in the src attribute of the image.

...
import logo from './logo.svg'

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

In this blog-post I will cover another way of working with SVG elements in React and show you the ways to animate complex SVGs using styled-components. All the necessary code is provided in the end of the post via Codesandbox.

The way of using SVG in the example above limited the possibilities of styling individual SVG layers and required to explicitly declare SVG in component to utilize all the benefits of styled-components as can be seen from the example below.

import styled from "styled-components";

const StyledCircle = styled.svg`
  fill: red;
`;

const circle = (
  <StyledCircle height="100" width="100">
    <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
  </StyledCircle>
);

function App() {
  return <div className="App">{circle}</div>;
}

Importing SVGs as React components

After one of the latest releases of React it’s finally possible to import SVG as a React component and use styled helper to style it instead.

import { ReactComponent as ImportedComponent } from "./logo.svg";

const StyledSVG = styled(ImportedComponent)`
  display: block;
  margin: auto;
  width: 25em;
  height: 25em;
`;

function App() {
  return (
    <div className="App">
      <StyledSVG />
    </div>
  );
}

Now it’s not necessary to have a full-blown SVG code in your components and animating parts of SVG is as simple as using css transitions for specific parts of our icon which can be achieved by using class names. Of course the most fun part of SVG animations is the possibility to animate individual paths, shapes and texts.

Let’s try to recreate React logo animation from latest version of create-react-app boilerplate with styled-components and using SVG as a React Component rather than <img>.

Preparing working environment

In this tutorial we use simple example of create-react-app. Be sure to use version later than 2.0.0 of create-react-app to follow along. To bootstrap a project with create-react-app follow simple instructions from this repo or just run the commands from the next code-block in terminal of your choice. Note that you need to have Node installed to use npm commands.

npm install -g create-react-app
create-react-app animating-svg
cd animating-svg
npm start

This will create a new React project we will work with. If you have never worked with React I highly encourage you to check out the docs so you can better follow the next steps.

Animating react logo with styled-components

In App.js import styled components and import React logo as React Component.

import { ReactComponent as ReactLogo } from './logo.svg'
import styled from 'styled-components'

Declare styled version of our Logo where we will write our styles. We will add some CSS to adjust size and position.

import React, { Component } from 'react';
import { ReactComponent as ReactLogo } from './logo.svg'
import styled from 'styled-components'

const StyledLogo = styled(ReactLogo)`

height:25rem;
width:25rem;
display:block;
margin:auto;

`

Delete img logo and use our StyledLogo instead of it.

...
import styled from 'styled-components'

const StyledLogo = styled(ReactLogo)`
height:25rem;
width:25rem;
display:block;
margin:auto;
`

class App extends Component {
  render() {
    return (
      <StyledLogo />
    );
  }
}

export default App;

Nothing is supposed to change by now except lack of rotating animation. We can quickly fix that by importing keyframes helper from styled-components and add this simple animation.

...

import styled, { keyframes } from 'styled-components'

const rotate = keyframes`
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
`;

const StyledLogo = styled(ReactLogo)`
animation: ${rotate} infinite 20s linear;
height:25rem;
width:25rem;
display:block;
margin:auto;
`

class App extends Component {
  render() {
    return (
      <StyledLogo />
    );
  }
}

export default App;

Now you see that everything look as good as it looked originally. You may ask

Why would we even import SVG like that as it makes the workflow more complex?

The answer is simple, if you are a fan of styled-components and love animations it’s really easy to animate complex SVGs (ones that consist of several elements as I’ve already mentioned above). Moreover we get almost unlimited flexibility in terms of adjustment of the animations and any other CSS styles based on props!

Animating individual SVG elements

We are lucky and can proceed with tutorial with the same Logo as it has couple of building blocks that can be animated separately. Namely those are circle and lines that together form react logo.

Let’s define the SVG parts we want to animate and give appropriate class names to them. In logo.svg lets add the following classes that correspond to elements of our SVG.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3">
    <g fill="#61DAFB">
		<path class='lines'  
		d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 

		... see the rest of svg code in logo.svg in your react project

		"/>
        <circle class='circle' cx="420.9" cy="296.5" r="45.7"/>
        <path d="M520.5 78.1z"/>
    </g>
</svg>

Now we are ready to add two keyframes that we will use for those two blocks of our SVG. In App.js let’s add the following keyframes and use them in our StyledLogo declaration.

...

const rotate = keyframes`
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
`;

const fade = keyframes`
0% {
fill:#61DAFB;
}
50% {
 fill:#999;
}
100%{
  fill:#61DAFB;
}
`;

const pulse = keyframes`
0% {
  transform: scale(0);
  opacity: 1;
  transform-origin: center;
}
100% {
  transform: scale(4.5);
  opacity: 0;
  transform-origin: center;
}
`;

const StyledLogo = styled(ReactLogo)`
animation: ${rotate} infinite 20s linear;
height:25rem;
width:25rem;
display:block;
margin:auto;
.lines{
  animation: ${fade} infinite 8s linear;
		}

.circle{
  animation: ${pulse} infinite 4s linear;
  &:hover{
    animation-play-state: paused;
    cursor:pointer;
}
}
`

Note how we added animation-play-state: paused to trigger on our circle hover. It’s here to show that although SVG is grouped, all the animations that correspond to specific class are independent and won’t ruin neither animation applied to the group like rotate nor to specific parts of it like fade and pulse. To test it just hover the inner circle of the logo and you will see that only that animation will stop.

Wrap up

So in this tutorial we covered the new way of importing SVG, basic animations with styled-components and css-transitions and the way to independently animate individual parts of our complex SVG component. Keep in mind that using classes with styled-components is not the most preferred way and you could achieve the same result by explicitly declaring SVG in the component and use SVG selectors to create styled versions of all paths and shapes like styled.path or styled.circle. Using our example from the start it would look like this:

import styled, { keyframes } from "styled-components";

const pulse = keyframes`
0% {
  fill:#10aded;
  stroke-width:10px
}
50% {
  fill:#bada55;
  stroke-width:2px
}
100%{
  fill:#10aded;
  stroke-width:10px
}
`;
const StyledCircle = styled.svg`
  height: 25rem;
  width: 25rem;
  display: block;
  margin: auto;
`;
const StyledInnerCircle = styled.circle`
  animation: ${pulse} infinite 4s linear;
  fill: blue;
`;

const circle = (
  <StyledCircle height="100" width="100">
    <StyledInnerCircle cx="50" cy="50" r="40" stroke="black" stroke-width="3" />
  </StyledCircle>
);

class App extends React.Component {
  render() {
    return <div>{circle}</div>;
  }
}

However it’s more verbose and requires to keep SVG code directly in the component.

As promised here is the code! I’ve also added configurability to our animations by creating an arrow function that accepts props and returns styled-components keyframes. This allows to set the ad hoc values for specific CSS properties. But as it is more complex topic I will write another post dedicated to it, for now feel free to look at this code and use it if you find it helpful!


Thanks for reading! I hope you’ve enjoyed reading this post as much as I’ve enjoyed writing it! If you have any questions or want to bring up a discussion don’t hesitate to reach out to me on twitter.

I highly encourage you to try styled-components for styling react applications, I’ve been using it for the past year and can say that I’m in love with this technology!


Comments section

Dimitri Ivashchuk

Written by Dimitri who lives and works in Vienna building useful things. He is passionate about modern web technologies and enjoys learning something new everyday as well as sharing his knowledge with others.

Front-end engineer @CubeTech
Instructor @egghead.io