Layout Animation
Layout animation menganimasi perubahan posisi dan ukuran elemen yang disebabkan oleh perubahan layout (reflow). Ini adalah salah satu animasi paling sulit dilakukan secara manual, tapi Framer Motion membuatnya sangat mudah.
layout Prop
Cukup tambahkan prop layout dan Framer Motion akan menganimasi perubahan layout apapun:
import { motion } from 'motion/react';
function ExpandableCard({ isExpanded }) {
return (
<motion.div
layout
className={isExpanded ? 'w-full p-8' : 'w-64 p-4'}
style={{ borderRadius: 12 }}
>
<motion.h3 layout="position">Card Title</motion.h3>
{isExpanded && (
<motion.p
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
>
Extended content here...
</motion.p>
)}
</motion.div>
);
}
Bagaimana Layout Animation Bekerja
Framer Motion menggunakan teknik FLIP (First, Last, Invert, Play):
- First — catat posisi elemen sebelum perubahan
- Last — render perubahan, catat posisi baru
- Invert — gunakan
transformuntuk memindahkan elemen kembali ke posisi awal - Play — animasi dari posisi awal ke posisi baru
Semua ini terjadi otomatis — kamu hanya perlu menambahkan layout prop.
layoutId — Shared Layout Animation
layoutId memungkinkan animasi antar komponen yang berbeda tapi berbagi identitas:
function TabBar({ activeTab, tabs }) {
return (
<div className="flex gap-2">
{tabs.map((tab) => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className="relative px-4 py-2"
>
{tab.label}
{activeTab === tab.id && (
<motion.div
layoutId="activeTab"
className="absolute inset-0 bg-blue-100 rounded-lg"
style={{ zIndex: -1 }}
transition={{
type: 'spring',
stiffness: 400,
damping: 30,
}}
/>
)}
</button>
))}
</div>
);
}
Indicator activeTab akan bergerak smooth dari tab satu ke tab lainnya!
List Reordering
import { motion, AnimatePresence } from 'motion/react';
function SortableList({ items }) {
return (
<ul>
<AnimatePresence>
{items.map((item) => (
<motion.li
key={item.id}
layout
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
transition={{
layout: { type: 'spring', stiffness: 350, damping: 25 },
opacity: { duration: 0.2 },
}}
>
{item.name}
</motion.li>
))}
</AnimatePresence>
</ul>
);
}
Saat list di-sort ulang, setiap item akan bergerak smooth ke posisi barunya.
Expand / Collapse Pattern
function Accordion({ items }) {
const [openId, setOpenId] = useState(null);
return (
<div className="space-y-2">
{items.map((item) => (
<motion.div
key={item.id}
layout
className="bg-white rounded-lg overflow-hidden"
>
<motion.button
layout="position"
className="w-full p-4 text-left font-semibold"
onClick={() =>
setOpenId(openId === item.id ? null : item.id)
}
>
{item.title}
</motion.button>
<AnimatePresence>
{openId === item.id && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
className="px-4 pb-4"
>
{item.content}
</motion.div>
)}
</AnimatePresence>
</motion.div>
))}
</div>
);
}
Shared Layout Animation (Hero Pattern)
Animasi dari thumbnail ke detail view:
// Thumbnail grid
function ImageGrid({ images, onSelect }) {
return (
<div className="grid grid-cols-3 gap-4">
{images.map((img) => (
<motion.div
key={img.id}
layoutId={\`image-${img.id}\`}
onClick={() => onSelect(img)}
className="cursor-pointer rounded-lg overflow-hidden"
>
<img src={img.thumbnail} alt={img.alt} />
</motion.div>
))}
</div>
);
}
// Detail view
function ImageDetail({ image, onClose }) {
return (
<motion.div
className="fixed inset-0 flex items-center justify-center"
onClick={onClose}
>
<motion.div
layoutId={\`image-${image.id}\`}
className="w-3/4 rounded-xl overflow-hidden"
>
<img src={image.full} alt={image.alt} />
</motion.div>
</motion.div>
);
}
Elemen akan beranimasi smooth dari posisi thumbnail ke posisi detail — termasuk ukuran, posisi, dan border-radius!
Tips layout Prop
- Gunakan
layout="position"jika hanya ingin menganimasi posisi (bukan ukuran) - Gunakan
layout="size"jika hanya ingin menganimasi ukuran - Set
borderRadiusinline (bukan via CSS class) agar turut dianimasi - Wrap dalam
LayoutGroupuntuk mengisolasi layout animation antar bagian halaman
Layout animation membuat transisi antar state terasa natural dan membantu user memahami hubungan antar elemen.