ReactHustle

How to Create React Wave Effect Animation using SVG

Jasser Mark Arioste

Jasser Mark Arioste

How to Create React Wave Effect Animation using SVG

Hello, in this tutorial, you'll learn how to create a react wave animation using an SVG element and some CSS.

Introduction #

I wanted to try creating a wave animation background in react. I liked this wave tutorial animation on youtube but it has a few problems. First, it uses a png file for the wave, meaning you can't easily change the background color of the waves unless you change the png file. Second, it is done using plain CSS so it's hard to modify the animation parameters at runtime.

I thought of using an SVG element so that I could modify the color of the waves, but it turns out that when you use background: url(/wave.svg), the currentColor property won't be applied. In this tutorial, we'll learn to work around this limitation.

I also tried react-wavify, and it's a great alternative for creating wave animations in react but, I didn't like the effect when stacking multiple waves so I decided to create my own component.

Tutorial Objectives #

In this tutorial, you'll create dynamic <Wave/> react component that uses an SVG element, then we'll add some props to modify the animation properties and color.

We'll also use NextJS as our react framework as a personal preference of mine, but you can also use create-react-app or other alternatives.

Final Output #

Below is what we'll be making today, the waves are smooth and it's not random like react-wavify. You may also go to Stackblitz for the demo: React Wave Effect Tutorial

React wave animation final output

All right! with that out of the way, let's code!

Step 0 - Project Setup #

First, let's create a new NextJS project by running the command:

npx create-next-app --ts react-wave-effect

Once the installation is complete, you can run the following command to start the local server:

cd react-wave-effect
yarn dev

Step 1 - Creating the <AnimatedWave/> Component #

Next, let's create the AnimatedWave component by creating the file components/AnimatedWave.tsx, and copying the code below. I added comments to explain the code.

// components/AnimatedWave.tsx
import { CSSProperties } from "react";
import { renderToStaticMarkup } from "react-dom/server";
// Step 1. Define the props. you can add other css properties according to your perferences, like zIndex
type Props = {
  color: CSSProperties["color"];
  animationDuration: CSSProperties["animationDuration"];
  animationDirection?: CSSProperties["animationDirection"];
  opacity: CSSProperties["opacity"];
};
const AnimatedWave: React.FC<Props> = ({ color, ...props }) => {
  //  Step 2. create a svg wave, i created this using figma
  const wave = (
    <svg viewBox="0 0 1000 126" xmlns="http://www.w3.org/2000/svg">
      <path
        d="M0 42.9533C178.148 -12.5894 287.4 -13.7474 500 42.9533C682.727 88.1203 798.763 97.7368 1000 42.9533V125.861H0V42.9533Z"
        fill={color}
      />
    </svg>
  );

  // Step 3. render svg to string
  const svgAsString = renderToStaticMarkup(wave);
  // Step 4. encode string to url so that we can use it in the background property
  const encodedWaveSvg = encodeURIComponent(svgAsString);
  return (
    <div
      style={{
        // Step 5. use encodedsvg as background
        background: `url('data:image/svg+xml;utf8,${encodedWaveSvg}')`,
        position: "absolute",
        bottom: 0,
        width: "100%",
        // Step 6. height and background size should match the svg viewBox for a smooth animation
        height: 126,
        backgroundSize: "1000px 126px",
        // animation-name is wave, we'll create this in the next step
        animation: `wave ${props.animationDuration} linear infinite`,
        animationDirection: props.animationDirection,
        opacity: props.opacity,
      }}
    ></div>
  );
};
export default AnimatedWave;
123456789101112131415161718192021222324252627282930313233343536373839404142434445

Basically, we used an SVG element so that we can pass the color property and transformed it into a string using renderToStaticMarkup so that we can use it as a background image for the div element.

Step 2 - Adding keyframes in globals.css #

We already completed the component but, we haven't defined the keyframes for the animation, let's add that in globals.css.

// styles/globals.css
...
@keyframes wave {
  0% {
    background-position-x: 1000px;
  }
  100% {
    background-position-x: 0px;
  }
}
12345678910

Here, we're using the background-position-x property to move the background image, take note that it should also match the backgroundSize property of the component so that it creates a seamless animation.

Step 3 - Usage #

To use the AnimatedWave component, we need to contain it in a position:relative parent element. For example

// pages/index.tsx
import type { NextPage } from "next";

import AnimatedWave from "../components/AnimatedWave";

const Home: NextPage = () => {
  return (
    <div
      style={{ minHeight: "100vh", display: "flex", flexDirection: "column" }}
    >
      <div style={{ flexGrow: 1 }}>Header</div>

      <div style={{ position: "relative" }}>
        <AnimatedWave
          color={"#3E54AC"}
          animationDuration="4s"
          opacity={"0.8"}
        />
        <AnimatedWave
          color={"#3E54AC"}
          animationDuration="12s"
          opacity={"0.5"}
        />
        <AnimatedWave
          color={"#3E54AC"}
          animationDirection="reverse"
          animationDuration="10s"
          opacity={"0.2"}
        />
      </div>
    </div>
  );
};

export default Home;
1234567891011121314151617181920212223242526272829303132333435

Here we have three wave components that have varying duration, direction, and opacity to create the awesome wave effect.

That's basically it!

Full Code #

The full code is accessible on GitHub: React Wave Effect

Conclusion #

We learned how to create a wave effect component in React. I think this code can be improved so that we don't rely on hard-coded height, backgroundSize, and background-position. However, I haven't found the solution to that yet. 

Resources #

Credits: Image by David Yu from Pixabay

Share this post!

Related Posts