Shake
8starlabs-ui/shakeFreeComponent
A Stripe-style perspective error shake for form fields, with a declarative wrapper and an imperative hook
Live preview
live · rendered by the registry
Rendered by 8starlabs-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ui.8starlabs.com/r/shake.jsonSource
1"use client";23import { useCallback, useEffect, useRef, type ReactNode } from "react";4import { cn } from "@/lib/utils";56/**7 * Injects the `shake-error` keyframes once. It is rendered automatically by the8 * `Shake` component, so you only need to render it yourself when you drive the9 * animation through the `useShake` hook on your own element.10 */11export function ShakeStyles() {12 return (13 <style jsx global>{`14 @keyframes shake-error {15 0% {16 transform: perspective(700px) translate3d(0, 0, 0) rotateY(0deg);17 }18 12% {19 transform: perspective(700px) translate3d(-7px, 0, 0) rotateY(-5deg);20 }21 26% {22 transform: perspective(700px) translate3d(6px, 0, 0) rotateY(4deg);23 }24 41% {25 transform: perspective(700px) translate3d(-5px, 0, 0) rotateY(-3deg);26 }27 56% {28 transform: perspective(700px) translate3d(4px, 0, 0) rotateY(2deg);29 }30 70% {31 transform: perspective(700px) translate3d(-2px, 0, 0) rotateY(-1deg);32 }33 84% {34 transform: perspective(700px) translate3d(1px, 0, 0) rotateY(0.5deg);35 }36 100% {37 transform: perspective(700px) translate3d(0, 0, 0) rotateY(0deg);38 }39 }4041 .shake-error {42 animation: shake-error 0.5s cubic-bezier(0.36, 0.07, 0.19, 0.97) both;43 transform-style: preserve-3d;44 backface-visibility: hidden;45 }4647 @media (prefers-reduced-motion: reduce) {48 .shake-error {49 animation: none;50 }51 }52 `}</style>53 );54}5556/**57 * Imperative API. Attach `ref` to an element and call `shake()` to replay a58 * Stripe-style perspective error wobble. Remember to render `<ShakeStyles />`59 * once when using the hook on its own.60 *61 * const { ref, shake } = useShake<HTMLFormElement>();62 * <form ref={ref} onSubmit={(e) => { if (bad) { e.preventDefault(); shake(); } }}>63 */64export function useShake<T extends HTMLElement = HTMLDivElement>() {65 const ref = useRef<T>(null);66 const shake = useCallback(() => {67 const el = ref.current;68 if (!el) return;69 el.classList.remove("shake-error");70 // Force a reflow so re-adding the class restarts the animation every time.71 void el.offsetWidth;72 el.classList.add("shake-error");73 }, []);74 return { ref, shake };75}7677interface ShakeProps {78 /**79 * Change this to a new truthy value to trigger a shake (e.g. pass your error80// … truncated
Files it writes
More from 8starlabs-ui
All 74 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Buttonbutton | 8starlabs-ui | Components | Free | 2 deps | |
| Collapsiblecollapsible | 8starlabs-ui | Components | Free | 1 dep | |
| Dropdown Menudropdown-menu | 8starlabs-ui | Components | Free | 2 deps | |
| Flip clockflip-clock | 8starlabs-ui | Components | Free | no deps | |
| Heatmapheatmap | 8starlabs-ui | Components | Free | no deps | |
| Inputinput | 8starlabs-ui | Components | Free | no deps | |
| JSON Viewerjson-viewer | 8starlabs-ui | Components | Free | 1 dep · 3 files | |
| Labellabel | 8starlabs-ui | Components | Free | no deps |
