Update React 19 di Next.js 15: Apa yang Baru & Kapan Harus Dipakai?

Dulu, ada momen pas kita mau update framework atau library favorit di proyek. Perasaan campur aduk itu pasti muncul: ada sedikit rasa excited karena bakal ada fitur baru yang bikin ngiler, tapi nggak bisa dipungkiri, ada juga deg-degannya. "Duh, apa aja nih yang berubah? Kode lama yang udah susah payah dibangun bakal pecah nggak ya?" Pertanyaan-pertanyaan itu sering banget melintas di pikiran. Kita langsung kepikiran, "Jangan-jangan bakal begadang lagi nih buat benerin bug yang muncul setelah update."

Pengalaman ini mungkin bukan cuma kita aja yang ngerasain. Hampir semua developer pernah mengalami sensasi kayak gini, apalagi kalau update-nya itu lumayan gede. Rasanya kayak mau nyobain mobil balap baru, tapi belum tahu pedal gasnya sensitif seberapa, atau remnya pakem banget apa enggak. Nah, perasaan campur aduk inilah yang mungkin lagi dirasakan para developer React dan Next.js saat mendengar kabar React 19 dan integrasinya yang bakal datang dengan Next.js 15. Tapi tenang, kali ini ceritanya justru lebih seru, lebih menjanjikan, dan bikin kita nggak sabar buat nyobain! Kali ini kita nggak perlu terlalu deg-degan, karena update ini justru banyak membawa kabar baik, apalagi buat performa dan kemudahan ngoding.


🧠 Apa Itu React 19 & Kenapa Beda?

Kalau kamu udah lama ngoding pakai React, kamu pasti familiar dengan hal-hal ini:

  • Harus bikin useState, useEffect, dan fetch tiap kali kirim data form
  • Bikin tombol loading sendiri, handle error sendiri
  • Dan yang paling sering... nulis API handler bolak-balik cuma buat validasi form sederhana 😡

Nah, React 19 hadir buat mengubah semua itu.

πŸŽ‰ React 19 bukan update biasa

React 19 itu bukan sekadar ganti versi. Ini transformasi besar yang bikin cara kita ngoding lebih simpel dan efisien.

Bayangin kamu biasa naik motor bebek buat antar orderan, sekarang dikasih motor listrik β€” tetap cepat, tapi lebih hemat tenaga dan nggak berisik.


πŸš€ Apa yang Baru di React 19?

1. action(): Kirim Data Tanpa Ribet

Sekarang kamu bisa kirim data ke server langsung dari komponen React tanpa harus bikin API handler terpisah.

Misalnya kamu bikin form booking mobil? Tinggal definisikan fungsi server di action() dan panggil langsung dari <form>.

<form action={pesanMobil}>...</form>

Di balik layar, React & Next.js yang urus submit data β†’ validasi β†’ simpan ke DB.

Dulu:

➑️ useState β†’ handleSubmit β†’ fetch β†’ handle loading β†’ handle error

Sekarang:

➑️ action() β†’ Done. ✨


2. Form API Baru: useFormStatus() & useFormState()

  • useFormStatus(): Cek apakah form sedang submitting, ada error, atau sukses
  • useFormState(): Ambil dan tangani state form yang berasal dari server (bukan dari client)

Dengan ini, kamu bisa bikin form:

  • Ada loading state otomatis
  • Error dari server langsung ditampilkan ke UI
  • Tanpa harus bikin 3-4 state manual di komponen

3. useOptimistic(): UI Langsung Update Sebelum Server Balas

Salah satu fitur paling kerasa di React 19: Optimistic UI.

Misal: kamu klik tombol "Pesan Sekarang" β†’ total booking langsung bertambah, meskipun server belum sempat kasih respon.

Baru nanti, kalau server bales error, UI bisa revert.

const [jumlah, setJumlah] = useOptimistic(10)

Kenapa ini penting?

  • User merasa aplikasi cepat
  • Responsif kayak native app
  • Nggak perlu loading spinner di setiap klik

πŸ§ͺ Perubahan Lain yang Ikut Diperbarui

  • React Compiler (masih eksperimental): Compiler otomatis optimasi kode, mirip Svelte atau Solid
  • Event Handling lebih ringan
  • Transisi UI lebih smooth (via startTransition)

βš–οΈ Perbandingan: Dulu vs Sekarang

Dulu (React <18):

  • Form harus pakai fetch, useState, dan handleSubmit manual
  • Harus bikin loading dan error handler sendiri
  • UI harus nunggu respon server dulu baru berubah
  • Banyak boilerplate dan kode berulang

Sekarang (React 19 + Next.js 15):

  • Form cukup pakai action() + Form API langsung di komponen
  • Loading & error otomatis ditangani via useFormStatus()
  • UI bisa langsung berubah pakai useOptimistic(), tanpa nunggu server
  • Kode jadi lebih pendek, lebih bersih, dan mudah dibaca

🧠 Kenapa Ini Relevan?

Karena sekarang user udah terbiasa sama UX cepat (kayak Gojek, Tokopedia, Traveloka).

Kalau web kamu lemot dikit, user bisa langsung cabut.

React 19 bantu kamu ngejar UX modern, tapi tanpa harus jadi ahli JavaScript yang rumit.


Kita udah tahu fitur-fiturnya, tapi... gimana cara pakainya dalam proyek nyata?

Yuk lanjut ke studi kasus: web sewa mobil πŸš—

πŸ” Daftar Fitur Baru (Singkat, Tapi Nendang)

React 19 hadir dengan fitur-fitur baru yang tujuannya satu: bikin hidup developer lebih simpel dan UX lebih cepat. Berikut daftarnya dalam bentuk list + analogi + contoh kode πŸ‘‡


1. βœ… action(): Jalankan Fungsi Server Langsung dari Form

Fungsi:

Memungkinkan kamu menjalankan fungsi server (seperti insert ke database) langsung dari form, tanpa bikin API route manual.

Analogi:

Kayak kamu pesan makanan di resto, dan pesananmu langsung masuk ke dapur β€” nggak perlu lewat admin, antrian, dll.

Contoh Kode:

// app/pesan/page.tsx

import { action } from 'react'
import { redirect } from 'next/navigation'

const pesanMobil = action(async (formData) => {
  const nama = formData.get('nama') as string
  const tanggal = formData.get('tanggal') as string

  // Simpan data ke DB (pura-pura dulu)
  await saveToDB(nama, tanggal)

  redirect('/terima-kasih')
})

export default function Page() {
  return (
    <form action={pesanMobil}>
      <input name="nama" placeholder="Nama Penyewa" required />
      <input name="tanggal" type="date" required />
      <button>Pesan Sekarang</button>
    </form>
  )
}

2. 🧠 useFormStatus(): Lacak Status Form Secara Otomatis

Fungsi:

Melacak status form saat dikirim, misalnya:

  • Apakah sedang submit (pending)
  • Apakah ada error

Analogi:

Seperti notifikasi driver Grab: β€œSedang dijemput”, β€œSedang dalam perjalanan”, atau β€œSudah sampai”.

Contoh Kode:

'use client'

import { useFormStatus } from 'react-dom'

export function PesanButton() {
  const { pending } = useFormStatus()

  return (
    <button type="submit" disabled={pending}>
      {pending ? 'Memesan...' : 'Pesan Sekarang'}
    </button>
  )
}

3. 🚦 useFormState(): Validasi Server Langsung ke UI

Fungsi:

Form bisa kirim dan menerima feedback langsung dari server. Cocok buat validasi nama, tanggal, dsb.

Nggak perlu bikin useState, useEffect, dan error handler manual lagi.

Analogi:

Kamu isi tanggal sewa β†’ server langsung bilang salah atau bener, tanpa harus reload halaman atau fetch manual.

Contoh Kode:

'use client'

import { useFormState } from 'react-dom'

async function prosesForm(_prevState: any, formData: FormData) {
  const nama = formData.get('nama')?.toString()
  if (!nama || nama.length < 3) {
    return { error: 'Nama minimal 3 huruf ya!' }
  }

  return { success: true }
}

export default function FormBooking() {
  const [state, formAction] = useFormState(prosesForm, null)

  return (
    <form action={formAction}>
      <input name="nama" placeholder="Nama Penyewa" />
      {state?.error && <p className="text-red-500">{state.error}</p>}
      <button>Pesan Mobil</button>
    </form>
  )
}

4. ⚑ useOptimistic(): Optimistic UI Tanpa Ribet

Fungsi:

Bikin UI langsung update sebelum server bales. Kalau nanti server error, tinggal dibalik lagi.

Analogi:

Klik tombol β†’ UI langsung berubah β†’ server nyusul.

Kayak kamu klik β€œlike” di Instagram, dan ikon langsung berubah, walau sinyal lagi lemot.

Contoh Kode:

'use client'
import { useOptimistic, useTransition } from 'react'

export function JumlahBooking() {
  const [jumlah, setJumlah] = useOptimistic(10)
  const [isPending, startTransition] = useTransition()

  function tambahBooking() {
    startTransition(() => {
      setJumlah((prev) => prev + 1)
      fetch('/api/tambah-booking', { method: 'POST' })
    })
  }

  return (
    <div>
      <p>Total Booking: {jumlah}</p>
      <button onClick={tambahBooking} disabled={isPending}>
        {isPending ? 'Loading...' : 'Booking Lagi'}
      </button>
    </div>
  )
}

5. πŸŒ€ Transisi UI Lebih Smooth dengan startTransition()

Fungsi:

Membuat transisi UI berat (seperti filter, pencarian, sorting) jadi halus dan nggak nge-freeze.

Kamu bisa pisahkan mana update penting dan mana yang bisa ditunda sedikit.

Analogi:

Pas kamu ketik filter mobil, UI tetap responsif walau datanya belum semua dimuat β€” kayak Netflix saat kamu cari film.

Contoh Kode:

'use client'
import { startTransition, useState } from 'react'

export default function FilterMobil() {
  const [filter, setFilter] = useState('')

  function handleFilter(e: React.ChangeEvent<HTMLInputElement>) {
    const value = e.target.value
    startTransition(() => {
      setFilter(value)
    })
  }

  return (
    <>
      <input onChange={handleFilter} placeholder="Cari tipe mobil..." />
      <p>Menampilkan hasil untuk: {filter}</p>
    </>
  )
}

Masing-masing fitur ini nggak cuma bikin kode lebih bersih, tapi juga membuat web terasa lebih cepat dan responsif untuk pengguna.

Next, yuk kita bahas gimana semua ini bisa diterapkan dalam proyek nyata: web sewa mobil πŸš—.


πŸš— 4. Studi Kasus: Web Sewa Mobil ala Tokocar

Bayangin kamu lagi bikin Tokocar β€” sebuah platform sewa mobil online. Fitur utamanya tentu aja:

  • Booking mobil
  • Tampilkan jumlah booking
  • Validasi input penyewa

Dengan React 19 + Next.js 15, semua ini bisa dibuat dengan lebih simpel, bersih, dan cepat.


πŸ”˜ Contoh: Booking Mobil dengan Server Action

Dulu, untuk submit form booking kamu harus:

  • Setup useState
  • Buat handleSubmit
  • Panggil API dengan fetch
  • Bikin endpoint API di /api/booking

Sekarang? Cukup pakai Server Action dari React 19.

βœ… Kode Lengkap

Halaman pesan:

// app/pesan/page.tsx

import { redirect } from "next/navigation";
import { SubmitButton } from "./SubmitButton";

export async function bookingMobil(formData: FormData) {
  "use server";

  const nama = formData.get("nama")?.toString();
  const tanggal = formData.get("tanggal")?.toString();

  if (!nama || !tanggal) {
    throw new Error("Data tidak lengkap");
  }

  // Simulasi simpan ke DB
  console.log("Booking disimpan:", { nama, tanggal });

  redirect("/terima-kasih");
}

export default function Page() {
  return (
    <div className="flex flex-col gap-10 items-center justify-center min-h-screen">
      <h2 className="text-2xl font-bold">Form Pesan</h2>
      <form action={bookingMobil} className="flex flex-col gap-6">
        <input name="nama" placeholder="Nama" required />
        <input name="tanggal" type="date" required />
        <SubmitButton />
      </form>
    </div>
  );
}

Komponen tombol submit:

// app/pesan/SubmitButton.tsx

"use client";

import { useFormStatus } from "react-dom";

export function SubmitButton() {
  const { pending } = useFormStatus();

  return (
    <button
      type="submit"
      disabled={pending}
      className="px-4 py-2 bg-blue-600 text-white rounded disabled:opacity-50"
    >
      {pending ? "Memesan..." : "Pesan Sekarang"}
    </button>
  );
}

Halaman terima kasih:

// app/terima-kasih/page.tsx
export default function Page() {
  return (
    <div className="flex flex-col items-center justify-center min-h-screen">
      <h2 className="text-2xl font-bold">Terima kasih sudah memesan</h2>
    </div>
  );
}

Hasilnyaa:

Server Action
Server Action

πŸ“Œ Catatan Tambahan

Agar ini bekerja:

  • Proyek kamu wajib pakai App Router (/app/, bukan /pages/)
  • File harus berada di direktori app/ dan di-render di server
  • Pastikan Next.js >=15 dan React >=19

πŸ’¨ Contoh: Optimistic UI dengan useOptimistic()

Tampilkan jumlah total booking yang langsung naik ketika user klik tombol, tanpa harus nunggu server bales.

βœ… Kode Lengkap

Komponen useOptimistic:

"use client";

import { useOptimistic, useTransition, useState } from "react";

export function TotalBooking() {
  const [serverJumlah, setServerJumlah] = useState<number>(20); // nilai awal dari DB
  const [optimisticJumlah, setOptimisticJumlah] = useOptimistic(serverJumlah);
  const [isPending, startTransition] = useTransition();

  const tambahBooking = () => {
    startTransition(() => {
      setOptimisticJumlah((prev) => prev + 1);

      fetch("/api/tambah-booking", { method: "POST" }) // dummy
        .then(() => setServerJumlah((prev) => prev + 1));
    });
  };

  return (
    <div className="mb-4 space-y-2">
      <p className="text-lg">Total Booking: {optimisticJumlah}</p>
      <button
        onClick={tambahBooking}
        disabled={isPending}
        className="px-4 py-2 bg-green-600 text-white rounded disabled:opacity-50"
      >
        {isPending ? "Memproses..." : "Booking Lagi"}
      </button>
    </div>
  );
}

Hasilnya:

useOptimistic()
useOptimistic()

πŸ“Œ Catatan Singkat:

  • useOptimistic(value) sekarang hanya butuh 1 argumen: nilai awal
  • setOptimisticValue((prev) => ...) digunakan untuk perubahan lokal
  • Setelah server update sukses, kamu wajib update state asli (setJumlahServer) agar sinkron

πŸ” Contoh: Validasi Nama Penyewa dengan useActionState()

Validasi input langsung dari server tanpa harus bikin state dan handler manual di client.

βœ… Kode Lengkap

"use client";

import { pesanMobil } from "./action";
import { SubmitButton } from "./SubmitButton";
import { useActionState } from "react";

const Form = () => {
  const [state, formAction] = useActionState(pesanMobil, null);
  return (
    <form action={formAction} className="space-y-4">
      <input
        name="nama"
        placeholder="Nama Penyewa"
        className="border p-2 w-full"
      />
      <input name="tanggal" type="date" className="border p-2 w-full" />
      {state?.error && <p className="text-red-500">{state.error}</p>}

      <SubmitButton />
    </form>
  );
};

export default Form;

🧠 Singkatnya...

React 19 + Next.js 15 bantu kamu bikin:

βœ… Form booking yang cepat & aman

βœ… Feedback UI yang langsung terasa

βœ… Validasi input tanpa ribet

βœ… Kode yang ringkas dan gampang dirawat

Semua fitur tadi pas banget buat proyek web sewa mobil, atau jenis aplikasi lain yang interaktif dan berbasis form.


Ini kode lengkapnya:

Halaman pesan:

// app/pesan/page.tsx

import Form from "./Form";
import { TotalBooking } from "./TotalBooking";

export default function PesanPage() {
  return (
    <div className="p-8 max-w-xl mx-auto space-y-6">
      <h1 className="text-2xl font-bold">Pesan Mobil</h1>
      <Form />
      <TotalBooking />
    </div>
  );
}

Komponen form:

// app/pesan/Form.tsx
"use client";

import { pesanMobil } from "./action";
import { SubmitButton } from "./SubmitButton";
import { useActionState } from "react";

const Form = () => {
  const [state, formAction] = useActionState(pesanMobil, null);
  return (
    <form action={formAction} className="space-y-4">
      <input
        name="nama"
        placeholder="Nama Penyewa"
        className="border p-2 w-full"
      />
      <input name="tanggal" type="date" className="border p-2 w-full" />
      {state?.error && <p className="text-red-500">{state.error}</p>}

      <SubmitButton />
    </form>
  );
};

export default Form;

Server action:

// app/pesan/action.ts
"use server";

import { redirect } from "next/navigation";

export type State = { error?: string; success?: boolean };

export async function pesanMobil(
  _: State | null,
  formData: FormData
): Promise<State> {
  const nama = formData.get("nama")?.toString() || "";
  const tanggal = formData.get("tanggal")?.toString() || "";

  if (nama.length < 3) {
    return { error: "Nama minimal 3 huruf." };
  }

  if (!tanggal) {
    return { error: "Tanggal wajib diisi." };
  }

  // Simulasi simpan ke DB
  console.log("βœ”οΈ Booking disimpan:", { nama, tanggal });

  redirect("/terima-kasih");
}

Komponen tombol submit:

// app/pesan/SubmitButton.tsx

"use client";

import { useFormStatus } from "react-dom";

export function SubmitButton() {
  const { pending } = useFormStatus();

  return (
    <button
      type="submit"
      disabled={pending}
      className="px-4 py-2 bg-blue-600 text-white rounded disabled:opacity-50"
    >
      {pending ? "Memesan..." : "Pesan Sekarang"}
    </button>
  );
}

Komponen useOptimistic:

// app/pesan/TotalBooking.tsx
"use client";

import { useOptimistic, useTransition, useState } from "react";

export function TotalBooking() {
  const [serverJumlah, setServerJumlah] = useState<number>(20); // nilai awal dari DB
  const [optimisticJumlah, setOptimisticJumlah] = useOptimistic(serverJumlah);
  const [isPending, startTransition] = useTransition();

  const tambahBooking = () => {
    startTransition(() => {
      setOptimisticJumlah((prev) => prev + 1);

      fetch("/api/tambah-booking", { method: "POST" }) // dummy
        .then(() => setServerJumlah((prev) => prev + 1));
    });
  };

  return (
    <div className="mb-4 space-y-2">
      <p className="text-lg">Total Booking: {optimisticJumlah}</p>
      <button
        onClick={tambahBooking}
        disabled={isPending}
        className="px-4 py-2 bg-green-600 text-white rounded disabled:opacity-50"
      >
        {isPending ? "Memproses..." : "Booking Lagi"}
      </button>
    </div>
  );
}

API tambah booking:

// app/api/tambah-booking/route.ts
import { NextResponse } from "next/server";

export async function POST() {
  console.log("βœ… Booking ditambahkan (dummy)");
  return NextResponse.json({ ok: true });
}

Hasilnya:

Hasil
Hasil

πŸ€” 5. Kapan Harus Dipakai?

β€œBagus sih... Tapi kapan cocoknya dipakai?”

Nah, React 19 di Next.js 15 bukan solusi buat semua jenis proyek. Tapi dia sangat cocok buat kebutuhan yang serba interaktif, responsif, dan efisien.

Kalau proyekmu punya ciri-ciri di bawah ini, kamu wajib pertimbangkan:


βœ… Cocok Dipakai Kalau...

  1. πŸ” Banyak Form & Booking
    • Misalnya: form pemesanan mobil, motor, tiket, produk, dll.
    • React 19 bikin alur submit data jauh lebih ringan, tanpa perlu bolak-balik bikin API route.
    • Dengan action() atau server action, kamu tinggal form action={handler} dan jalan.
  2. ⚑ Ingin UI Cepat Tanpa JavaScript Berat
    • React 19 memungkinkan kamu pakai Optimistic UI langsung dari server-side.
    • Hasilnya: UX tetap mulus, tanpa overuse JS di client.
  3. 🧹 Pengen Ngurangin API Handler Manual
    • Gak perlu lagi bikin POST /api/simpan, PATCH /api/edit, dsb untuk form sederhana.
    • Semua bisa langsung dari form + server action.
  4. πŸ“ˆ SEO dan UX Harus Optimal
    • Karena semua tetap jalan di server (Next.js 15), page tetap SSR/SSG friendly.
    • UX meningkat, tanpa mengorbankan SEO.

πŸ›΅ Contoh Proyek yang Pas Pakai React 19

Kalau kamu bikin proyek seperti:

  • Sewa mobil online (contoh: Tokocar)
  • Sewa motor harian
  • Booking tiket konser / event
  • E-commerce ringan (produk terbatas, checkout cepat)
  • Undangan digital dengan form RSVP
  • Aplikasi survey / polling

Maka React 19 + Next.js 15 ini cocok banget. Kamu bisa dapetin:

  • ✨ UX sekelas native app
  • 🧼 Kode lebih bersih & maintainable
  • πŸ”’ Logika tetap di server, lebih aman

🀷 Kapan Belum Perlu?

Kalau kamu bikin:

  • Landing page statis tanpa interaksi
  • Blog sederhana hanya butuh Markdown
  • Aplikasi super berat di sisi client (like drag & drop canvas editor)

...React 19 tetap bisa dipakai, tapi nggak terlalu ngasih dampak besar dibanding benefit-nya di proyek interaktif.


Intinya:

Kalau aplikasi kamu β€œklik β†’ loading β†’ kirim data”, React 19 adalah upgrade yang pantas banget dicoba.

🧼 6. Bonus: Hidup Lebih Simpel dengan React 19

React 19 ngajarin kita satu hal penting:

β€œJangan ribet kalau bisa simpel.”

Dulu, bikin form itu kayak bikin soto dari nol:

  • Harus useState buat tiap input
  • useEffect buat syncing
  • handleSubmit() manual
  • Panggil fetch()
  • Bikin API handler lagi di /api/...

Capek? Jelas πŸ˜…


Tapi sekarang?

Dengan React 19 + Next.js 15 kamu cukup:

<form action={handleBooking}>
  <input name="nama" />
  <button>Pesan</button>
</form>

Dan di handleBooking, kamu tulis aja:

'use server'
export async function handleBooking(formData: FormData) {
  // Akses & simpan data langsung
}

That’s it.

πŸ“¦ Semua logic tetap di server

πŸ”’ Aman

⚑ Simpel

πŸ’‘ Gampang dibaca & dirawat


Penutup

Sebagai penutup, adopsi React 19 di Next.js 15 adalah lebih dari sekadar pembaruan versi; ini adalah gerbang menuju potensi baru yang signifikan untuk aplikasi web Anda. Setiap rilis baru dari React dan Next.js membawa kita lebih dekat pada pengembangan yang lebih efisien dan aplikasi yang lebih responsif, dan kombinasi versi terbaru ini tidak terkecuali.

Peningkatan kinerja yang dijanjikan, terutama dengan inovasi seperti React Forget dan penyempurnaan pada Server Components, berarti aplikasi Anda tidak hanya akan terasa lebih cepat bagi pengguna, tetapi juga akan lebih mudah diskalakan seiring pertumbuhan kebutuhan. Pengembangan yang lebih intuitif dan pengalaman developer yang lebih baik juga akan membebaskan Anda untuk fokus pada fitur inti dan inovasi, mengurangi boilerplate dan kompleksitas yang tidak perlu.

Mempertimbangkan migrasi ke kombinasi teknologi terkini ini adalah langkah strategis, terutama jika Anda sedang membangun aplikasi baru atau ingin merevitalisasi proyek yang sudah ada. Ini adalah kesempatan untuk tidak hanya mengikuti tren, tetapi untuk benar-benar berada di garis depan inovasi web, memanfaatkan tool yang dirancang untuk performa dan produktivitas maksimal.

Jadi, apakah Anda siap untuk membawa proyek Anda ke level berikutnya dengan memanfaatkan kekuatan penuh dari React 19 dan Next.js 15? Mulailah eksplorasi, pahami nuansanya, dan rasakan sendiri bagaimana perpaduan teknologi ini dapat mengubah cara Anda membangun web, membuka jalan bagi aplikasi yang lebih cepat, lebih efisien, dan lebih menyenangkan untuk dikembangkan.