← Tất cả bài viết
GSAP Hay Framer Motion: Nên Chọn Cái Nào?
GSAP hay Framer Motion? Một creative developer so sánh API, mức hợp với React, scroll, timeline, bundle size và khi nào nên chọn thư viện nào.

Vài tuần một lần lại có client hoặc designer hỏi tôi cùng một câu: GSAP hay Framer Motion — nên dùng thư viện animation nào? Tôi ship cả hai trong production. Tôi dùng GSAP hơn mười năm cho các website đoạt giải, và tôi chọn Framer Motion (giờ chỉ còn tên Motion) khi một app React cần UI khai báo, điều khiển bằng cử chỉ. Thực ra chúng không hẳn là đối thủ; chúng là hai công cụ khác nhau có phần chồng lấn. Bài này là bản so sánh tôi luôn muốn gửi cho mọi người — khác biệt API thật sự, cách tích hợp React, scroll, timeline, bundle size, và câu trả lời thẳng thắn cho "dùng cả hai được không?".
Một lưu ý về tên gọi: tháng 2/2025 Framer Motion được đổi tên thành Motion, import khuyến nghị giờ là motion/react (package cũ framer-motion vẫn chạy được). Và tháng 4/2025 Webflow đã làm GSAP miễn phí 100%, bao gồm mọi plugin từng phải trả phí (SplitText, MorphSVG, ScrollSmoother...). Cả hai thay đổi này đều ảnh hưởng tới quyết định, nên tôi sẽ lồng vào.
Tóm tắt nhanh: chọn cái nào
- Chọn Framer Motion (Motion) nếu bạn làm app React/Next.js và phần lớn animation là state của component, transition vào/ra, thay đổi layout, và cử chỉ (hover, drag, tap). Nó khai báo, hiểu vòng đời của React, và layout animation thì đúng nghĩa "ảo diệu".
- Chọn GSAP nếu bạn cần chuyển động phức tạp, chính xác, dàn dựng theo timeline — cảnh scroll, hero sequence nhiều bước, morph SVG, tách chữ, easing có cảm giác vật lý, hay bất cứ thứ gì bạn cần điều phối hàng chục phần tử tới từng mili-giây. Nó cũng độc lập framework, nên kiến thức dùng được luôn cho Webflow, vanilla JS hay framework bất kỳ.
- Dùng cả hai khi hợp lý — chúng sống chung tốt. Nói kỹ ở dưới.
Khác biệt cốt lõi: khai báo vs ra lệnh
Đây là ngã rẽ quan trọng nhất, hiểu trước rồi mới tính tiếp.
Framer Motion là khai báo (declarative). Bạn mô tả trạng thái mong muốn, thư viện tự lo transition. Bạn render một component motion và animate qua props:
import { motion } from "motion/react";
export function Card({ isOpen }: { isOpen: boolean }) {
return (
<motion.div
animate={{ opacity: isOpen ? 1 : 0, y: isOpen ? 0 : 24 }}
transition={{ duration: 0.4, ease: "easeOut" }}
>
Nội dung
</motion.div>
);
}
Không có lệnh "play" nào. Bạn lật isOpen, React re-render, Motion nội suy từ giá trị cũ sang mới. Khớp hoàn hảo với cách React vốn vận hành.
GSAP là ra lệnh (imperative). Bạn nắm lấy phần tử và điều khiển chúng trên một timeline, tự quyết khi nào mỗi thứ xảy ra:
import gsap from "gsap";
const tl = gsap.timeline({ defaults: { ease: "power3.out" } });
tl.from(".hero-title", { y: 80, opacity: 0, duration: 0.8 })
.from(".hero-sub", { y: 40, opacity: 0, duration: 0.6 }, "-=0.4")
.from(".hero-cta", { scale: 0.9, opacity: 0, duration: 0.5 }, "-=0.3");
"-=0.4" là tham số vị trí tương đối — bắt đầu tween này trước khi tween trước kết thúc 0.4s. Kiểu chồng lấn và sắp xếp thời gian này là nơi GSAP bứt lên và không có đối thủ. Khi dàn dựng một intro phức tạp hay một câu chuyện scroll, mô hình timeline biểu đạt tốt hơn nhiều so với nối chuỗi các state khai báo.
Tích hợp React: useGSAP vs motion/react
Nhiều người tưởng GSAP và React "đá nhau". Không hề — bạn chỉ cần đúng pattern cleanup.
GSAP có hook chính thức useGSAP (từ @gsap/react) lo cleanup và scope giúp bạn. Mọi thứ tạo bên trong nó được tự revert khi unmount — đúng thứ bạn cần với React 18/19 khi Strict Mode gọi effect hai lần:
import { useRef } from "react";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";
export function Hero() {
const root = useRef<HTMLDivElement>(null);
useGSAP(
() => {
gsap.from(".hero-title", { y: 80, opacity: 0, duration: 0.8 });
},
{ scope: root } // selector được scope vào ref này; tự revert khi unmount
);
return (
<div ref={root}>
<h1 className="hero-title">Chuyển động đoạt giải</h1>
</div>
);
}
Với Motion bạn không viết cleanup gì cả — component chính là animation. Vào/ra do AnimatePresence lo:
import { AnimatePresence, motion } from "motion/react";
<AnimatePresence>
{isVisible && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>;
Chốt lại: Motion ít ma sát hơn cho UI state, GSAP mạnh hơn cho dàn dựng. Nếu animation là "thứ này xuất hiện/biến mất/di chuyển khi state đổi" thì Motion thắng về độ tiện. Nếu animation là "chuỗi 12 bước với thời gian chồng lấn" thì GSAP thắng về độ kiểm soát.
Scroll: ScrollTrigger vs useScroll của Motion
Scroll là thứ tôi bị hỏi nhiều nhất, và đây là chỗ phân hóa rõ nhất.
GSAP ScrollTrigger là engine scroll mạnh nhất trên web — pin section, scrub timeline theo tiến độ scroll, snap, và trigger start/end chính xác. Không có gì sánh được cho việc kể chuyện bằng scroll:
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
gsap.to(".panel", {
xPercent: -100,
ease: "none",
scrollTrigger: {
trigger: ".panel-wrap",
pin: true, // ghim section khi cuộn
scrub: 1, // gắn tiến độ vào thanh cuộn
end: "+=2000",
},
});
Muốn xem chi tiết, tôi có một bài riêng hướng dẫn GSAP ScrollTrigger với pin, scrub và parallax, và một bài về cách kết hợp nó với cuộn mượt trong Next.js bằng GSAP và Lenis.
useScroll của Motion rất ổn cho các trường hợp phổ biến — thanh tiến độ, parallax đơn giản, reveal khi cuộn — và ở hẳn trong thế giới React/khai báo:
import { motion, useScroll, useTransform } from "motion/react";
export function ParallaxImage() {
const { scrollYProgress } = useScroll();
const y = useTransform(scrollYProgress, [0, 1], ["0%", "30%"]);
return <motion.img style={{ y }} src="/photo.jpg" alt="Ảnh parallax" />;
}
Cho một thanh tiến độ hay một lớp parallax đơn, useScroll gọn hơn. Nhưng ngay khi bạn cần pin, timeline scrub, hay snap nhiều section, ScrollTrigger là câu trả lời — đây là một trong những lý do chính GSAP xuất hiện trên rất nhiều site Awwwards.
Timeline và sắp xếp chuỗi
Cả hai đều sắp chuỗi được, nhưng độ sâu khác nhau.
Timeline của GSAP là một object hạng nhất: lồng nhau, điều khiển phát (tl.pause(), tl.reverse(), tl.timeScale(2)), nhảy tới label, tái sử dụng. Không thể thay thế cho các intro phức tạp và chuỗi tương tác.
Motion sắp chuỗi bằng hàm animate() (khi cần imperative) hoặc các prop điều phối như staggerChildren trên variants. Đủ tốt cho danh sách stagger và UI phối hợp, nhưng không phải một timeline đầy đủ với scrub, label và time-scale.
Bundle size: con số thật
Bundle size thường là yếu tố quyết định cho dự án nhạy về hiệu năng, nên tôi nói chính xác. Đây là con số min+gzip xấp xỉ; hãy kiểm tra trên bundlephobia cho đúng phiên bản của bạn:
| Tiêu chí | GSAP | Motion (Framer Motion) |
|---|---|---|
| Core / mức nền | ~23 KB gzip (core) | ~34 KB gzip (motion đầy đủ) |
| Tree-shaking | Import từng plugin (ScrollTrigger thêm ~10 KB) | LazyMotion + m → ~4.6 KB ban đầu, tính năng nạp theo nhu cầu |
| Plugin / phần thêm | Mọi plugin nay miễn phí (SplitText, MorphSVG...) | Layout/drag/cử chỉ có sẵn hoặc lazy-load |
| Khóa vào framework | Không (vanilla, mọi framework) | Chỉ React |
Điểm tinh tế: component motion đầy đủ của Motion nặng hơn GSAP core, nhưng nếu bạn dùng pattern LazyMotion + m thì ban đầu chỉ tốn ~4.6 KB và nạp tính năng theo nhu cầu — nhẹ hơn GSAP cho UI nhỏ, ít cử chỉ. GSAP giữ mức ~23 KB cho core và bạn chỉ thêm plugin nào bạn import. Với site nặng animation, bạn gần như chắc chắn kéo thêm ScrollTrigger, nên hãy tính ~33 KB+.
Không con số nào đáng sợ trên dự án thật — nhưng nếu bạn ship một widget tí hon mà từng kilobyte đều quý, LazyMotion cho sàn nhẹ hơn.
Khi nào mỗi thư viện thực sự thắng
Chọn Framer Motion (Motion) khi:
- Bạn làm UI sản phẩm React/Next.js: modal, drawer, tab, accordion, toast.
- Bạn muốn layout animation — prop
layouttự animate thay đổi kích thước/vị trí, thuộc loại tốt nhất hiện nay. - Cử chỉ quan trọng: drag, hover, tap, pan với spring physics có sẵn.
- Team thiên React và muốn animation đọc giống phần còn lại của component.
Chọn GSAP khi:
- Bạn dựng một hero đặc trưng, một intro sequence, hay một câu chuyện scroll với thời gian chính xác.
- Bạn cần ScrollTrigger (pin, scrub, snap) — không có thứ thay thế thật sự.
- Bạn muốn morph SVG, hiệu ứng chữ nâng cao (SplitText), hay motion-path.
- Bạn cần cùng một kỹ năng dùng được ngoài React (Webflow, vanilla, Svelte, Vue).
- Bạn cần kiểm soát tuyệt đối, chính xác từng frame trong dàn dựng.
Trên website sự kiện Iventions đoạt giải, phần nặng nhất — dàn dựng scroll, bàn giao qua WebGL/Three.js, các reveal theo chuỗi — là GSAP. Đó là kiểu công việc mà độ kiểm soát của GSAP là bắt buộc. Còn với một dashboard SaaS điển hình có panel chuyển động và kéo-thả sắp xếp, tôi sẽ chọn Motion mỗi lần.
Dùng cả hai trong cùng dự án được không?
Được, và tôi vẫn làm. Chúng không xung đột — GSAP biến đổi DOM/style theo kiểu imperative còn Motion điều khiển component của riêng nó. Một cách chia sạch sẽ thường gặp:
- Motion cho UI state và cử chỉ (menu, vào/ra cấp trang, drag).
- GSAP cho hero, cảnh scroll, và hiệu ứng SVG/chữ.
Quy tắc duy nhất: đừng để cả hai thư viện cùng animate một thuộc tính trên cùng một phần tử một lúc — chúng sẽ giành nhau transform. Giữ quyền sở hữu rõ ràng (Motion sở hữu phần tử này, GSAP sở hữu phần tử kia) là chúng sống chung vui vẻ. Chi phí bundle khi ship cả hai thường chấp nhận được nếu mỗi cái làm đúng sở trường; nếu bạn cực kỳ tiết kiệm dung lượng, hãy chọn một và đi sâu.
Câu hỏi thường gặp
GSAP năm 2026 còn tính phí không?
Không. Từ 30/4/2025, Webflow đã làm GSAP miễn phí 100%, gồm tất cả plugin Club từng trả phí (SplitText, MorphSVG, DrawSVG, ScrollSmoother, Inertia). Điều này gỡ bỏ lý do lớn nhất khiến nhiều team trước đây né nó.
Framer Motion có phải là Motion không?
Đúng — Framer Motion được đổi tên thành Motion đầu năm 2025. Package khuyến nghị là motion và bạn import từ motion/react. Package cũ framer-motion vẫn chạy cho dự án hiện có.
Cái nào tốt hơn cho animation scroll?
ScrollTrigger của GSAP cho mọi thứ vượt mức cơ bản (pin, timeline scrub, snap). useScroll/useTransform của Motion hoàn toàn ổn cho thanh tiến độ và parallax đơn giản trong React.
Cái nào bundle nhỏ hơn?
Với LazyMotion + component m, Motion có thể ship ~4.6 KB ban đầu — nhỏ hơn GSAP core (~23 KB). Nhưng với site nhiều animation bạn sẽ thêm ScrollTrigger vào GSAP, nên hãy so theo bộ tính năng thực tế, đừng nhìn con số tiêu đề.
Chỉ làm app React thì có cần biết GSAP không?
Không bắt buộc — Motion bao phủ phần lớn nhu cầu animation UI trong React. Nhưng ngày bạn cần một câu chuyện scroll thực thụ, dàn dựng phức tạp, hay morph SVG, GSAP là công cụ, và nó còn dùng được ngoài React. Đó là một trong những kỹ năng định hình một creative developer.
Vẫn chưa chắc cái nào hợp dự án của bạn — hay muốn một trải nghiệm hero/scroll thực sự đoạt giải? Đó đúng là việc tôi làm. Xem tôi có thể giúp gì ở trang dịch vụ.