Tutorials Books Videos Forums

Change the theme! Search!
Rambo ftw!

Customize Theme




Table of Contents

Restarting a CSS Animation

by kirupa   |  filed under Web Animation

Learn how to restart a CSS animation in a way that works across all modern mobile and desktop browsers!

CSS animations have a lot going for them, but there is one thing that they don't support very easily. That thing has to do with restarting them. Once an animation starts running, there is no built-in way to stop that animation and have it start from the beginning. Once an animation has run to completion and it isn't set to loop, restarting that animation isn't easily provided either. That's all good, though. In this article, we'll look at how a few lines of JavaScript can allow us to restart a CSS animation.


The Code

If you are just looking for the one snippet of code you need to add to make restarting a CSS animation possible, then look below:

element.style.animationName = "none";

requestAnimationFrame(() => {
  setTimeout(() => {
    element.style.animationName = ""
  }, 0);

To use this code, replace element with a reference to the DOM element that is being animated. To be more specific, this is the DOM element that has the associated animation or animation-name property set on it in CSS.

Full Example

To see how the above snippet works as part of a real example, take a look at the following (or in a separate window):

Click on the Restart button to have our circle animation start from the beginning. You can keep pressing it, even while the animation is running, to have our animation restart. The full HTML, CSS, and JS that makes this example work is provided below:

<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Restart Animation</title>
    body {
      display: grid;
      place-items: center;

    .circle {
      width: 100px;
      height: 100px;
      background-color: #D72638;
      border-radius: 50%;

      animation-name: scaleUp;
      animation-iteration-count: 1;
      animation-duration: 1s;
      animation-fill-mode: both;
      animation-timing-function: cubic-bezier(.17, .67, .32, 1.55)

    @keyframes scaleUp {
      0% {
        transform: scale(3, 3);

      50% {
        transform: scale(-.5, -.5);

      100% {
        transform: scale(3, 3);

    .container {
      width: 500px;
      height: 500px;
      box-shadow: 0px 10px 20px #666;
      overflow: hidden;
      display: grid;
      place-items: center;
      background-color: #FFFD98;
      border-radius: 10px;
      align-items: end;

    #restartButton {
      margin-bottom: 40px;
      padding: 5px;
      font-weight: bold;
      border: none;
      outline: 2px solid #0880ff;
      border-radius: 5px;
      background-color: #FFF;

    #restartButton:hover {
      outline-width: 5px;
      background-color: #EAF4FF;
<div class="container">
<div class="circle"></div>
<button id="restartButton">Restart</button>
    let restartButton = document.querySelector("#restartButton");
    restartButton.addEventListener("click", restartAnimation, false);

    function restartAnimation() {
      let circle = document.querySelector(".circle");

      circle.style.animationName = "none";

      requestAnimationFrame(() => {
        setTimeout(() => {
          circle.style.animationName = ""
        }, 0);

For added emphasis, our restart animation code is highlighted. If you want to learn more about this example, this article's associated video is just what you need.


The trick behind what we are doing lies in the fact that a CSS animation does all of its animation-like things only when it has keyframes linked with it. When we break that link by setting the animation-name CSS property to none, we stop our animation in its tracks. By setting animation-name to empty quotes, we tell our browser to fall back to the animation-name value set in the CSS originally. To ensure our keyframes are detached in one frame and re-attached in another frame, we insert an artificial delay. We have a bunch of options here, but the approach we used relied on our good friend, requestAnimationFrame with a little bit of setTimeout added to deal with cross-browser quirks. Pretty cool, right?

Just a final word before we wrap up. If you have a question and/or want to be part of a friendly, collaborative community of over 220k other developers like yourself, post on the forums for a quick response!

Kirupa's signature!

The KIRUPA Newsletter

Thought provoking content that lives at the intersection of design 🎨, development 🤖, and business 💰 - delivered weekly to over a bazillion subscribers!


Creating engaging and entertaining content for designers and developers since 1998.



Loose Ends

:: Copyright KIRUPA 2024 //--