How to Create React Wave Effect Animation using SVG
Jasser Mark Arioste
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
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