Đang tải...

Ếch Trendy

Cùng chú ếch nhỏ khám phá thế giới Web đầy biến động. Cập nhật trending nhanh như cách ếch đớp mồi! ⌨️🌿


0

React 19 và Next.js 15: những điểm mới dev đang đọc nhiều nhất để tối ưu app web

Ếch Trendy
Ếch Trendy

4 tháng trước · 10 phút đọc

React 19 và Next.js 15 thay đổi gì mà dev đang đọc nhiều vậy?

Mình nhớ hồi nâng cấp dự án từ Next.js 14 lên 15, cái đầu tiên mình làm là Google "Next.js 15 breaking changes" - và thấy cả cộng đồng đang hỏi đúng câu đó. Không phải vì framework hỏng, mà vì lần này thay đổi thật sự ảnh hưởng đến cách bạn nghĩ về kiến trúc app.

React 19 stable ra mắt cuối 2024, kéo theo Next.js 15 cập nhật loạt tính năng quan trọng. Không phải chỉ là thêm API mới - mà là thay đổi mô hình render từ gốc. Server Components không còn là "tính năng thử nghiệm", Server Actions giờ stable, và Partial Prerendering (PPR) đang được thử nghiệm rộng hơn.

344698 Ba trụ cột chính của React 19 + Next.js 15 bạn cần nắm

Bài này mình sẽ đi thẳng vào 4 chủ đề đang được đọc nhiều nhất: Server Components (dùng đúng chỗ), Server Actions (thay thế API route như thế nào), Partial Prerendering (PPR là gì và khi nào dùng), và migration thực tế từ các phiên bản cũ. Có ví dụ code cụ thể, lỗi hay gặp, và khi nào không nên nâng cấp.


Server Components: dùng đúng chỗ mới thấy hiệu quả

Server Components không phải khái niệm mới - Next.js App Router đã dùng từ phiên bản 13. Nhưng React 19 là lần đầu tiên nó được đưa vào spec chính thức của React, không còn là "Next.js experiment" nữa.

Vấn đề ở đâu? Nhiều dev nhảy vào App Router rồi biến mọi component thành Server Component "cho nó nhanh". Thực tế phức tạp hơn.

Server Component phù hợp khi:

  • Fetch data trực tiếp từ database hoặc API (không cần waterfall qua client)
  • Render nội dung tĩnh hoặc ít thay đổi (blog post, product detail)
  • Component cần access secrets/API keys (không expose ra client bundle)
  • Giảm JavaScript gửi về client - component nặng logic nhưng không cần interactivity

Client Component vẫn cần khi:

  • Dùng useState, useEffect, event handlers
  • Cần browser APIs (localStorage, geolocation)
  • Animation, real-time updates
  • Third-party libraries chưa hỗ trợ Server Components

344699 Server Component fetch thẳng DB, không cần API route trung gian

Ví dụ migration thực tế - trước đây với Pages Router:

// Pages Router (cũ) - pages/products/[id].js
export async function getServerSideProps({ params }) {
  // Gọi internal API route
  const res = await fetch(`/api/products/${params.id}`);
  const product = await res.json();
  return { props: { product } };
}

export default function ProductPage({ product }) {
  return <ProductDetail product={product} />;
}

Với App Router + Server Component:

// App Router (mới) - app/products/[id]/page.js
// Đây là Server Component mặc định
async function ProductPage({ params }) {
  // Fetch thẳng từ DB, không qua API route
  const product = await db.product.findUnique({
    where: { id: params.id }
  });

  return <ProductDetail product={product} />;
}

export default ProductPage;

Kết quả thực tế? Bỏ hẳn một lớp network request. Với app có nhiều data fetching, 3%-8%+ smaller client-side JavaScript size (Next.js Blog (Vercel)). Nhưng trade-off rõ ràng: bạn cần tư duy lại về data flow, không còn fetch trong useEffect nữa.

Lỗi hay gặp nhất: truyền function/callback từ Server Component xuống Client Component. Không được - Server Component không serialize được function. Giải pháp: tách component boundary rõ ràng, Server Component truyền data (plain object), Client Component xử lý interaction.


Server Actions: khi nào thay thế được API route?

Server Actions là tính năng mình thấy dev tranh luận nhiều nhất trong 2024-2025. Một bên nói "dùng luôn cho đơn giản", một bên nói "đừng bao giờ dùng thay API". Cả hai đều sai một phần.

Server Actions là function async chạy trên server, được gọi trực tiếp từ component mà không cần tạo API endpoint riêng. Stable trong React 19 và Next.js 14+.

// app/actions.js
'use server';

import { db } from '@/lib/db';
import { revalidatePath } from 'next/cache';

export async function createPost(formData) {
  const title = formData.get('title');
  const content = formData.get('content');

  // Validate input
  if (!title || title.length < 3) {
    return { error: 'Tiêu đề phải có ít nhất 3 ký tự' };
  }

  // Ghi thẳng vào DB
  await db.post.create({
    data: { title, content }
  });

  // Revalidate cache sau khi tạo mới
  revalidatePath('/posts');
  return { success: true };
}
// app/posts/new/page.jsx
import { createPost } from '@/app/actions';

export default function NewPostPage() {
  return (
    <form action={createPost}>
      <input name="title" placeholder="Tiêu đề" />
      <textarea name="content" placeholder="Nội dung" />
      <button type="submit">Tạo bài viết</button>
    </form>
  );
}

344700 Server Actions phù hợp nhất cho form mutations trong cùng Next.js app

Dùng Server Actions khi nào?

Phù hợp: Form submission trong Next.js app (tạo, sửa, xóa data), optimistic updates với useOptimistic hook mới của React 19, revalidate cache sau mutation.

Không phù hợp: Khi bạn có mobile app hoặc bên thứ ba cần gọi API - họ không gọi được Server Action. Khi cần versioning API rõ ràng. Khi team có backend riêng (Node.js, Go, Java) - API route hoặc external API vẫn cần thiết.

Lỗi hay gặp: quên 'use server' directive, hoặc đặt Server Action trực tiếp trong Client Component file mà không import từ file riêng. Với React 19, bạn có thể đặt 'use server' ở đầu file để đánh dấu toàn bộ file là server-only - dùng cái này cho file actions tập trung.

Một điểm thú vị của React 19: useActionState hook (đổi tên từ useFormState) giúp handle loading state và error state của Server Action gọn hơn nhiều:

'use client';
import { useActionState } from 'react';
import { createPost } from '@/app/actions';

export default function PostForm() {
  const [state, formAction, isPending] = useActionState(createPost, null);

  return (
    <form action={formAction}>
      {state?.error && <p className="text-red-500">{state.error}</p>}
      <input name="title" disabled={isPending} />
      <button disabled={isPending}>
        {isPending ? 'Đang tạo...' : 'Tạo bài viết'}
      </button>
    </form>
  );
}

Partial Prerendering: tính năng ít người hiểu đúng nhất

PPR (Partial Prerendering) là thứ Next.js team đang đặt cược lớn nhất trong 2025. Hiện tại vẫn ở experimental trong Next.js 15, nhưng đang được thử nghiệm rộng và sắp stable.

Vấn đề PPR giải quyết: trước đây bạn phải chọn một trong hai - static (nhanh nhưng data cũ) hoặc dynamic (data mới nhưng chậm hơn). PPR cho phép một page vừa có static shell (render ngay lập tức từ CDN) vừa có dynamic parts (stream sau).

344701 PPR kết hợp static shell + dynamic streaming trong cùng một route

Cụ thể hơn - một trang product listing:

// app/products/page.jsx
import { Suspense } from 'react';
import { ProductGrid } from './ProductGrid';
import { RecommendedProducts } from './RecommendedProducts';

export default function ProductsPage() {
  return (
    <div>
      {/* Phần này static - render từ CDN ngay lập tức */}
      <header>
        <h1>Sản phẩm</h1>
        <nav>...</nav>
      </header>

      {/* ProductGrid cần data động - stream sau */}
      <Suspense fallback={<ProductSkeleton />}>
        <ProductGrid />
      </Suspense>

      {/* RecommendedProducts dựa vào user - stream sau */}
      <Suspense fallback={<RecommendedSkeleton />}>
        <RecommendedProducts />
      </Suspense>
    </div>
  );
}

Bật PPR trong next.config.js:

// next.config.js
module_exports = {
  experimental: {
    ppr: 'incremental' // Bật theo từng route
  }
};

// Trong page.jsx, thêm:
export const experimental_ppr = true;

Tại sao quan trọng? Với model này, TTFB (Time to First Byte) của bạn giống như static site, nhưng data vẫn fresh. Người dùng thấy layout ngay, content stream vào sau. Trải nghiệm giống SPA nhưng không tốn JavaScript khổng lồ.

Khi nào CHƯA nên dùng PPR: app đang dùng Pages Router (PPR chỉ có trong App Router), team chưa quen với Suspense boundaries, hoặc app toàn dynamic data không có phần static nào đáng kể - PPR không giúp nhiều trong trường hợp đó.


Migration thực tế: lỗi hay gặp và cách tránh

Nâng cấp lên Next.js 15 không phải npm install next@latest rồi xong. Có một số breaking changes thật sự cần xử lý.

Breaking change quan trọng nhất: async params và searchParams

Trong Next.js 15, params và searchParams trong page component giờ là Promise - bạn phải await chúng:

// Next.js 14 (cũ) - đồng bộ
export default function Page({ params, searchParams }) {
  const { id } = params; // Lấy thẳng
  const { sort } = searchParams;
  // ...
}

// Next.js 15 (mới) - bất đồng bộ
export default async function Page({ params, searchParams }) {
  const { id } = await params; // Phải await
  const { sort } = await searchParams;
  // ...
}

Vì sao thay đổi này? Next.js cần thời gian để xác định params trước khi truyền xuống - việc làm async giúp tối ưu hóa rendering pipeline. Nhưng đây là nguồn gốc của Version 15 introduced Async Request APIs as a breaking change, with temporary synchronous compatibility; in Next.js 16, synchronous access is fully removed. (Next.js Upgrading: Version 16) lỗi nâng cấp mà dev báo cáo trên GitHub issues.

344702 Checklist migration Next.js 15 - đừng bỏ qua bước nào

React 19: thay đổi ref handling

forwardRef không còn cần thiết nữa. React 19 cho phép truyền ref như prop bình thường:

// React 18 (cũ)
const Input = forwardRef((props, ref) => (
  <input ref={ref} {...props} />
));

// React 19 (mới) - ref như prop thông thường
const Input = ({ ref, ...props }) => (
  <input ref={ref} {...props} />
);

Checklist trước khi nâng cấp:

  1. Chạy npx @next/codemod@latest upgrade latest - Next.js cung cấp codemod tự động sửa phần lớn breaking changes
  2. Kiểm tra tất cả params/searchParams usage - thêm await
  3. Review cookies(), headers() từ next/headers - cũng đã async trong Next.js 15
  4. Kiểm tra third-party libraries tương thích React 19 (đặc biệt các UI libraries)
  5. Test kỹ Suspense boundaries nếu dùng streaming

Mình khuyên nâng cấp theo từng bước: Next.js 14 → 15 trước, ổn định rồi mới xem xét các tính năng experimental như PPR. Đừng migration "all at once" với production app đang chạy.

Nếu bạn đang học React từ đầu hoặc muốn có nền tảng vững trước khi đụng vào Next.js 15, khóa Xây Dựng Website với ReactJS trên F8 cover từ cơ bản đến nâng cao - hiểu React rõ thì tiếp thu các thay đổi này nhanh hơn nhiều.

Tải checklist migration đầy đủ tại đây: Tải checklist migration Next.js 15


Tóm lại: bạn nên làm gì ngay bây giờ?

React 19 và Next.js 15 không phải bản update "cứ nâng lên cho mới". Các thay đổi này có trade-off rõ ràng và cần hiểu đúng trước khi áp dụng.

Takeaways:

  • Server Components - dùng cho data fetching và static rendering, không biến mọi thứ thành Server Component
  • Server Actions - phù hợp cho form mutations trong Next.js app, không thay thế API khi có client bên ngoài
  • PPR - còn experimental, theo dõi nhưng chưa dùng production trừ khi bạn chịu được instability
  • Migration - chạy codemod trước, async params/searchParams là nguồn lỗi số một

Bước tiếp theo thực tế:

  1. Nếu đang dùng Next.js 13-14: chạy npx @next/codemod@latest upgrade latest trong môi trường dev, xem output cần fix gì
  2. Nếu đang học hoặc bắt đầu project mới: dùng App Router ngay từ đầu, học patterns đúng luôn
  3. Nếu còn dùng Pages Router và app đang chạy ổn: không có lý do gì phải migrate ngay - Pages Router vẫn được support

Gặp vướng mắc gì trong quá trình nâng cấp, drop comment - mình sẽ cố giải đáp.

Trang web dùng cookie để hoạt động và đo hiệu quả quảng cáo. Xem chính sách bảo mật