Motion doesn't need to do much to feel good. Sometimes it's a scale change when pressing the button, a checkmark drawing itself, or an indicator moving smoothly from one tab to another.
These details are easy to overlook when they're missing, but they can make an interface feel noticeably different when they're done well.
I started building shadcn-animated because I wanted reusable, production-quality micro-interactions that felt right.
Something I could use across different projects without need rebuild the same animations every time.
My thought process behind motion
I wanted animations that would fit into different interfaces without needing to be restyled for each project.
Some animation libraries lean into expressive effects, like button text shifting on hover. These can look great in the right context, but they don't always fit the interface you're building.
With shadcn-animated, I wanted to take a different approach. The animations should feel natural without drawing too much attention to themselves or imposing a particular style on the UI.
Finding the right feel
I experimented with different motion patterns to find what felt right. A few things made the biggest difference: duration, easing, balance, and performance.
Duration
Most interactions should feel fast, but not so fast that the movement becomes hard to follow.
The right duration depends on the interaction. A button press should feel almost immediate, while expanding a card usually needs more time because there's more visual change to communicate.
To keep durations consistent, I introduced three timing tokens: fast, medium, and slow. This made it easier to choose a duration based on the interaction rather than tuning every animation from scratch.
Easing
I initially used ease-out for most animations. But after experimenting with different options, I found that spring animations often felt more natural. They worked particularly well for small interactions, like the radio button animation.
Springs didn't work equally well for everything, though. When I tried them on tabs, the indicator felt too bouncy as it moved between items. I wanted the transition to feel smooth and responsive without drawing too much attention to itself, so a simpler easing curve worked better.
Balance
It's easy to overdo motion.
An animation might feel good on its own but become distracting when combined with other effects.
I ran into this with button interactions. I already had a hover animation that slightly increased the button's size. When I added a click animation, the two effects felt excessive together. I removed the hover scaling and the default background-color change on hover.
With fewer effects competing for attention, the click animation felt much better.
Performance
I also wanted the animations to run smoothly. Animating certain properties, such as width or height, can trigger layout recalculations, so I paid attention to which properties each interaction changed.
I preferred approaches using properties like transform and opacity, which browsers can often animate more efficiently. The goal was to make the interactions feel good without introducing unnecessary work for the browser.
Keeping familiar API
I built shadcn-animated on top of shadcn/ui, so developers can use the components they're already familiar with. I wanted the API to feel familiar, too.
For existing components, I kept the shadcn/ui API unchanged, so adding motion wouldn't require learning a new way to use them. For new components, I followed similar patterns and abstractions to make them feel like a natural part of the library.
import { FabMenu, FabMenuContent, FabMenuItem, FabMenuTrigger } from "@/components/ui/fab-menu";
<FabMenu>
<FabMenuTrigger />
<FabMenuContent align="center">
<FabMenuItem>📧 Send an email</FabMenuItem>
<FabMenuItem>💬 Send a message</FabMenuItem>
<FabMenuItem>☀️ Make a reminder</FabMenuItem>
</FabMenuContent>
</FabMenu> Making it easy to use
You can install components directly using the CLI and use them just like you would with shadcn/ui.
npx shadcn-animated add switchimport { Switch } from "@/components/ui/switch"<Switch />Conclusion
Building shadcn-animated was about getting the small details right. Motion doesn't need to be flashy to make an interface feel better. Sometimes, a subtle scale change or a smoothly moving indicator is all it takes.
I wanted to make these interactions easy to reuse, easy to customize, and natural to work with. Hopefully, shadcn-animated makes it a little easier for developers to bring that extra bit of polish to their projects.