Back to Dictionary
Empty & Error States
●○○Ready to Use
Recommended defaultError Shake
A quick horizontal shaking motion applied to an element (like an input field) to indicate invalid input or an error, mimicking a head shake.
Use
Incorrect password entry
Avoid
System-wide crashes (too playful/annoying)
Safer Alternative
No safer default listed.
Risk Level
Low risk
Live Demo
What It Is
A quick horizontal shaking motion applied to an element (like an input field) to indicate invalid input or an error, mimicking a head shake.
✓When to Use
- Incorrect password entry
- Form validation failure (empty required field)
- Declined payment
✕When NOT to Use
- System-wide crashes (too playful/annoying)
- Success states
Configuration Tips
- 01Pair with a red border and a clear text error message
- 02Keep it fast (around 300ms total)
You've Seen It In
Apple iOSStripe Checkout
AI Implementation Prompts
Move from a fast scaffold to production details, then tune timing and edge states.
Make the password input shake left and right if it's incorrect.
Reference Implementation
The same implementation approach used by the live preview, kept compact enough to inspect and adapt.
'use client';
import { motion, useAnimation } from 'framer-motion';
import { useState, useEffect, useCallback } from 'react';
import { Lock } from 'lucide-react';
export function ErrorShakeDemo({ isPlaying = false }: { isPlaying?: boolean }) {
const controls = useAnimation();
const [hasError, setHasError] = useState(false);
const triggerError = useCallback(async () => {
setHasError(false);
"text-stone-400 italic">// Add small delay to reset the state visibly if tracking repeated clicks
await new Promise(resolve => setTimeout(resolve, 50));
setHasError(true);
await controls.start({
x: [0, -8, 8, -8, 8, 0],
transition: { duration: 0.4, ease: "easeInOut" }
});
}, [controls]);
useEffect(() => {
if (isPlaying) {
const timer = setTimeout(triggerError, 100);
const interval = setInterval(triggerError, 2500);
return () => {
clearTimeout(timer);
clearInterval(interval);
};
}
}, [isPlaying, triggerError]);
return (
<div className="flex w-64 flex-col gap-3 rounded-2xl bg-white p-5 shadow-sm ring-1 ring-stone-200">
<div className="space-y-1.5">
<label className="text-[11px] font-semibold uppercase tracking-wider text-stone-500">Password</label>
<motion.div animate={controls}>
<div className={`flex h-10 w-full items-center gap-2 rounded-xl border px-3 transition-colors ${hasError ? 'border-red-500 bg-red-50' : 'border-stone-200 bg-stone-50'
}`}>
<Lock className={`h-4 w-4 ${hasError ? 'text-red-500' : 'text-stone-400'}`} />
<div className="flex gap-1">
{[1, 2, 3, 4, 5, 6].map(i => (
<div key={i} className={`h-1.5 w-1.5 rounded-full transition-colors ${hasError ? 'bg-red-900' : 'bg-stone-900'}`} />
))}
</div>
</div>
</motion.div>
<div className="h-4">
{hasError && (
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="text-[10px] font-medium text-red-500"
>
Incorrect password
</motion.p>
)}
</div>
</div>
<button
onClick={triggerError}
className="h-10 w-full rounded-xl bg-stone-900 text-xs font-medium text-white shadow-sm transition-colors hover:bg-stone-800"
>
Sign In
</button>
</div>
);
}