Beberapa bulan lalu saya bikin landing page pakai Next.js, dan jujur, performanya oke tapi bundle size-nya bikin gerah. Halaman sederhana aja ngirim 200KB JavaScript ke browser. Terus saya denger tentang SvelteKit dari teman, katanya "kamu harus coba, bundle-nya kecil banget". Saya coba, dan ya, halaman yang sama cuma butuh 30KB JS. Beda jauh.
Kalau kamu lagi cari framework yang cepat, ringan, dan gak bikin browser pusing, SvelteKit jawabannya. Framework ini pakai Svelte 5 (versi terbaru dengan runes mode) yang compile ke vanilla JavaScript bukan virtual DOM kayak React atau Vue. Hasilnya? Aplikasi yang load cepat dan interaktif tanpa overhead runtime.
Dalam tutorial ini, saya mau ajak kamu bikin web app sederhana dari nol pakai SvelteKit. Kita bikin aplikasi todo list dengan SSR (Server-Side Rendering), form handling, dan API route. Gak perlu pengalaman Svelte sebelumnya saya jelasin step by step.
Apa itu SvelteKit dan Kenapa Harus Pakai?
SvelteKit itu meta-framework untuk Svelte, sama kayak Next.js untuk React atau Nuxt untuk Vue. Tapi bedanya, Svelte gak pakai virtual DOM. Setiap komponen di-compile jadi JavaScript murni yang langsung manipulasi DOM. Artinya, gak ada diffing overhead di runtime.
Beberapa keunggulan SvelteKit:
- Bundle size kecil: Karena compiler ngelakuin optimisasi saat build, hasil akhirnya jauh lebih kecil dari React/Vue
- SSR otomatis: Setiap page langsung server-rendered di first load, jadi SEO-friendly dan cepat
- File-based routing: Bikin file di
src/routes, jadi route. Gak perlu konfigurasi router manual - Universal load function: Code yang jalan di server DAN client, gak perlu bikin terpisah
- Adapter system: Deploy ke Vercel, Netlify, VPS, atau static hosting tinggal ganti adapter
Svelte 5 yang terbaru juga ngenalin runes sistem reaktivitas baru yang lebih eksplisit dan gampang di-debug dibanding let reactive di Svelte 4. Kalau kamu pernah pakai React hooks, konsepnya mirip tapi syntax-nya lebih bersih.
Persiapan: Install Node.js dan Bikin Project
Pastikan kamu punya Node.js versi 18 atau lebih baru. Cek dengan:
node --version
# v20.11.0 atau lebih baru oke
Kalau belum install, download dari nodejs.org atau pakai nvm:
# Install nvm
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# Install Node 20 LTS
nvm install 20
nvm use 20
Sekarang bikin project SvelteKit. Buka terminal, lalu:
npx sv create my-svelte-app
Command ini bakal nanya beberapa hal:
- Template: Pilih "SvelteKit minimal" (atau "Demo" kalau mau liat contoh)
- Type checking: Pilih "TypeScript" (saran: pakai, biar gampang debug)
- Additional options: Pilih "Add Playwright for browser testing" kalau mau, tapi gak wajib
Setelah selesai, masuk ke folder dan install dependencies:
cd my-svelte-app
npm install
npm run dev -- --open
Browser otomatis buka http://localhost:5173. Kamu bakal liat halaman welcome SvelteKit. Selamat, project pertama kamu udah jalan!
Memahami Struktur Folder SvelteKit
Sebelum kita mulai coding, pahami dulu struktur project-nya:
my-svelte-app/
|-- src/
| |-- routes/
| | |-- +page.svelte # Halaman homepage (UI)
| | |-- +page.ts # Load function (optional)
| | |-- +layout.svelte # Layout wrapper (navbar, footer)
| | |-- about/
| | |-- +page.svelte # Halaman /about
| |-- app.html # Template HTML root
| |-- app.css # CSS global
|-- static/
| |-- favicon.png # Static assets
|-- svelte.config.js # Konfigurasi SvelteKit
|-- vite.config.ts # Konfigurasi Vite
|-- package.json
Intinya: setiap folder di src/routes jadi satu route. File +page.svelte adalah komponen halaman, +page.ts (atau +page.server.ts) untuk load function, dan +layout.svelte untuk layout yang dipakai semua page.
Yang penting diingat: file yang diawali + itu "special" SvelteKit otomatis recognize. File tanpa + gak dianggap route, jadi aman untuk bikin komponen helper.
Membangun Aplikasi Todo List dengan Svelte 5 Runes
Sekarang kita bikin aplikasi todo list yang berfungsi. Kita mulai dari struktur route dan komponennya.
Pertama, bikin file store untuk manage state todo. Bikin file src/lib/stores.svelte.ts:
// src/lib/stores.svelte.ts
// Svelte 5 runes mode - pakai $state() untuk reactive state
interface Todo {
id: number;
text: string;
done: boolean;
}
class TodoStore {
items = $state<Todo[]>([]);
filter = $state<'all' | 'active' | 'done'>('all');
add(text: string) {
if (!text.trim()) return;
this.items = [
...this.items,
{ id: Date.now(), text: text.trim(), done: false }
];
}
toggle(id: number) {
this.items = this.items.map(t =>
t.id === id ? { ...t, done: !t.done } : t
);
}
remove(id: number) {
this.items = this.items.filter(t => t.id !== id);
}
get filtered() {
if (this.filter === 'active') return this.items.filter(t => !t.done);
if (this.filter === 'done') return this.items.filter(t => t.done);
return this.items;
}
get remaining() {
return this.items.filter(t => !t.done).length;
}
}
export const todos = new TodoStore();
Di Svelte 5, $state() itu rune untuk bikin reactive state. Kalau kamu pernah pakai React useState, konsepnya mirip tapi gak perlu hooks atau dependency array. Cukup assign value baru dan UI auto-update.
Sekarang bikin komponen utama di src/routes/+page.svelte:
<script lang="ts">
import { todos } from '$lib/stores.svelte';
let input = $state('');
function handleSubmit(e: Event) {
e.preventDefault();
todos.add(input);
input = '';
}
</script>
<main>
<h1>Todo List</h1>
<p class="subtitle">Bikin dengan SvelteKit + Svelte 5 Runes</p>
<form on:submit={handleSubmit}>
<input
type="text"
bind:value={input}
placeholder="Mau ngapain hari ini?"
/>
<button type="submit">Tambah</button>
</form>
<div class="filters">
<button class:active={todos.filter === 'all'}
onclick={() => todos.filter = 'all'}>Semua</button>
<button class:active={todos.filter === 'active'}
onclick={() => todos.filter = 'active'}>Aktif</button>
<button class:active={todos.filter === 'done'}
onclick={() => todos.filter = 'done'}>Selesai</button>
</div>
<ul>
{#each todos.filtered as todo (todo.id)}
<li class:done={todo.done}>
<input type="checkbox" checked={todo.done}
onchange={() => todos.toggle(todo.id)} />
<span>{todo.text}</span>
<button onclick={() => todos.remove(todo.id)}>Hapus</button>
</li>
{/each}
</ul>
<p class="count">{todos.remaining} dari {todos.items.length} belum selesai</p>
</main>
<style>
main { max-width: 500px; margin: 0 auto; padding: 2rem; }
form { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
input[type="text"] {
flex: 1; padding: 0.75rem; border: 1px solid #ccc;
border-radius: 4px; font-size: 1rem;
}
button {
padding: 0.75rem 1.5rem; background: #ff3e00; color: white;
border: none; border-radius: 4px; cursor: pointer;
}
.filters { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
.filters button {
background: transparent; color: #333;
border: 1px solid #ddd; font-size: 0.85rem;
}
.filters button.active {
background: #ff3e00; color: white; border-color: #ff3e00;
}
ul { list-style: none; padding: 0; }
li {
display: flex; align-items: center; gap: 0.75rem;
padding: 0.75rem; border-bottom: 1px solid #eee;
}
li.done span { text-decoration: line-through; color: #999; }
li button {
background: #e74c3c; font-size: 0.75rem; padding: 0.3rem 0.6rem;
}
.count { color: #666; font-size: 0.9rem; margin-top: 1rem; }
</style>
Beberapa hal yang perlu kamu perhatiin di kode di atas:
bind:value={input}two-way binding, sama kayakv-modeldi Vue atauonChange+valuedi React. Tapi lebih ringkas.{#each ... as ... (key)}Svelte's each block, key di akhir untuk efisiensi updateclass:done={todo.done}class binding, nambah classdonekalautodo.donetrueon:submitdanonclickevent listener (Svelte 5 pakaionclicktanpaon:prefix untuk DOM events)
Server-Side Rendering dan Load Function
Salah satu kekuatan SvelteKit itu SSR. Default-nya, setiap page di-render di server dulu sebelum dikirim ke browser. Ini bikin halaman cepat dan SEO-friendly.
Tapi bagaimana kalau kita mau ambil data dari API atau database sebelum render? Pakai load function. Bikin file src/routes/+page.ts (atau +page.server.ts kalau code cuma boleh jalan di server):
// src/routes/+page.server.ts
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ fetch }) => {
// fetch di sini jalan di server saat SSR
// lalu di-client saat client-side navigation
const res = await fetch('/api/todos');
const todos = await res.json();
return {
todos: todos
};
};
Di +page.svelte, kamu akses datanya lewat data prop:
<script lang="ts">
let { data } = $props();
// data.todos berisi hasil dari load function
</script>
{#each data.todos as todo (todo.id)}
<div>{todo.text}</div>
{/each}
Yang keren: load function ini universal. Saat first load (SSR), jalan di server. Saat client-side navigation (SPA mode), jalan di browser. Kamu gak perlu nulis dua versi code. SvelteKit handle semuanya.
Bikin API Route di SvelteKit
SvelteKit juga bisa jadi backend API. Bikin endpoint itu sama kayak bikin page tinggal bikin file +server.ts di folder route:
// src/routes/api/todos/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
// Mock data (di production, pakai database sungguhan)
let todos = [
{ id: 1, text: 'Belajar SvelteKit', done: false },
{ id: 2, text: 'Deploy ke Vercel', done: false }
];
export const GET: RequestHandler = async () => {
return json(todos);
};
export const POST: RequestHandler = async ({ request }) => {
const body = await request.json();
const newTodo = {
id: Date.now(),
text: body.text,
done: false
};
todos = [...todos, newTodo];
return json(newTodo, { status: 201 });
};
export const DELETE: RequestHandler = async ({ url }) => {
const id = Number(url.searchParams.get('id'));
todos = todos.filter(t => t.id !== id);
return json({ success: true });
};
Kalau kamu butuh database real, tinggal install Prisma atau Drizzle ORM, connect ke PostgreSQL atau MySQL, dan query di dalam +server.ts. Sama kayak bikin API di Express atau Fastify, tapi tanpa setup tambahan.
Deploy SvelteKit ke VPS atau Vercel
SvelteKit pakai sistem adapter untuk build. Default adapter bikin app yang jalan di Vercel atau Netlify. Tapi kalau kamu deploy ke VPS sendiri, pakai @sveltejs/adapter-node:
npm install -D @sveltejs/adapter-node
Edit svelte.config.js:
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
export default {
preprocess: vitePreprocess(),
kit: {
adapter: adapter({
// Port default 3000
envPrefix: 'PUBLIC_'
})
}
};
Build dan jalankan:
npm run build
node build/index.js
# App jalan di port 3000
# Bisa pakai PM2 untuk production:
npm install -g pm2
pm2 start build/index.js --name svelte-app
pm2 save
pm2 startup
Untuk production di VPS, pakai nginx sebagai reverse proxy di depannya:
server {
listen 80;
server_name myapp.example.com;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Atau kalau mau yang paling gampang, deploy ke Vercel: npx vercel. Tinggal push ke GitHub, connect repo, dan auto-deploy setiap push. SvelteKit punya adapter Vercel built-in.
Tips dan Best Practices SvelteKit
Setelah bikin beberapa project dengan SvelteKit, ini hal-hal yang saya pelajari:
- Pakai
$statedi class, bukan global variable. Kalau kamu bikin store global, pakai class dengan$state()di field-nya. Lebih predictable dan gampang di-test. - Jangan lupa
keydi{#each}. Tanpa key, Svelte gak tau elemen mana yang berubah, jadi mungkin render ulang semua. Tambahkan(item.id)di akhir each block. - Pisah logic ke
+page.server.tskalau sensitif. Code di file+page.svelteterkirim ke browser. Code di+page.server.tscuma jalan di server. Taruh API key dan database query di sana. - Pakai
export const prerender = trueuntuk static pages. Halaman About, Privacy, dan blog posts bisa di-prerender jadi HTML statis. Lebih cepat dan murah untuk hosting. - Test performa dengan Lighthouse. SvelteKit sering dapet 95+ di semua kategori Lighthouse tanpa effort tambahan.
SvelteKit vs Next.js vs Nuxt: Mana yang Pilih?
Tabel singkat buat bantu kamu putuskan:
- SvelteKit: Bundle paling kecil, syntax paling bersih, cocok untuk project baru yang mau performa maksimal. Kurva belajar paling landai.
- Next.js: Ekosistem terbesar, App Router powerful, cocok kalau tim sudah familiar React. Tapi bundle size besar dan konsep server/client component bisa bikin pusing.
- Nuxt: Kalo tim Vue, ini no-brainer. Auto-imports dan file routing oke. Tapi performa gak secepat Svelte.
Kalau kamu baru mulai project dan bebas pilih stack, saya saranin coba SvelteKit. Developer experience-nya enak banget. Gak perlu pusing useEffect dependency array atau memo yang gak efektif. Cukup tulis kode, dan biarin compiler optimisasi.
Kesimpulan
SvelteKit itu framework yang nge-rasa "benar" buat bikin web app. Compiler approach-nya bikin bundle kecil, runes mode di Svelte 5 bikin reaktivitas gampang dipahami, dan file-based routing bikin struktur project bersih. Untuk developer Indonesia yang lagi cari alternatif selain Next.js, SvelteKit worth banget dicoba.
Kalau kamu udah pernah pakai SvelteKit, atau ada bagian tutorial ini yang mau kamu tanya, komen di bawah ya. Saya mau denger pengalaman kalian terutama kalau ada yang udah deploy production dan mau share tips-nya.