npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@leonardosalasd/webmotion

v0.1.0

Published

Beautiful, lightweight, editable animation components for React, so every element can enter, move, and leave with intention.

Downloads

105

Readme

WebMotion

Beautiful, lightweight, editable animation components for React.

Native apps move with intention. You always see where things come from and where they go. The web usually just pops them into place. WebMotion brings that sense of motion to your app with a handful of components you can drop in and forget about.

npm version bundle size license types PRs welcome sponsor

Why

Most things on a page appear out of nowhere. A card, a modal, a list, all there one frame and gone the next. WebMotion gives every element a way to enter, move, and leave so the interface feels like it has weight and direction, the way good mobile apps do, without you hand-writing keyframes every time.

Highlights

  • Tiny and dependency-free. The whole library is a few KB gzipped and ships zero runtime dependencies beyond React.
  • Hybrid engine. By default it rides the browser's Web Animations API, even baking springs into a linear() easing so there's no per-frame JavaScript. When you need a real, interruptible spring (gestures, live values), reach for the useSpring hook.
  • Drop-in components. FadeIn, SlideIn, ScaleIn, BlurIn, Reveal, Stagger, plus a Motion primitive when you want full control.
  • Editable by design. Every variant, easing, and spring preset is exported. Read them, tweak them, or register your own.
  • Accessible by default. It respects prefers-reduced-motion and renders the final state instead of animating when someone asks for less motion.
  • Typed end to end. Written in TypeScript, with autocomplete for every prop and preset.

Install

pnpm add @leonardosalasd/webmotion
npm install @leonardosalasd/webmotion
yarn add @leonardosalasd/webmotion

React 17, 18, and 19 are all supported (it's a peer dependency).

Quick start

import { FadeIn, SlideIn, Stagger } from '@leonardosalasd/webmotion';

export function Hero() {
  return (
    <section>
      <SlideIn direction="up">
        <h1>Everything arrives with intention</h1>
      </SlideIn>

      <FadeIn delay={150}>
        <p>No more elements blinking into existence.</p>
      </FadeIn>

      <Stagger animation="fade-up" interval={80}>
        <Card />
        <Card />
        <Card />
      </Stagger>
    </section>
  );
}

That's it. Each component animates itself in on mount, or as it scrolls into view, and gets out of your way otherwise.

Playground

Want to see everything move before you install anything? Clone the repo and run the local playground:

pnpm install
pnpm -C playground install
pnpm -C playground dev

It opens a page that shows every component and preset, with a button to replay the animations. The playground imports the library straight from source, so any edit you make shows up live.

Components

Motion

The primitive the others are built on. Pass a named animation or your own from/to states.

<Motion animation="zoom" spring="bouncy">
  <Badge>New</Badge>
</Motion>

// or fully custom
<Motion
  from={{ opacity: 0, transform: 'translateY(40px) rotate(-4deg)' }}
  to={{ opacity: 1, transform: 'translateY(0) rotate(0deg)' }}
  duration={600}
>
  <Panel />
</Motion>

FadeIn, SlideIn, ScaleIn, BlurIn

Ergonomic shortcuts for the entrances you reach for most.

<FadeIn>Soft and simple</FadeIn>
<SlideIn direction="left" distance={32}>From the side</SlideIn>
<ScaleIn spring="snappy">Grows into place</ScaleIn>
<BlurIn blur={12}>Sharpens into focus</BlurIn>

Reveal

Motion with the scroll trigger turned on. Anything inside animates as it enters the viewport.

<Reveal animation="fade-up" once amount={0.3}>
  <Testimonial />
</Reveal>

Stagger

Plays its children one after another. Give it an animation and it applies it to each child, spacing out their delays so a list cascades instead of landing all at once.

<Stagger animation="fade-up" interval={60} trigger="inView">
  {items.map((item) => (
    <li key={item.id}>{item.label}</li>
  ))}
</Stagger>

Shared props

These work on Motion and every component built on it:

| Prop | Type | Default | What it does | | ------------------------ | ------------------------------ | ----------- | -------------------------------------------------- | | animation | VariantName | "fade" | Which built-in entrance to use. | | from / to | MotionKeyframe | — | Roll your own states instead of a variant. | | duration | number (ms) | 500 | How long the animation runs. | | delay | number (ms) | 0 | Wait before it starts. | | easing | Easing | "easeOut" | A preset name, a raw CSS easing, or { spring }. | | spring | SpringPreset \| SpringConfig | — | Use a spring instead of an easing curve. | | trigger | "mount" \| "inView" | "mount" | Animate on mount or when scrolled into view. | | once | boolean | true | For in-view triggers, only animate the first time. | | amount | number (0–1) | — | How much must be visible to count as in view. | | as | ElementType | "div" | Render as a different element. | | disabled | boolean | false | Skip the animation and show the final state. | | onStart / onComplete | () => void | — | Lifecycle callbacks. |

Built-in variants

fade, fade-up, fade-down, fade-left, fade-right, scale, scale-up, zoom, pop, blur, rotate, flip-x, flip-y.

import { variants, getVariant } from '@leonardosalasd/webmotion';

// peek at what a variant resolves to
const { from, to } = getVariant('fade-up', { distance: 48 });

Easings and springs

import { easings, springs } from '@leonardosalasd/webmotion';
  • Easing presets: linear, ease, easeIn, easeOut, easeInOut, emphasized, anticipate.
  • Spring presets: gentle, smooth, snappy, bouncy, wobbly, stiff.

Springs are baked into a CSS linear() easing so the browser plays them with no JavaScript running each frame. You can bake one yourself:

import { springToLinearEasing } from '@leonardosalasd/webmotion';

const { easing, duration } = springToLinearEasing({ stiffness: 260, damping: 14 });

Hooks

For the cases the components don't cover.

useMotion

The engine behind every component. Give it two states and timing, get back a ref and the initial style.

import { useMotion } from '@leonardosalasd/webmotion';

function Toast() {
  const { ref, style } = useMotion({
    from: { opacity: 0, transform: 'translateY(12px)' },
    to: { opacity: 1, transform: 'translateY(0)' },
    spring: 'snappy',
  });
  return (
    <div ref={ref} style={style}>
      Saved
    </div>
  );
}

useSpring

A real, interruptible spring that runs in JavaScript. The returned number chases its target, so change the target whenever you want and it redirects mid-flight. Great for gestures and values you map straight onto style.

import { useSpring } from '@leonardosalasd/webmotion';

function Follower({ x }: { x: number }) {
  const sx = useSpring(x, 'wobbly');
  return <div style={{ transform: `translateX(${sx}px)` }} />;
}

useInView

Reports when an element scrolls into view, with once, amount, and margin options.

useReducedMotion

Tells you whether the visitor has asked for less motion, so you can branch your own UI.

Accessibility

WebMotion checks prefers-reduced-motion for you. When it's set to reduce, components skip the animation and render their final state, so the content is still there, just calmer. You don't have to do anything to get this behavior.

Server-side rendering

It's SSR-safe. Components render their starting state on the server and animate once they hydrate in the browser, so there's no flash of the final state.

Roadmap

  • Exit animations and presence handling for elements leaving the tree
  • Gesture helpers built on useSpring
  • A live playground to preview and copy variants
  • Adapters for other frameworks on top of the same core

Ideas and PRs for any of these are very welcome.

Contributing

Contributions are open to everyone. Check out CONTRIBUTING.md for how to set up the project, the scripts you'll use, and what makes a change easy to merge. First-timers are genuinely welcome.

Sponsor

If WebMotion saves you time, you can support its development through GitHub Sponsors. Every bit helps keep it moving.

License

MIT © Leonardo Salas