← Tất cả bài viết

[ Blog ]

27 tháng 6, 2026

10 phút đọc

React Three Fiber Cho Người Mới Bắt Đầu

Hướng dẫn React Three Fiber cho người mới — dựng Canvas, tạo mesh, animate bằng useFrame, thêm ánh sáng, OrbitControls và load model 3D, kèm các lỗi thường gặp.

React Three Fiberthree.jsWebGLReact3D
React Three Fiber Cho Người Mới Bắt Đầu

Nếu bạn viết được React, bạn viết được 3D. Đó chính là lời hứa của React Three Fiber — một renderer React cho three.js, biến đống boilerplate WebGL rườm rà thành các component khai báo (declarative) mà bạn đã quen tư duy. Bài React Three Fiber cho người mới này sẽ dẫn bạn từ một <Canvas> trống đến một scene có ánh sáng, xoay được bằng chuột và load được model 3D thật — và quan trọng không kém, chỉ ra những lỗi mà ai mới bắt đầu cũng vấp phải trong ngày đầu tiên.

Tôi đã làm WebGL và 3D trên các website thương mại từng đoạt giải quốc tế, và R3F là công cụ tôi chọn khi một scene cần sống bên trong một app React. Bắt tay vào dựng nào.

React Three Fiber là gì (và vì sao nên dùng)?

three.js là thư viện WebGL phổ biến nhất trên trình duyệt. Tự thân nó mang tính mệnh lệnh (imperative): bạn phải new ra scene, camera, renderer, mesh, rồi tự tay điều khiển vòng lặp animation. React Three Fiber (R3F) bọc tất cả lại trong React. Bạn mô tả scene bằng JSX, R3F reconcile nó như mọi cây React khác, và bạn được dùng hooks, state cùng mô hình component miễn phí.

Điểm mấu chốt: không có cú pháp đặc biệt nào phải học thuộc. Mỗi class của three.js trở thành một phần tử JSX viết kiểu camelCase. new THREE.Mesh() thành <mesh>; new THREE.BoxGeometry() thành <boxGeometry>. Tham số constructor ánh xạ vào prop args; mọi thuộc tính là một prop. Chỉ một quy tắc đó đã bao trùm 90% những gì bạn sẽ viết.

R3F nằm trong hệ sinh thái pmndrs, cùng nhóm tạo ra Drei, Zustand và React Spring. Đây là một mảng anh em với GSAP và Lenis trong bài cuộn mượt trong Next.js với GSAP & Lenis — đều nhằm làm trình duyệt trở nên sống động, chỉ khác tầng trong stack.

Cài đặt và chiếc Canvas đầu tiên

Bạn cần React 19 cho bản major hiện tại. Cài renderer cùng Drei (thư viện helper gồm các component dựng sẵn như camera, controls, loader) và bản thân three.js:

npm install three @react-three/fiber @react-three/drei
# Người dùng TypeScript:
npm install -D @types/three

Phiên bản tại thời điểm viết: @react-three/fiber@9 đi cùng react@19. Nếu bạn vẫn dùng React 18, hãy cài @react-three/fiber@8. Luôn kiểm tra peer dependency hiện hành trên trang npm trước khi bắt đầu.

Mọi thứ 3D đều nằm trong <Canvas>. Nó tạo WebGL renderer, một scene và một camera mặc định, đồng thời tự chạy vòng lặp render cho bạn. Quan trọng: Canvas phải có kích thước — nó lấp đầy phần tử cha, nên phần tử cha cần một chiều cao.

// Scene.tsx
import { Canvas } from "@react-three/fiber";

export default function Scene() {
  return (
    <div style={{ width: "100%", height: "100vh" }}>
      <Canvas>
        <mesh>
          <boxGeometry args={[1, 1, 1]} />
          <meshStandardMaterial color="orange" />
        </mesh>
      </Canvas>
    </div>
  );
}

Bạn sẽ thấy... một ô vuông tối thui. Đúng rồi đấy, và đây là lỗi đầu tiên: meshStandardMaterial là vật liệu dựa trên vật lý (PBR) nên cần ánh sáng. Không có nguồn sáng, nó render màu đen. Ta sẽ sửa ngay sau đây — trước hết là cấu tạo.

Mesh, geometry và material

Một vật thể 3D nhìn thấy được là một mesh = geometry (hình dạng) + material (bề mặt). Trong R3F đó là ba phần tử lồng nhau:

<mesh position={[0, 0, 0]} rotation={[0, 0, 0]} scale={1}>
  <boxGeometry args={[1, 1, 1]} />      {/* rộng, cao, sâu */}
  <meshStandardMaterial color="orange" />
</mesh>

Ba điều cần khắc cốt:

  • args chính là chữ ký của constructor. <boxGeometry args={[1, 1, 1]} /> đúng nghĩa là new THREE.BoxGeometry(1, 1, 1). Thứ tự rất quan trọng; tra tài liệu three.js cho từng class.
  • position, rotation, scale là cách viết tắt cho transform của object. Chúng nhận mảng [x, y, z] (rotation tính bằng radian, không phải độ).
  • Chọn material rất quan trọng. meshBasicMaterial bỏ qua ánh sáng (hợp với phong cách phẳng, kiểu UI); meshStandardMaterialmeshPhysicalMaterial phản ứng với ánh sáng (chân thực). Người mới nên dùng meshStandardMaterial.

Thử đổi <boxGeometry> thành <sphereGeometry args={[1, 32, 32]} /> hay <torusKnotGeometry args={[1, 0.3, 128, 32]} /> để cảm nhận cách args chi phối hình dạng.

Vòng lặp render: useFrame

Scene tĩnh thì nhàm chán. Để animate, ta móc vào vòng lặp render bằng hook useFrame. Nó chạy mỗi khung hình (lý tưởng là 60fps) và đưa cho bạn đồng hồ cùng trạng thái scene. Vì nó thay đổi object three.js trực tiếp qua ref nên không kích hoạt re-render của React — đúng thứ bạn cần cho chuyển động mượt 60fps.

import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
import type { Mesh } from "three";

function SpinningBox() {
  const ref = useRef<Mesh>(null!);

  useFrame((state, delta) => {
    // delta = số giây kể từ khung hình trước → độc lập với frame rate
    ref.current.rotation.y += delta;
    ref.current.rotation.x += delta * 0.5;
  });

  return (
    <mesh ref={ref}>
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color="orange" />
    </mesh>
  );
}

Lỗi lớn nhất của người mới ở đây: animate bằng giá trị cố định kiểu rotation.y += 0.01. Cách đó buộc tốc độ vào frame rate, nên hộp sẽ xoay nhanh gấp đôi trên màn hình 120Hz. Luôn nhân với delta để độc lập với frame rate — đúng nguyên tắc giúp các timeline của GSAP và Framer Motion đồng nhất trên mọi thiết bị.

useFrame chỉ hoạt động bên trong một component được render dưới <Canvas>. Gọi nó bên ngoài, bạn sẽ gặp lỗi "useFrame must be used within Canvas" — một cú vấp kinh điển khác.

Ánh sáng: vì sao scene của bạn đen thui

Vật liệu standard cần ánh sáng. Hãy thêm ít nhất một ambient light (ánh sáng nền phẳng) cùng một directional light (mặt trời, tạo highlight):

<Canvas>
  <ambientLight intensity={0.4} />
  <directionalLight position={[5, 5, 5]} intensity={2} />
  <SpinningBox />
</Canvas>

Công thức ánh sáng gần như luôn nhìn ổn để khởi đầu:

Loại đènVai tròThiết lập điển hình
ambientLightÁnh sáng nền mềm, xóa vùng đen tuyệt đốiintensity={0.3–0.5}
directionalLightĐèn chính / mặt trời, tạo khốilệch position, intensity={1–3}
pointLightVầng sáng cục bộ (đèn bàn, lửa)đặt gần vật thể
<Environment /> (Drei)Chiếu sáng theo ảnh, chân thực tức thìpreset="city"

Để có kết quả nhanh và đẹp như ảnh chụp, <Environment preset="sunset" /> của Drei chiếu sáng cả scene từ một ảnh HDRI chỉ trong một dòng — một mẹo tôi dùng liên tục.

OrbitControls: cho người dùng kéo camera

Khi prototype, bạn hầu như không muốn camera bị khóa cứng. Drei có sẵn OrbitControls, cho phép người dùng xoay, pan và zoom bằng chuột — thả vào là xong:

import { OrbitControls } from "@react-three/drei";

<Canvas camera={{ position: [3, 2, 5], fov: 50 }}>
  <ambientLight intensity={0.4} />
  <directionalLight position={[5, 5, 5]} intensity={2} />
  <SpinningBox />
  <OrbitControls enableDamping makeDefault />
</Canvas>

Đặt camera khởi đầu qua prop camera trên <Canvas> (fov là góc nhìn tính bằng độ). enableDamping thêm quán tính để thao tác kéo có cảm giác đầm tay; makeDefault đăng ký bộ controls này làm mặc định của scene để các helper Drei khác phối hợp ăn ý.

Load model 3D (glTF)

Hộp là để học; dự án thật thì load model. Định dạng chuẩn của web là glTF/GLB (cứ hình dung như "JPEG cho 3D"). Hook useGLTF của Drei load nó và trả về scene graph đã được phân tích. Luôn bọc nội dung bất đồng bộ trong <Suspense> để React hiển thị fallback trong lúc file đang tải:

import { Suspense } from "react";
import { useGLTF } from "@react-three/drei";

function Model() {
  const { scene } = useGLTF("/models/duck.glb");
  return <primitive object={scene} />;
}

export default function ModelScene() {
  return (
    <Canvas camera={{ position: [0, 1, 4] }}>
      <ambientLight intensity={0.5} />
      <directionalLight position={[5, 5, 5]} intensity={2} />
      <Suspense fallback={null}>
        <Model />
      </Suspense>
      <OrbitControls makeDefault />
    </Canvas>
  );
}

// Preload để model sẵn sàng trước khi component mount
useGLTF.preload("/models/duck.glb");

<primitive object={...} /> là "cửa thoát hiểm" để nhét một object three.js có sẵn vào cây JSX. Nếu cần kiểm soát từng mesh chi tiết hơn, hãy chạy file GLB qua gltfjsx — nó sinh ra một component có type với mọi node được bóc tách, cực kỳ hữu ích khi bạn cần animate hay đổi màu từng bộ phận.

Các lỗi người mới hay gặp

Danh sách những lỗi tôi thấy nhiều nhất:

  • Màn hình đen → thiếu ánh sáng, hoặc dùng meshBasicMaterial mà lại mong nó phản ứng với sáng.
  • Không render gì → phần tử cha của Canvas không có chiều cao (co về 0px).
  • Lỗi useFrame/useLoader → hook bị gọi bên ngoài <Canvas>. Mọi hook R3F phải chạy bên trong.
  • Tốc độ khác nhau theo màn hình → bạn quên nhân chuyển động với delta.
  • Load model giật → thiếu boundary <Suspense>, hoặc chưa preload.
  • Next.js App Router → render Canvas trong Client Component ("use client"); WebGL không chạy được khi render phía server.
  • Bundle nặng / first paint chậm → để mắt tới hiệu năng từ sớm; three.js khá nặng. Cùng tính kỷ luật trong công việc front-end chú trọng hiệu năng áp dụng được ở đây.

Đi tiếp từ đâu

Bạn đã nắm vòng lặp cốt lõi: Canvas → mesh → useFrame → ánh sáng → controls → model. Từ đây, các bước tự nhiên tiếp theo là post-processing, shader GLSL tùy biến và 3D điều khiển theo scroll. Một bài thực hành nối tiếp rất hay là hiệu ứng displacement ảnh bằng WebGL, áp dụng chính những nguyên thủy này vào một hiệu ứng chuyển cảnh dựa trên shader mà bạn có thể đưa lên site thật.

Nếu 3D là một phần trong dự án lớn hơn — và bạn muốn một chuyên gia lo phần WebGL, hiệu năng và tích hợp — hãy xem dịch vụ creative development của tôi, hoặc đọc creative developer là gì.

Câu hỏi thường gặp

Có cần biết three.js trước khi học React Three Fiber không?

Không bắt buộc — nhưng có thì tốt. R3F ánh xạ một-một với các class của three.js, nên hiểu mesh, material và camera về mặt khái niệm sẽ giúp các prop trở nên hiển nhiên. Bạn có thể học cả hai cùng lúc; chỉ cần mở sẵn tài liệu three.js để tra args của từng class.

React Three Fiber đã đủ "chín" để chạy production chưa?

Rồi. Nó được dùng rộng rãi trên các site thương mại và từng đoạt giải, do nhóm pmndrs duy trì. Lưu ý chính là hiệu năng: WebGL ngốn GPU, nên hãy tính trước phần tối ưu (instancing, model nén draco, lazy load) khi scene lớn dần.

Dùng React Three Fiber với Next.js được không?

Được. Render <Canvas> bên trong một Client Component ("use client") — WebGL cần trình duyệt và không chạy được khi render phía server. Cách phổ biến là lazy-load component 3D bằng next/dynamic với { ssr: false }.

R3F và Drei khác nhau thế nào?

@react-three/fiber là renderer (cầu nối giữa React và three.js). @react-three/drei là bộ công cụ tùy chọn gồm các helper dựng sẵn — OrbitControls, useGLTF, Environment, camera — giúp bạn khỏi phải tự viết lại những phần thường gặp. Hầu hết dự án dùng cả hai.