Programming
Mengenal TanStack Start: Framework React Full-Stack yang Type-Safe
Daftar isi
- Apa Itu TanStack Start dan Kenapa Ramai Dibicarakan
- Konsep Inti yang Wajib Dipahami
- TanStack Start vs Next.js vs React Router: Pilih yang Mana?
- Prasyarat Sebelum Mulai
- Step 1: Bikin Proyek TanStack Start Baru
- Error Umum di Tahap Ini
- Step 2: Pahami Struktur Proyek dan Routing
- Step 3: Ambil Data dengan Loader
- Step 4: Bikin Server Functions dengan createServerFn
- Step 5: Tambahkan Database dan Autentikasi
- Setup Better Auth dengan Prisma
- Alternatif: OAuth 2.0 dengan Provider Sendiri
- Step 6: Lindungi Route dengan beforeLoad
- Step 7: Deploy ke Produksi
- Opsi A: Cloudflare Workers
- Opsi B: Bun + Nitro
- Perbandingan Opsi Deployment
- Error Umum dan Cara Mengatasinya
- Tips dari Pengalaman
- TanStack Start vs Next.js vs Remix: Jujur, Pilih yang Mana?
- Kapan TanStack Start Jadi Pilihan Tepat
- Optimasi Performa yang Sering Dilupakan
- Pertanyaan yang Sering Muncul
- Middleware: Satu Tempat buat Logika yang Berulang
- Server Routes: Saat Butuh Endpoint API Beneran
- Menguji Aplikasi Sebelum Naik ke Produksi
- Memantau Aplikasi Setelah Rilis
- Menjaga Environment Variables Tetap Aman
- Ikut Perkembangan Menuju Versi 1.0
- Integrasi TanStack Query: Duet yang Sayang Dilewatkan
- Autentikasi dengan Better Auth: Dari Nol Sampai Sesi Jalan
- Mau Deploy ke Mana? Panduan Singkat Memilih Platform
- Migrasi Bertahap dari Next.js Tanpa Drama
- Optimasi Performa: Prefetching yang Bikin Navigasi Terasa Instan
- Menangani Error Tanpa Bikin User Bingung
- SEO dan Meta Tags: Jangan Sampai Kelupaan
- Jebakan Umum yang Sering Bikin Garuk Kepala
- Testing: Biar Nggak Deg-degan Tiap Kali Deploy
- Middleware: Logika yang Nggak Perlu Ditulis Berulang
- Checklist Sebelum Rilis ke Produksi
- Kesimpulan
Kalau Teman-Teman lagi cari framework React full-stack yang type-safe dari ujung ke ujung, TanStack Start layak banget masuk daftar pertimbangan. Framework ini dibangun di atas TanStack Router, Vite, dan Nitro, dengan dukungan SSR full-document, streaming, dan server functions yang bisa dipanggil kayak fungsi biasa. Di tulisan ini, MUGHU bakal ajak kalian kenalan dari nol, bikin proyek pertama, sampai ngebahas jebakan-jebakan yang sering bikin frustrasi.
TanStack Start adalah framework React full-stack yang menggabungkan routing type-safe dari TanStack Router dengan lapisan server: SSR, streaming, server functions, server routes, dan output build yang bisa dideploy ke hampir semua platform hosting.
Ringkasnya begini: artikel ini membahas apa itu TanStack Start, kenapa dia beda dari Next.js dan React Router, cara setup proyek dari awal, cara bikin route dan loader, cara pakai server functions, sampai autentikasi dan deployment ke Cloudflare Workers maupun Bun. Ada juga tabel perbandingan, error umum, dan tips dari pengalaman MUGHU sendiri.
Apa Itu TanStack Start dan Kenapa Ramai Dibicarakan
Kalau Teman-Teman pernah pakai TanStack Query atau TanStack Table, nama TanStack pasti nggak asing. Mereka tim di balik banyak library populer di ekosistem React, dipimpin Tanner Linsley.
TanStack Start adalah "lem" yang menyatukan semua itu jadi framework utuh. Tiga fondasi utamanya:
- TanStack Router — routing berbasis file yang benar-benar type-safe. Params, search params, dan data loader semuanya diketik otomatis.
- Vite — build tool dan dev server yang cepat, dengan ekosistem plugin yang luas.
- Nitro — runtime server yang bikin aplikasi bisa dideploy ke Node, Bun, Cloudflare Workers, Netlify, dan banyak platform lain.
Yang menarik, Start memilih pendekatan client-first. Menurut Tanner Linsley sendiri, Start pada dasarnya adalah TanStack Router yang diberi lapisan tipis server-side rendering. Jadi pengalaman membangunnya tetap terasa seperti bikin SPA, tapi dengan kekuatan server penuh.
Adopsinya juga bukan main-main. Lovable, platform AI app builder, sekarang menjadikan TanStack Start sebagai fondasi semua proyek baru mereka sejak Mei — alasan utamanya type safety dan fleksibilitas deployment. Bun dan Cloudflare juga sudah punya dokumentasi resmi untuk framework ini.
Konsep Inti yang Wajib Dipahami
Sebelum ngoding, ada empat konsep yang jadi tulang punggung TanStack Start:
- File-based routing yang type-safe — route adalah file, dan path-nya dicek compiler terhadap struktur folder asli.
- Loader — fungsi pengambil data yang jalan sebelum route dirender, baik di server (saat SSR) maupun di client (saat navigasi).
- Server functions — fungsi yang dibuat dengan
createServerFn, badannya hanya jalan di server, tapi bisa dipanggil dari mana saja seperti fungsi async biasa. - Integrasi TanStack Query — cache query di-hydrate dari server, jadi data hasil SSR nggak perlu di-fetch ulang di client.
Server function adalah fungsi yang didefinisikan dengan
createServerFndan diubah jadi endpoint RPC saat build. Di bundle client, isi fungsinya diganti stubfetch()yang diketik penuh, jadi kode database, secret, dan cookie nggak pernah bocor ke browser.
Analogi gampangnya: server function itu kayak drive-thru. Teman-Teman tinggal "pesan" dari client, dapurnya (server) yang masak, dan kalian nggak perlu tahu isi dapur — tapi menunya (tipe datanya) jelas tertulis.
TanStack Start vs Next.js vs React Router: Pilih yang Mana?
Ini pertanyaan yang paling sering MUGHU dapat. Jawaban jujurnya: tergantung kebutuhan. Tapi biar nggak menggantung, ini perbandingannya.
| Kriteria | TanStack Start | Next.js (App Router) | React Router / Remix |
|---|---|---|---|
| Build tool | Vite | Webpack / Turbopack | Vite |
| Model server | Server functions (RPC) | React Server Components + Server Actions | Loader + Action |
| Type safety routing | Penuh, termasuk search params | Path string tanpa cek compile-time | Sebagian |
| Deployment | Ke mana saja via Nitro | Optimal di Vercel | Fleksibel |
| Rendering | SSR, SSG, CSR per route | RSC-first | SSR, CSR |
| Kematangan ekosistem | Lebih muda, berkembang cepat | Paling matang | Matang |
| Integrasi TanStack Query | First-class | Manual | Manual |
Rekomendasi berdasarkan skenario:
- Pilih TanStack Start kalau kalian sudah cinta TanStack Query, butuh type safety end-to-end, dan mau bebas deploy ke Cloudflare, Bun, atau server sendiri.
- Pilih Next.js kalau tim bergantung berat pada React Server Components, butuh tutorial melimpah, atau memang mau deploy di Vercel.
- Pilih React Router kalau proyeknya migrasi dari Remix atau butuh ekosistem yang sudah lama teruji.
Yang perlu dicatat soal kejujuran: TanStack Start saat ini berstatus Release Candidate (RC). API-nya dianggap stabil menjelang versi 1.0, tapi tim TanStack sendiri menyarankan untuk pin versi dependensi dan rajin baca release notes. Komunitasnya juga masih lebih kecil dari Next.js, jadi bersiaplah lebih sering baca dokumentasi resmi ketimbang Stack Overflow.
Oh iya, buat Teman-Teman yang main di ekosistem Solid: TanStack Router, Start, dan Query sekarang juga mendukung Solid 2.0 beta, jadi Start bukan cuma soal React.
Prasyarat Sebelum Mulai
Siapkan dulu hal-hal ini biar perjalanan lancar:
- Node.js versi LTS terbaru (atau Bun kalau mau jalur alternatif) terpasang di komputer.
- Pemahaman dasar TypeScript dan React — nggak perlu jago, yang penting paham komponen dan hooks.
- Editor kode seperti VS Code.
- Terminal yang nyaman dipakai.
Kenapa TypeScript penting? Karena nilai jual utama TanStack Start adalah type safety. Kalau ditulis pakai JavaScript polos, Teman-Teman kehilangan setengah keuntungannya — kayak beli mobil sport tapi jalannya di gang sempit.
Step 1: Bikin Proyek TanStack Start Baru
Cara tercepat adalah pakai starter resmi. Clone contoh basic dengan perintah ini:
npx gitpick TanStack/router/tree/main/examples/react/start-basic start-basic
cd start-basic
npm install
npm run dev
Kalau Teman-Teman pengguna Bun, ada jalur yang lebih mulus lagi lewat CLI interaktif:
bunx @tanstack/cli create my-tanstack-app
cd my-tanstack-app
bun --bun run dev
Output yang diharapkan: dev server jalan di http://localhost:3000 dan muncul halaman sederhana dengan navigasi yang sudah berfungsi.
Kenapa langkah ini penting? Starter resmi sudah mengurus konfigurasi Vite, plugin TanStack Start, dan struktur folder standar. Setup dari nol memang bisa (ada panduan build from scratch di dokumentasi resmi TanStack Start), tapi buat proyek pertama, mending fokus ke konsepnya dulu.
Error Umum di Tahap Ini
- Port 3000 sudah dipakai — matikan aplikasi lain atau set variabel
PORT. - Versi Node terlalu lama — TanStack Start butuh fitur modern; upgrade ke LTS terbaru.
- Instalasi gagal di jaringan kantor — coba ganti registry npm atau pakai jaringan lain, ini masalah klasik proxy.
Step 2: Pahami Struktur Proyek dan Routing
Setelah proyek jalan, buka folder src/routes/. Di sinilah jantung aplikasi berada. Struktur khasnya seperti ini:
src/
├── routes/
│ ├── __root.tsx → layout root (header, scripts, dsb.)
│ ├── index.tsx → halaman utama "/"
│ ├── api/ → server routes / API
│ └── recipe/
│ └── $id/
│ └── index.tsx → route dinamis "/recipe/:id"
├── components/
├── lib/
└── utils/
Beberapa aturan main yang perlu diingat:
Baca juga OpenCode Free: Agen AI Coding Gratis dan Open Source
- Tanda
$menandakan segmen dinamis. Folder$idberarti route menerima parameterid. - Awalan
_(misal_authenticated) bikin pathless layout route — layout pembungkus tanpa menambah segmen URL. Ini pola favorit buat proteksi halaman. - File
__root.tsxadalah layout paling luar, tempat ideal buat komponenHeaderyang tampil di semua halaman.
Route dibuat dengan createFileRoute, dan path-nya dicek terhadap struktur file asli:
// src/routes/index.tsx
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Home,
});
function Home() {
return <h1>Halo dari TanStack Start!</h1>;
}
Kenapa ini penting? Kalau kalian salah ketik path atau memindahkan file tanpa update kode, compiler langsung protes — bukan user kalian yang nemu error 404 di produksi. Ini beda jauh sama routing berbasis string biasa.
Step 3: Ambil Data dengan Loader
Loader adalah cara resmi mengambil data sebelum halaman dirender. Dia jalan di server saat request pertama (SSR) dan di client saat navigasi antar halaman.
export const Route = createFileRoute("/")({
component: Home,
loader: async () => {
const recipes = await getRecipes();
if (!recipes) {
throw new Error("Gagal mengambil data resep");
}
return recipes;
},
});
function Home() {
const recipes = Route.useLoaderData();
return (
<div>
{recipes.map((r) => (
<RecipeCard key={r.id} recipe={r} />
))}
</div>
);
}
Yang bikin nyaman: Route.useLoaderData() mengembalikan tipe yang persis sama dengan return loader. Nggak ada tebak-tebakan, nggak ada any yang nyelip.
Buat route dinamis, parameter tersedia lewat params:
export const Route = createFileRoute("/recipe/$id/")({
loader: async ({ params }) => {
const recipe = await getRecipeById({ data: params.id });
return recipe;
},
component: RecipeDetail,
});
Kenapa loader penting buat SEO? Karena dengan SSR, server menjalankan loader lalu mengirim HTML yang sudah berisi konten ke browser. Crawler mesin pencari dan pratinjau media sosial langsung dapat isi halaman — nggak perlu menunggu JavaScript dieksekusi. Ini alasan utama Lovable pindah ke Start: aplikasi client-side rendering murni sering terlihat kosong di mata crawler yang nggak mengeksekusi JavaScript.
Step 4: Bikin Server Functions dengan createServerFn
Ini fitur andalannya. Server function ditulis di file biasa, tapi saat build, Vite memisahkan kodenya: badan fungsi masuk bundle server, sementara bundle client cuma dapat stub fetch() yang diketik.
// src/utils/serverActions/recipes.ts
import { createServerFn } from "@tanstack/react-start";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export const getRecipes = createServerFn({ method: "GET" }).handler(
async () => {
return prisma.recipe.findMany({
where: { isPublic: true },
});
}
);
export const getRecipeById = createServerFn({ method: "GET" })
.validator((id: string) => id)
.handler(async ({ data }) => {
const recipe = await prisma.recipe.findUnique({
where: { id: data },
include: { author: true },
});
if (!recipe) {
throw new Error(`Resep dengan ID ${data} tidak ditemukan`);
}
return recipe;
});
Beberapa hal yang layak digarisbawahi:
- Method
validatordipakai buat memvalidasi input. Contoh di atas cuma validasi tipe dasar — di proyek serius, pakai library seperti Zod biar input yang aneh-aneh langsung ditolak. - Kode Prisma, secret, dan koneksi database nggak pernah ikut ke bundle browser.
- Fungsi ini bisa dipanggil dari loader, komponen, bahkan event handler — rasanya kayak manggil fungsi lokal.
Kenapa model ini efisien? Dulu, logika backend harus dipisah jadi edge function terpisah dengan URL dan deploy sendiri. Sekarang, satu file, satu model mental, satu deploy. Kalau ada yang rusak, titik periksanya lebih sedikit — hemat waktu debugging, hemat biaya operasional.
Step 5: Tambahkan Database dan Autentikasi
Aplikasi nyata butuh data yang persisten dan user yang bisa login. Kombinasi yang populer di ekosistem Start adalah Prisma + PostgreSQL + Better Auth.
Setup Better Auth dengan Prisma
Pasang paketnya dulu:
npm install better-auth
npm install @prisma/client@latest
Lalu konfigurasi instance auth di sisi server:
// src/lib/server/auth.ts
import { betterAuth } from "better-auth";
import { reactStartCookies } from "better-auth/react-start";
import { PrismaClient } from "@prisma/client";
import { prismaAdapter } from "better-auth/adapters/prisma";
const prisma = new PrismaClient();
export const auth = betterAuth({
database: prismaAdapter(prisma, { provider: "postgresql" }),
baseURL: process.env. BASE_URL,
socialProviders: {
google: {
clientId: process.env. GOOGLE_CLIENT_ID!,
clientSecret: process.env. GOOGLE_CLIENT_SECRET!,
},
},
plugins: [reactStartCookies()],
});
Jangan lupa isi file .env:
BETTER_AUTH_SECRET={secret}
GOOGLE_CLIENT_ID={client id}
GOOGLE_CLIENT_SECRET={client secret}
BASE_URL=http://localhost:3000
DATABASE_URL={connection string}
Generate skema dan migrasi:
npx @better-auth/cli generate
npx prisma generate
npx @better-auth/cli migrate
Output yang diharapkan: jalankan npx prisma studio, dan model seperti User, Session, dan Account muncul di sidebar. Kalau muncul, migrasi berhasil.
Kenapa Better Auth cocok di sini? Konfigurasinya ringkas dan dia punya integrasi resmi dengan TanStack Start lewat plugin reactStartCookies, jadi urusan cookie session nggak perlu diracik manual.
Alternatif: OAuth 2.0 dengan Provider Sendiri
Kalau perusahaan Teman-Teman pakai Keycloak, Auth0, atau provider OIDC lain, pola yang umum dipakai adalah OAuth 2.0 dengan PKCE memakai library ringan seperti Arctic. Alurnya:
- User klik login, diarahkan ke endpoint
/api/auth/login. - Server bikin authorization URL dengan parameter
state(anti-CSRF) dancode verifier(PKCE). - User login di halaman provider, lalu dikembalikan ke
/api/auth/callback. - Server memvalidasi
state, menukar authorization code dengan token, dan membuat session. - Access token di-refresh otomatis sebelum kedaluwarsa.
Satu aturan emas yang nggak boleh dilanggar: refresh token jangan pernah dikirim ke client. Simpan di session server-side saja. MUGHU pernah lihat proyek yang menaruh refresh token di localStorage — itu resep bencana keamanan.
Step 6: Lindungi Route dengan beforeLoad
Tampilan UI saja nggak cukup buat mengamankan halaman. Kalau seseorang tahu URL halaman edit, dia bisa akses langsung. Proteksi yang benar dilakukan di level route:
export const Route = createFileRoute("/add-recipe/")({
component: RouteComponent,
beforeLoad: async () => {
const userID = await getUserID();
return { userID };
},
loader: async ({ context }) => {
if (!context.userID) {
throw redirect({ to: "/" });
}
},
});
Fungsi getUserID sendiri adalah server function yang membaca session dari middleware:
Baca juga ClinePass: Langganan Model Open Weight untuk Coding
import { createServerFn } from "@tanstack/react-start";
import { authMiddleware } from "./auth-middleware";
export const getUserID = createServerFn({ method: "GET" })
.middleware([authMiddleware])
.handler(async ({ context }) => {
return context?.user?.id;
});
Kenapa polanya begini? Di TanStack Start, middleware bertugas menyuntikkan konteks (identitas user), lalu keputusan redirect dibuat di beforeLoad atau loader. Pemisahan ini bikin logika auth bisa dipakai ulang di banyak route tanpa copy-paste.
Pola yang lebih rapi lagi: bikin pathless layout route _authenticated/route.tsx yang mengecek auth sekali, lalu semua route di dalamnya otomatis terlindungi. Sekali pasang pagar, seisi kompleks aman.
Step 7: Deploy ke Produksi
Ini bagian di mana Nitro bersinar. Aplikasi yang sama bisa dideploy ke banyak target tanpa mengubah cara menulis route.
Opsi A: Cloudflare Workers
Cara paling cepat, cukup satu perintah di proyek tanpa file konfigurasi Wrangler:
npx wrangler deploy
Wrangler otomatis mendeteksi TanStack Start dan men-generate konfigurasinya. Buat setup manual, tambahkan plugin Cloudflare di vite.config.ts:
import { defineConfig } from "vite";
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import { cloudflare } from "@cloudflare/vite-plugin";
import react from "@vitejs/plugin-react";
export default defineConfig({
plugins: [
cloudflare({ viteEnvironment: { name: "ssr" } }),
tanstackStart(),
react(),
],
});
Bonusnya, server functions bisa langsung mengakses binding Cloudflare seperti R2, KV, Queues, dan Workflows lewat import { env } from "cloudflare:workers". Detailnya ada di dokumentasi Cloudflare Workers.
Opsi B: Bun + Nitro
Buat yang hosting di server sendiri atau platform seperti Railway:
bun add nitro
// vite.config.ts
import { nitro } from "nitro/vite";
const config = defineConfig({
plugins: [
tanstackStart(),
nitro({ preset: "bun" }),
],
});
export default config;
Lalu di package.json:
{
"scripts": {
"build": "bun --bun vite build",
"start": "bun run .output/server/index.mjs"
}
}
Output yang diharapkan: setelah bun run build && bun run start, server produksi jalan di port 3000.
Catatan penting kalau deploy ke Vercel: jangan pakai preset bun di Nitro — pakai konfigurasi runtime Vercel (runtime: "bun1.x") atau set bunVersion di vercel.json.
Perbandingan Opsi Deployment
| Target | Cocok Untuk | Catatan |
|---|---|---|
| Cloudflare Workers | Edge, latensi rendah, binding R2/KV | Butuh flag nodejs_compat |
| Bun (custom server) | VPS sendiri, kontrol penuh | Bisa atur preload aset statis |
| Vercel | Tim yang sudah di ekosistem Vercel | Jangan pakai preset bun Nitro |
| Node via Nitro | Hosting tradisional | Paling universal |
Error Umum dan Cara Mengatasinya
Dari pengalaman MUGHU bolak-balik nyoba framework ini, ini daftar masalah yang paling sering muncul:
Route.useLoaderData()tipe-nyaunknown— biasanya karenarouteTree.gen.tsbelum di-regenerate. Restart dev server, file itu dibuat otomatis.- Server function error "cannot be called on client" — cek apakah kalian mengimpor modul server-only (misal Prisma) langsung di komponen, bukan lewat server function.
- Redirect di loader nggak jalan — pastikan pakai
throw redirect({...}), bukanreturn redirect({...}). Katathrow-nya wajib. - Session hilang setelah logout tapi halaman nggak berubah — panggil
router.invalidate()setelahsignOut()biar router mengevaluasi ulang loader danbeforeLoad. - Env variable bocor ke client — di ekosistem Vite, hanya variabel berawalan
VITE_yang masuk bundle client. Secret database jangan pernah diberi prefix itu. - Build gagal setelah update paket — status RC berarti API masih bisa bergeser. Pin versi di
package.jsondan baca release notes sebelum upgrade.
Tips dari Pengalaman
Beberapa pelajaran yang MUGHU dapat dengan cara yang agak menyakitkan, biar Teman-Teman nggak perlu mengulanginya:
- Aktifkan
defaultPreload: 'intent'di router. Route di-preload saat user hover link, jadi navigasi terasa instan. Gratis, cuma satu baris. - Validasi input server function pakai Zod sejak awal. Validator tipe dasar nggak melindungi dari data jahat — server function itu endpoint publik, perlakukan seperti API.
- Pisahkan file berdasarkan boundary: taruh kode server-only di file dengan pola nama jelas (misal
*.server.ts). Selain rapi, ini bikin AI coding assistant dan rekan setim langsung paham mana yang jalan di mana. - Manfaatkan fungsi
headdi route buat set judul dan meta per halaman dari data loader. SEO per halaman jadi otomatis, nggak perlu API metadata terpisah. - Prerender halaman statis (landing page, blog) dengan opsi
prerender: { enabled: true }di plugin. Halaman yang isinya sama buat semua orang nggak perlu dirender ulang tiap request. - Pertimbangkan matang-matang kalau tim bergantung pada RSC. TanStack Start memakai model loader + server function, bukan React Server Components. Buat sebagian besar aplikasi ini cukup, tapi kalau arsitektur kalian sudah RSC-berat, migrasinya bukan sekadar ganti import.
- Buat proyek serius, siapkan ekspektasi realistis soal komunitas. Dokumentasinya bagus, tapi jumlah tutorial pihak ketiga belum sebanyak Next.js. Anggap itu trade-off dari memakai teknologi yang lebih muda dengan desain yang lebih modern.
Terakhir, kalau mau lihat semua primitif ini dalam aplikasi utuh — bukan cuma potongan kode — proyek contoh resmi di GitHub TanStack plus tutorial recipe app dari komunitas adalah tempat belajar terbaik. Clone, oprek, rusak, perbaiki. Cara itu jauh lebih nempel di kepala daripada sekadar baca.
TanStack Start vs Next.js vs Remix: Jujur, Pilih yang Mana?
Ini pertanyaan yang paling sering mampir ke MUGHU tiap kali bahas framework ini. Jawabannya nggak bisa satu kalimat, jadi mari kita bedah pelan-pelan.
Next.js masih juara soal ekosistem. Tutorial ada di mana-mana, komunitasnya raksasa, dan hampir semua library React punya contoh integrasi dengannya. Kekuatannya ada di React Server Components dan App Router — cocok buat aplikasi yang berat di konten dan butuh rendering granular per komponen. Tapi ada harganya: model mentalnya makin kompleks. Batas antara server dan client component sering bikin bingung, dan caching bawaannya kadang terasa seperti sihir yang susah di-debug.
Remix (sekarang menyatu ke React Router v7) punya filosofi mirip TanStack Start: loader, action, dan web standard. Bedanya, TanStack Start menang telak di sisi type safety. Di Remix, kalian masih perlu type assertion di beberapa titik. Di TanStack Start, dari definisi route sampai useLoaderData(), semuanya ter-infer otomatis. Salah ketik nama param? TypeScript langsung teriak sebelum kode jalan.
TanStack Start sendiri unggul di tiga hal: routing yang 100% type-safe, server functions yang terasa seperti manggil fungsi biasa, dan fleksibilitas deployment lewat Vite. Kalian nggak dikunci ke satu vendor hosting. Mau Cloudflare, VPS sendiri, atau Vercel — tinggal ganti plugin, logika aplikasi nggak berubah.
| Aspek | TanStack Start | Next.js | Remix / RR7 |
|---|---|---|---|
| Type safety routing | Penuh, otomatis | Sebagian | Sebagian |
| Model rendering | SSR + loader | RSC + SSR | SSR + loader |
| Ekosistem & tutorial | Berkembang | Terbesar | Besar |
| Vendor lock-in | Minim | Cenderung Vercel | Minim |
| Kurva belajar | Landai kalau paham React Query | Curam (RSC) | Landai |
Kapan TanStack Start Jadi Pilihan Tepat
Dari pengalaman MUGHU, framework ini paling pas buat:
- Aplikasi dashboard atau SaaS yang interaksinya padat. Kombinasi loader + TanStack Query bikin manajemen data server terasa alami, tanpa tarik-ulur state manual.
- Tim yang serius sama TypeScript. Kalau kalian tipe yang risih lihat
anyberkeliaran, inference penuh dari route sampai komponen itu nikmat banget. - Proyek yang butuh kontrol deployment. Karena berdiri di atas Vite, kalian bebas pilih target tanpa nulis ulang arsitektur.
Sebaliknya, pikirkan dua kali kalau proyek kalian sangat bergantung pada React Server Components, atau timnya butuh banyak referensi tutorial pihak ketiga buat onboarding cepat.
Optimasi Performa yang Sering Dilupakan
Setup jalan itu satu hal. Bikin aplikasinya kencang itu cerita lain. Beberapa hal yang sering di-skip padahal dampaknya besar:
Baca juga Kiro Dev: IDE Agentic AWS untuk Spec-Driven Development
Selective SSR per route. Nggak semua halaman butuh dirender di server. Halaman yang isinya murni interaktif — misal editor atau kanvas — bisa dilewatkan dari SSR biar server nggak kerja sia-sia:
export const Route = createFileRoute("/editor")({
ssr: false,
component: EditorPage,
});
Defer data yang lambat. Kalau loader kalian nunggu query berat, jangan blokir seluruh halaman. Kembalikan promise mentah, lalu render bagian itu dengan Await dan tampilkan skeleton dulu. Halaman muncul cepat, data berat menyusul.
Perhatikan Core Web Vitals. Metrik seperti LCP dan INP sekarang jadi faktor peringkat pencarian. Panduan lengkap soal target angkanya bisa Teman-Teman baca di web.dev. Kabar baiknya, arsitektur SSR + streaming TanStack Start sudah kasih fondasi bagus — sisanya soal disiplin: kompres gambar, jangan muat JavaScript yang nggak dipakai, dan manfaatkan prerender buat halaman statis.
Cache di level HTTP. Buat halaman yang datanya jarang berubah, set header Cache-Control dari loader atau server function. Kalau deploy di Cloudflare Workers, tambahkan cache di edge — respons kedua dan seterusnya nggak perlu nyentuh origin sama sekali.
Pertanyaan yang Sering Muncul
Apakah TanStack Start sudah siap buat produksi? Statusnya release candidate, dan sudah banyak yang pakai di produksi. Sarannya tetap sama: pin versi, baca release notes tiap upgrade, dan tulis test buat alur kritis. Itu praktik sehat di framework mana pun, bukan cuma di sini.
Harus paham TanStack Router dulu? Nggak harus, karena Start dibangun di atasnya dan kalian belajar keduanya sekaligus. Tapi kalau sempat, baca bagian routing di dokumentasi resmi TanStack — konsep search params yang type-safe di sana termasuk fitur paling keren yang jarang dibahas.
Bisa dipakai bareng TanStack Query? Justru itu kombinasi idealnya. Loader dipakai buat data awal, Query mengelola caching dan refetching di sisi client. Keduanya dari tim yang sama, jadi integrasinya mulus — ada setupRouterSsrQueryIntegration yang menyambungkan hidrasi data otomatis.
Gimana nasib SEO kalau bukan RSC? Aman. SSR penuh berarti crawler menerima HTML utuh, bukan halaman kosong yang nunggu JavaScript. Ditambah fungsi head per route buat meta tag dinamis dan prerender buat halaman statis, kebutuhan SEO standar sudah tercakup semua.
Migrasi dari SPA Vite biasa susah nggak? Ini salah satu jalur migrasi paling enak. Karena sama-sama Vite, kalian nggak ganti bundler — cukup tambah plugin, susun ulang route ke struktur file-based, lalu pindahkan fetching ke loader secara bertahap. Aplikasi tetap jalan selama proses transisi, jadi nggak perlu big bang rewrite yang bikin deg-degan.
Berapa lama waktu yang wajar buat produktif? Kalau Teman-Teman sudah nyaman dengan React dan pernah pakai React Query, satu akhir pekan cukup buat bikin aplikasi CRUD lengkap dengan autentikasi. Bagian yang paling butuh pembiasaan biasanya pola throw redirect dan alur beforeLoad — setelah dua-tiga kali pakai, rasanya jadi natural.
Middleware: Satu Tempat buat Logika yang Berulang
Setelah aplikasi mulai membesar, biasanya muncul kebutuhan yang sama di banyak tempat: cek sesi, catat log, ukur durasi request. Daripada copy-paste di tiap server function, TanStack Start punya konsep middleware yang bisa dirangkai dan dipakai ulang.
import { createMiddleware } from "@tanstack/react-start";
const logMiddleware = createMiddleware({ type: "function" }).server(
async ({ next }) => {
const mulai = Date.now();
const hasil = await next();
console.log(`Selesai dalam ${Date.now() - mulai}ms`);
return hasil;
},
);
const authMiddleware = createMiddleware({ type: "function" })
.middleware([logMiddleware])
.server(async ({ next }) => {
const session = await getSession();
if (!session) throw new Error("Unauthorized");
return next({ context: { userId: session.userId } });
},
);
Perhatikan bagian next({ context }). Nilai yang Teman-Teman masukkan di situ bakal muncul di handler berikutnya — lengkap dengan tipenya. Jadi begitu server function pakai authMiddleware, context.userId langsung tersedia dan TypeScript tahu itu bukan undefined. Nggak ada lagi tebak-tebakan isi context.
Pengalaman MUGHU, pola yang paling sering dipakai itu tiga lapis: logging paling luar, autentikasi di tengah, lalu validasi rate limit paling dalam buat endpoint yang sensitif. Sekali disusun, semua server function tinggal pasang.
Server Routes: Saat Butuh Endpoint API Beneran
Server function itu enak buat komunikasi internal aplikasi. Tapi ada momen kalian butuh endpoint HTTP sungguhan — misal buat webhook pembayaran, callback OAuth, atau API publik yang dipanggil aplikasi lain. Di sinilah server routes masuk.
// routes/api/webhook.ts
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/api/webhook")({
server: {
handlers: {
POST: async ({ request }) => {
const payload = await request.json();
// verifikasi signature, proses event, dst.
return Response.json({ received: true });
},
},
},
});
Yang bikin nyaman: semuanya pakai standar web Request dan Response, bukan objek buatan framework. Kalau Teman-Teman pernah baca dokumentasi Fetch API di MDN, pengetahuan itu langsung terpakai di sini tanpa perlu belajar abstraksi baru. Efek sampingnya juga bagus buat portabilitas — handler yang sama jalan mulus di Node, Bun, maupun Cloudflare Workers.
Satu jebakan yang pernah bikin MUGHU garuk-garuk kepala: webhook dari payment gateway biasanya butuh raw body buat verifikasi signature. Jangan panggil request.json() dulu — ambil request.text(), verifikasi, baru parse. Urutannya kebalik sedikit saja, signature nggak akan pernah cocok.
Menguji Aplikasi Sebelum Naik ke Produksi
Karena Start berdiri di atas Vite, urusan testing jadi jauh lebih ringan. Vitest tinggal pasang tanpa konfigurasi aneh-aneh, dan komponen route bisa diuji kayak komponen React biasa.
Strategi yang menurut MUGHU paling masuk akal dibagi tiga lapis:
- Unit test buat server function. Karena handler-nya cuma fungsi async biasa, kalian bisa panggil langsung di test dengan input palsu. Mock koneksi database-nya, lalu pastikan validasi dan logikanya benar.
- Test komponen dengan router palsu. TanStack Router menyediakan
createRouteryang bisa diarahkan ke memory history. Jadi kalian bisa render satu route lengkap dengan loader data tiruan, tanpa perlu server jalan. - End-to-end pakai Playwright. Buat alur kritis — login, checkout, submit form — nggak ada gantinya selain test yang beneran buka browser. Jalankan
vite buildlaluvite preview, arahkan Playwright ke situ.
Yang sering dilupakan: test buat beforeLoad. Logika redirect itu gampang banget rusak diam-diam pas refactor. Tulis satu-dua test yang memastikan user tanpa sesi terlempar ke halaman login, dan user dengan sesi bisa lewat. Lima menit nulisnya, tapi berkali-kali menyelamatkan MUGHU dari bug yang memalukan.
Memantau Aplikasi Setelah Rilis
Deploy sukses bukan garis akhir. Justru setelah user beneran masuk, cerita menariknya dimulai. Ada tiga hal yang layak dipasang sejak hari pertama:
Baca juga Codex CLI dari OpenAI: Panduan Lengkap Ngoding di Terminal
Error tracking. Sentry punya integrasi resmi buat TanStack Start yang menangkap error di dua sisi sekaligus — client dan server function. Setup-nya cuma beberapa baris di entry file, dan tiap error datang lengkap dengan stack trace plus konteks route-nya. Detailnya bisa dicek di dokumentasi Sentry.
Log yang terstruktur. console.log berserakan itu musuh masa depan kalian sendiri. Manfaatkan middleware logging yang tadi dibahas — satu tempat, format konsisten, gampang dicari pas ada insiden jam dua pagi.
Data Web Vitals dari user asli. Angka Lighthouse di laptop kalian itu kondisi ideal. User beneran pakai HP kentang di jaringan seadanya. Pasang library web-vitals, kirim metriknya ke analytics, dan pantau persentil ke-75 — bukan rata-rata, karena rata-rata suka menipu.
Menjaga Environment Variables Tetap Aman
Ini kelihatan sepele tapi sering jadi sumber kebocoran. Aturan mainnya di Start mengikuti Vite: variabel berawalan VITE_ ikut terbawa ke bundle client, sisanya cuma hidup di server.
# aman, cuma bisa diakses server function
DATABASE_URL=postgres://...
BETTER_AUTH_SECRET=rahasia-banget
# sengaja diekspos ke client
VITE_PUBLIC_APP_URL=https://app.kalian.com
Kesalahan klasiknya: menaruh API key berbayar dengan prefix VITE_ karena "biar gampang diakses". Begitu build naik, key itu bisa dibaca siapa pun yang buka DevTools. Kalau ragu sebuah nilai boleh publik atau nggak, anggap saja nggak — akses lewat server function selalu jadi jalur yang lebih aman.
Buat tim yang deploy ke Cloudflare Workers, ingat kalau secrets di sana dikelola lewat wrangler secret put, bukan file .env yang ikut ter-commit. Pisahkan juga nilai buat development dan production sejak awal biar nggak ada insiden database staging kena data user beneran.
Ikut Perkembangan Menuju Versi 1.0
Status release candidate artinya API-nya sudah dianggap stabil, tapi polesan terakhir masih berjalan. Beberapa kebiasaan kecil yang bikin proses ini nyaman diikuti:
- Kunci versi
@tanstack/react-startdan@tanstack/react-routerdi angka pasti, jangan pakai range^yang longgar. - Jadwalkan upgrade sebagai pekerjaan terencana. Baca catatan rilisnya, jalankan ulang test buat route dan server function yang penting, baru merge.
- Kalau nemu perilaku aneh, bikin reproduksi kecil dan laporkan. Tim TanStack terkenal responsif, dan masukan di fase ini beneran dibaca — banyak perbaikan di RC lahir dari laporan komunitas.
Bonusnya, ekosistem partner di sekitar TanStack juga aktif banget. Integrasi resmi buat autentikasi, database, sampai deployment terus bertambah, jadi kemungkinan besar kebutuhan Teman-Teman sudah ada jalurnya tanpa harus meracik semuanya dari nol.
Integrasi TanStack Query: Duet yang Sayang Dilewatkan
Loader di TanStack Router sudah kuat, tapi begitu dipasangkan dengan TanStack Query, rasanya kayak nemu pasangan yang memang ditakdirkan. Polanya sederhana: loader bertugas prefetch data di server, lalu Query yang mengelola cache-nya di client.
export const Route = createFileRoute('/produk/$id')({
loader: ({ context, params }) =>
context.queryClient.ensureQueryData(produkQueryOptions(params.id)),
component: HalamanProduk,
})
function HalamanProduk() {
const { id } = Route.useParams()
const { data } = useSuspenseQuery(produkQueryOptions(id))
return <DetailProduk produk={data} />
}
Kenapa pola ini enak banget dipakai? Karena kalian dapat dua dunia sekaligus. Saat halaman pertama kali dibuka, data sudah siap dari server — nggak ada loading spinner yang bikin user bengong. Setelah itu, Query mengambil alih: refetch otomatis saat tab kembali fokus, invalidation setelah mutasi, sampai optimistic update buat interaksi yang terasa instan.
Satu jebakan yang pernah bikin MUGHU debugging setengah hari: pastikan queryClient yang dipakai di loader dan di komponen itu instance yang sama, dioper lewat router context. Kalau kalian bikin instance baru di dua tempat, cache-nya nggak nyambung dan data bakal di-fetch dua kali. Gejalanya halus — aplikasi tetap jalan, cuma lebih lambat — jadi sering lolos dari pengamatan.
Buat mutasi, kombinasikan server function dengan useMutation. Server function jadi eksekutornya, Query yang mengurus状 status pending dan invalidation. Bersih, dan tiap bagian punya tanggung jawab yang jelas.
Autentikasi dengan Better Auth: Dari Nol Sampai Sesi Jalan
Better Auth belakangan jadi pilihan favorit di ekosistem TanStack, dan bukan tanpa alasan. Setup-nya ringkas, dukungan TypeScript-nya rapi, dan integrasinya dengan server function terasa natural.
Alurnya kurang lebih begini. Pertama, definisikan instance auth di sisi server lengkap dengan koneksi database dan BETTER_AUTH_SECRET yang tadi sudah kita amankan di environment variables. Kedua, buat satu server route sebagai catch-all buat endpoint auth:
// src/routes/api/auth/$.ts
import { auth } from '~/lib/auth'
export const ServerRoute = createServerFileRoute('/api/auth/$').methods({
GET: ({ request }) => auth.handler(request),
POST: ({ request }) => auth.handler(request),
})
Ketiga, cek sesi di beforeLoad pada route yang perlu proteksi — pola redirect yang sudah dibahas di bagian testing tadi. Sesi disimpan di cookie HttpOnly, jadi nggak bisa dibaca JavaScript di browser. Ini penting: token yang disimpan di localStorage itu sasaran empuk serangan XSS. Kalau mau paham lebih dalam soal atribut cookie dan implikasi keamanannya, dokumentasi MDN soal cookies layak dibaca pelan-pelan.
Pengalaman MUGHU, bagian yang paling sering bikin bingung justru bukan setup-nya, tapi memutuskan di mana pengecekan sesi diletakkan. Aturan praktisnya: taruh di route paling atas yang butuh proteksi (misalnya _authed.tsx sebagai layout route), lalu semua route anaknya otomatis ikut terlindungi. Jangan ulang-ulang pengecekan di tiap halaman — sekali di layout, beres.
Mau Deploy ke Mana? Panduan Singkat Memilih Platform
Karena Start dibangun di atas Vite dan Nitro, pilihan deployment-nya luas. Tapi luas juga bisa bikin bingung, jadi begini cara MUGHU memetakannya:
Cloudflare Workers cocok buat aplikasi yang butuh respons cepat di banyak wilayah dengan biaya rendah. Cold start-nya nyaris nggak terasa. Catatannya: runtime-nya bukan Node.js penuh, jadi cek dulu apakah library yang kalian pakai kompatibel — terutama driver database yang butuh koneksi TCP langsung.
Netlify jadi jalur paling mulus karena berstatus partner deployment resmi TanStack Start. Konfigurasinya minim, praktis tinggal hubungkan repositori dan selesai.
VPS atau server sendiri masuk akal buat tim yang butuh kontrol penuh atau punya aturan soal lokasi data. Hasil vite build bisa dijalankan sebagai server Node.js biasa di belakang Nginx, dibungkus Docker kalau mau lebih rapi.
Baca juga CodeBuddy: Editor Kode AI untuk Coding Lebih Cepat
Pertimbangan utamanya bukan "mana yang paling canggih", tapi seberapa cocok dengan kebutuhan: lokasi user, jenis database, dan kemampuan tim mengelola infrastruktur. Aplikasi internal kantor dengan user satu kota nggak butuh edge network global — VPS seharga kopi sebulan sudah lebih dari cukup.
Migrasi Bertahap dari Next.js Tanpa Drama
Banyak yang nanya ke MUGHU: "Proyek Next.js gue udah gede, worth it nggak pindah?" Jawaban jujurnya: jangan rewrite total. Hampir selalu berakhir jadi proyek zombie yang nggak kelar-kelar.
Strategi yang lebih waras itu bertahap:
- Mulai dari fitur baru. Bangun modul berikutnya sebagai aplikasi Start terpisah di subdomain atau path tersendiri, misalnya
app.domain.comsementara marketing site tetap di Next.js. - Pindahkan logika, bukan cuma tampilan. Komponen React bisa dipakai ulang hampir apa adanya. Yang perlu ditulis ulang biasanya data layer —
getServerSidePropsjadi loader, API routes jadi server function atau server route. - Samakan pola autentikasi lebih dulu. Kalau dua aplikasi berbagi sesi lewat cookie di domain yang sama, perpindahan user antar keduanya bakal mulus tanpa harus login ulang.
Perbedaan mental model yang paling terasa: di Next.js App Router kalian mikir dalam kerangka Server Components, di Start kalian mikir dalam kerangka isomorphic loader plus server function. Dua-duanya valid, tapi jangan bawa kebiasaan yang satu ke yang lain mentah-mentah. Loader di Start jalan di server saat request pertama dan di client saat navigasi — kode yang mengandalkan API khusus Node.js harus dipindah ke server function.
Soal performa hasil migrasi, ukur dengan data, bukan perasaan. Jalankan audit sebelum dan sesudah, lalu bandingkan metrik Core Web Vitals dari user asli — panduan di web.dev menjelaskan ambang batas tiap metrik dengan gamblang. Angka yang membaik jadi amunisi bagus saat presentasi ke tim, dan kalau ternyata ada yang memburuk, kalian tahu persis bagian mana yang perlu dibedah.
Optimasi Performa: Prefetching yang Bikin Navigasi Terasa Instan
Salah satu alasan aplikasi Start terasa gesit itu bukan sulap, tapi fitur preloading bawaan TanStack Router. Saat kursor user melayang di atas sebuah link, router bisa langsung mengambil data dan kode halaman tujuan di belakang layar. Begitu link-nya diklik, halamannya sudah setengah siap.
Mengaktifkannya gampang banget — cukup satu opsi di konfigurasi router:
// src/router.tsx
export function createRouter() {
return createTanStackRouter({
routeTree,
defaultPreload: 'intent',
defaultPreloadStaleTime: 30_000,
})
}
Nilai 'intent' artinya preload jalan saat user menunjukkan niat — hover atau sentuh link. Ada juga 'viewport' yang memicu preload begitu link muncul di layar, cocok buat halaman daftar artikel atau katalog produk.
Tapi ada catatan dari pengalaman MUGHU: jangan asal nyalakan preload di semua tempat. Kalau loader kalian memanggil server function yang berat (misalnya query database mahal), setiap hover jadi satu request ke server. Di halaman dengan puluhan link, itu bisa jadi banjir request. Solusinya dua: atur staleTime yang masuk akal biar hasil preload dipakai ulang, atau matikan preload di route tertentu lewat opsi per-route.
Soal code splitting, Start sudah mengerjakan bagian beratnya untuk kalian. Setiap route otomatis dipecah jadi chunk terpisah, jadi user cuma mengunduh kode halaman yang mereka buka. Yang perlu kalian jaga tinggal komponen besar di dalam halaman — chart library, editor teks kaya, atau peta interaktif sebaiknya dimuat malas pakai lazy biar nggak membebani muatan awal.
Menangani Error Tanpa Bikin User Bingung
Aplikasi yang bagus bukan yang nggak pernah error — itu mustahil — tapi yang errornya ditangani dengan sopan. Di Start, tiap route bisa punya errorComponent dan notFoundComponent sendiri:
export const Route = createFileRoute('/produk/$id')({
loader: async ({ params }) => {
const produk = await getProduk({ data: params.id })
if (!produk) throw notFound()
return produk
},
errorComponent: ({ error }) => (
<div>
<h2>Waduh, ada yang nggak beres</h2>
<p>Coba muat ulang halaman ini. Kalau masih bermasalah, kabari kami.</p>
</div>
),
notFoundComponent: () => <p>Produk yang kalian cari nggak ketemu.</p>,
})
Polanya berjenjang: kalau route anak nggak mendefinisikan penanganan error, router naik ke route induk sampai ketemu yang punya. Praktisnya, taruh errorComponent umum di root route sebagai jaring pengaman, lalu bikin versi yang lebih spesifik di route yang butuh pesan khusus.
Satu jebakan yang sering MUGHU temui: developer lupa bahwa error di server function itu menyeberang batas server-client. Pesan error dari server bakal terkirim ke browser, jadi jangan pernah lempar error yang mengandung detail sensitif — string koneksi database, jejak stack internal, atau data user lain. Bungkus error asli di server, catat lengkapnya ke sistem logging, lalu lempar pesan generik yang aman ke client.
SEO dan Meta Tags: Jangan Sampai Kelupaan
Karena Start merender halaman di server secara penuh, mesin pencari menerima HTML utuh sejak respons pertama — modal SEO yang bagus. Tapi HTML utuh saja belum cukup. Tiap halaman tetap butuh judul dan meta description yang unik, dan di Start urusan ini ditangani lewat opsi head di route:
export const Route = createFileRoute('/blog/$slug')({
loader: ({ params }) => getArtikel({ data: params.slug }),
head: ({ loaderData }) => ({
meta: [
{ title: `${loaderData.judul} — Blog MUGHU` },
{ name: 'description', content: loaderData.ringkasan },
{ property: 'og:title', content: loaderData.judul },
{ property: 'og:image', content: loaderData.gambarUtama },
],
}),
})
Perhatikan bagian menariknya: head menerima loaderData, jadi meta tags bisa dinamis mengikuti konten. Judul artikel, gambar Open Graph buat tampilan cantik saat dibagikan ke media sosial, semua diambil dari data yang sama dengan yang dirender di halaman. Nggak ada lagi cerita meta description ketinggalan update padahal isi artikelnya sudah direvisi.
Buat halaman yang nggak boleh diindeks — dashboard internal, halaman hasil pencarian, atau preview draft — tambahkan robots dengan nilai noindex. Panduan lengkap soal bagaimana Google membaca dan memproses halaman bisa kalian dalami di dokumentasi Google Search Central, termasuk soal data terstruktur kalau mau hasil pencarian kalian tampil lebih kaya.
Jebakan Umum yang Sering Bikin Garuk Kepala
Setelah beberapa proyek pakai Start, ada pola masalah yang muncul berulang. MUGHU rangkum biar Teman-Teman nggak perlu jatuh di lubang yang sama:
Hydration mismatch. Gejalanya: warning di console soal HTML server dan client yang beda. Penyebab paling umum adalah kode yang hasilnya beda antara server dan browser — Date.now(), angka acak, atau pengecekan window langsung di badan komponen. Obatnya: pindahkan logika semacam itu ke useEffect, atau pakai data dari loader yang konsisten di dua sisi.
Server function dipanggil di tempat yang salah. Server function memang bisa diimpor di file komponen, tapi isinya cuma jalan di server. Kalau kalian nggak sengaja menyelipkan logika client (akses localStorage, misalnya) ke dalamnya, errornya baru muncul saat runtime dan pesannya kadang membingungkan. Biasakan pisahkan file: logika server di ~/server/, komponen di ~/components/.
Loader yang terlalu gemuk. Godaannya nyata — karena loader bisa ambil data apa saja, semua kebutuhan halaman dijejalkan ke satu loader raksasa. Akibatnya navigasi jadi menunggu data yang sebenarnya nggak mendesak. Pecah: data penting buat render awal tetap di loader, data sekunder (komentar, rekomendasi) ambil lewat TanStack Query dengan pola useQuery biasa biar bisa tampil menyusul.
Baca juga GitHub Copilot: Cara Mempercepat Kerja Tim
Lupa validasi input server function. Ini soal keamanan, bukan sekadar kerapian. Setiap server function itu pada dasarnya endpoint yang bisa dipanggil siapa pun — jangan percaya begitu saja pada data yang masuk. Pakai inputValidator dengan skema Zod atau Valibot di tiap server function yang menerima input. Sekali kalian jadikan kebiasaan, rasanya kayak sabuk pengaman: nggak terasa sampai suatu hari benar-benar menyelamatkan.
Testing: Biar Nggak Deg-degan Tiap Kali Deploy
Satu hal yang bikin MUGHU makin betah di ekosistem Start: pengujiannya nggak ribet. Karena server function pada dasarnya cuma fungsi async biasa, kalian bisa mengujinya langsung tanpa harus menyalakan server sungguhan. Cukup impor, panggil dengan input tiruan, lalu periksa hasilnya.
Alat yang paling pas buat pekerjaan ini adalah Vitest, test runner yang memang lahir dari ekosistem Vite — fondasi yang sama dengan Start. Konfigurasinya nyaris nol karena Vitest membaca setup Vite yang sudah ada di proyek kalian.
import { describe, it, expect, vi } from 'vitest'
import { hitungOngkir } from '~/server/ongkir'
describe('hitungOngkir', () => {
it('menolak berat di bawah nol', async () => {
await expect(
hitungOngkir({ data: { berat: -1, tujuan: 'Bandung' } })
).rejects.toThrow()
})
it('menghitung tarif Jabodetabek dengan benar', async () => {
const hasil = await hitungOngkir({
data: { berat: 2, tujuan: 'Depok' },
})
expect(hasil.tarif).toBe(18000)
})
})
Perhatikan tes pertama: kita justru menguji jalur gagalnya. Ini kebiasaan yang sering dilupakan, padahal validasi input yang MUGHU singgung sebelumnya baru terasa manfaatnya kalau benar-benar diuji. Skema Zod yang menolak berat negatif itu bagus di atas kertas — tes yang membuktikannya bekerja itu yang bikin tidur nyenyak.
Buat komponen, pendekatannya sama seperti proyek React lain: pakai Testing Library dan uji dari sudut pandang user, bukan detail implementasi. Yang perlu diingat, komponen yang bergantung pada data loader butuh pembungkus router saat diuji. Jangan lewati langkah ini — errornya kalau lupa cukup misterius dan bikin buang waktu setengah jam cuma buat sadar routernya belum di-mock.
Middleware: Logika yang Nggak Perlu Ditulis Berulang
Begitu server function di proyek mulai berjumlah belasan, pola berulang mulai kelihatan: cek sesi di sini, catat log di sana, cek sesi lagi di fungsi berikutnya. Middleware hadir buat memangkas pengulangan itu.
import { createMiddleware } from '@tanstack/react-start'
const authMiddleware = createMiddleware({ type: 'function' })
.server(async ({ next }) => {
const sesi = await ambilSesi()
if (!sesi) throw new Error('Silakan login dulu')
return next({ context: { user: sesi.user } })
})
Setelah didefinisikan, tinggal pasang di server function mana pun yang butuh proteksi lewat opsi middleware. Konsepnya mirip middleware di Express kalau Teman-Teman pernah menyentuh dunia backend Node.js — ada rantai eksekusi, tiap lapisan bisa menambahkan context, dan next() meneruskan ke lapisan berikutnya. Bedanya, di Start semuanya type-safe: context user yang disuntikkan middleware langsung dikenali TypeScript di dalam handler.
Pengalaman MUGHU, tiga kandidat middleware yang hampir selalu dibutuhkan proyek nyata: autentikasi, logging durasi request, dan rate limiting buat endpoint yang rawan disalahgunakan. Mulai dari tiga itu dulu — jangan tergoda bikin rantai middleware panjang sejak awal, karena tiap lapisan menambah kompleksitas debugging.
Checklist Sebelum Rilis ke Produksi
Sebelum tombol deploy dipencet, MUGHU biasanya jalankan pemeriksaan singkat ini:
- Semua server function punya validasi input. Sekali lagi, ini endpoint publik.
- Error message di production nggak bocorin detail internal. Cek ulang bagian penanganan error tadi.
- Meta tags dinamis sudah terpasang di route konten utama, lengkap dengan Open Graph.
- Environment variables produksi terisi di dashboard platform hosting — bukan cuma di
.envlokal. - Halaman internal sudah diberi
noindexbiar nggak nyasar ke hasil pencarian. - Build produksi dicoba lokal dengan
npm run buildlalu jalankan hasilnya, jangan cuma percaya dev server.
Daftar ini kelihatan sepele, tapi lima menit mengeceknya jauh lebih murah daripada satu insiden di jam sibuk.
Kesimpulan
Membangun aplikasi dengan TanStack Start 2 pada akhirnya bukan soal menghafal API, melainkan soal membangun kebiasaan yang benar sejak awal: memperlakukan server function sebagai endpoint publik yang wajib divalidasi, menulis tes yang justru mengincar jalur gagal, dan memindahkan logika berulang seperti autentikasi dan logging ke middleware yang type-safe. Tiga kebiasaan itu terdengar sederhana, tapi dari pengalaman MUGHU, justru di situlah pembeda antara proyek yang tenang di produksi dan proyek yang bikin panik tengah malam.
Yang menarik dari Start dibanding meta-framework lain adalah bagaimana type safety mengalir dari middleware, ke server function, sampai ke komponen — kesalahan yang biasanya baru ketahuan saat runtime kini ditangkap editor sebelum kode sempat di-commit. Buat Teman-Teman yang datang dari Express atau framework React lain, kurva belajarnya nggak curam; konsepnya familiar, hanya dikemas dengan jaminan tipe yang lebih ketat.
Jangan lupa juga ritual kecil sebelum rilis: jalankan checklist produksi tadi, dari validasi input sampai mencoba build lokal. Lima menit pemeriksaan itu investasi termurah yang bisa dilakukan sebelum aplikasi menghadapi trafik nyata.
Langkah berikutnya? Buka proyek kecil, terapkan satu middleware autentikasi, tulis satu tes untuk jalur gagal, lalu rasakan sendiri bedanya. Kalau butuh referensi lebih dalam, dokumentasi resmi TanStack Start selalu jadi sumber paling akurat untuk mengikuti perkembangan framework ini. Selamat membangun — dan semoga deploy berikutnya berjalan tanpa drama.
Referensi
TanStack. (2026). TanStack Start.
TanStack. (2026). Solid 2.0 Beta Support in TanStack Router, Start, and Query.
MakerKit. (2026). What Is TanStack Start? A 2026 Guide for React Developers.
Lovable. (2026). Building apps using TanStack Start.
LogRocket. (2026). A step-by-step guide to building a full-stack app with TanStack Start.
Baca juga Mengenal Astro 7.1: Framework JavaScript Ringan untuk Website
GitHub. (2026). TanStack.
Medium. (2026). TanStack Start Authentication with OIDC/OAuth 2.0 (Keycloak Example).
YouTube. (2026). TanStack Start in 100 Seconds.
Bun. (2026). Use TanStack Start with Bun.
Cloudflare. (2026). TanStack Start on Cloudflare Workers.
Komentar (0)
Belum ada komentar. Jadilah yang pertama berbagi pendapat!
Tinggalkan komentar