Documentation

Documentation

JavaScript

CSS

CSS

It's common to reach for a JavaScript library like Motion when you want to perform spring animations. But Motion is also capable of generating springs via CSS, both on the server and in the browser.

In this guide, we'll learn how to make spring animations with CSS, with a variety of libraries and frameworks:

  • React Server Components

  • Via the style attribute

  • CSS-in-JS (Styled Components, Tamagui)

  • Astro

  • React

  • Vue

We'll also learn how to fall back to either no animation or a different animation for cross-browser support.

Import

To generate our spring CSS rules, we're going to be using Motion's spring() function.

import { spring } from "motion"

Overview

spring has two features that makes it perfect for CSS generation.

  1. A toString() method.

  2. A spring(visualDuration, bounce) shorthand.

toString() returns the spring as a CSS duration and easing. The new shorthand makes it simpler than ever to make springs.

Put together, we can create CSS rules like this:

transition: transform ${spring(0.5, 0.2)};

// Outputs:
// transition: transform 800ms linear(...)

The generated duration can be longer than the one provided to spring because it accepts the new visualDuration option, which makes it easier to edit springs and coordinate them with other transitions:

transition:
  opacity 0.5s ease-out,
  transform ${spring(0.5, 0.2)};

Here, transform will actually take longer, but it will appear to take a similar amount of time to animate as opacity. This is because the visualDuration defines the amount of time the animation takes to first reach its target, not perform the "bouncy bit" after.

Server generation

React Server Components

With React Server Components (RSC), it's possible to set springs via the style prop.

<div style={{ transition: "all " + spring() }}>

This code will be run entirely server-side, with no runtime overhead.

It's also possible to use the style tag:

<style>{`
  button:hover {
    transition: transform ${spring(0.8, 0)};
    transform: scale(1.2);
  }
`}</style>

Client generation

style attribute

You can set transition on an element at runtime, before changing its other values.

const element = document.querySelector("button")

element.style.transition = "transform " + spring(0.3)
element.style.transform = "scale(1.2)"

CSS-in-JS

CSS-in-JS follows the same basic approach of string concatination, with the exact pattern depending on your library of choice:

Styled Components

const Button = styled.button`
  transition: opacity ${spring(0.5)};
`

Tamagui

export const RoundedSquare = styled(View, {
  transition: "opacity " + spring(0.5)
})

Astro

In Astro, you can define the spring as a CSS variable using JavaScript, and then in your CSS use that value with var():

<style define:vars={{ spring: spring(0.2, 0) }}>
  span {
    transition: transform var(--spring);
  }
</style>

Vue

const springTransition = ref(spring(0.3, 1))
<div :style="{ transition: 'filter ' + springTransition }"></div>

Fallbacks

By default, the browser will ignore your animation if it doesn't support the linear() easing function.

In CSS it's possible to set a second transition with a lower specificity to act as a fallback, though this might not be supported by all CSS generators (like setting via style):

transition: filter 0.3s ease-out;
transition: filter ${spring(0.3)};

This is another benefit of the spring() shorthand accepting visualDuration instead of duration - you can use the same duration for both of these animations and they'll feel like they take an equivalent time to complete.

A Motion+ membership will give you early access to features & content, access to our private Github and Discord, and more.

It's a one-time fee that grants lifetime access. No subscription necessary!

MOTION+

Premium React components

Motion+ is a one-time fee, lifetime membership that supports Motion and grants access to the source code of an ever-growing library of examples.


You also gain access to Cursor and AnimateNumber, two exclusive new React components. Check out what they can do: