Pengembangan Svelte dan SvelteKit dengan Claude Code
Panduan praktis Claude Code untuk Svelte/SvelteKit: setup, runes, routing, form actions, testing, dan edit yang aman.
Mengapa Claude Code cocok untuk SvelteKit
Svelte adalah framework UI yang mengompilasi komponen deklaratif menjadi JavaScript ringan. SvelteKit menambahkan lapisan aplikasi: routing berbasis file, server rendering, fungsi load, form actions, endpoint, dan adapter deployment. Dalam aplikasi nyata, satu fitur kecil sering menyentuh src/routes, src/lib/components, $lib/server, tipe yang dihasilkan, dan test.
Claude Code adalah alat agentic coding yang dapat membaca repository, mengedit file, dan menjalankan command. Pada proyek SvelteKit, nilainya paling terasa ketika tugasnya dibatasi: minta Claude Code membaca file terkait, menjelaskan data flow, mengusulkan rencana, lalu mengubah scope kecil. Prompt seperti “buatkan SaaS SvelteKit” terlalu lebar karena mencampur UI, database, auth, security, dan deployment.
Untuk pemula, bagian terpenting adalah Svelte 5. Runes adalah cara reaktif saat ini: $props menerima input komponen, $state menyimpan state yang berubah, $derived menghitung nilai dari state, dan $effect dipakai untuk side effect browser. Artikel ini memakai contoh aplikasi task kecil untuk membahas setup, komponen, shared state, routing, form actions, test, prompt yang aman, use case, dan jebakan umum.
flowchart LR
A["Tulis kebutuhan kecil"] --> B["Claude Code membaca file terkait"]
B --> C["Edit komponen Svelte"]
C --> D["Cek load/actions"]
D --> E["Jalankan check dan test"]
E --> F["Review git diff manual"]
Setup proyek
Untuk membuat proyek SvelteKit baru, jalur resmi saat ini dimulai dari Svelte CLI: npx sv create my-app. Jika hanya butuh aplikasi Svelte standalone di atas Vite, template svelte-ts dari Vite juga bisa dipakai. Panduan Vite saat ini mensyaratkan Node.js 20.19+ atau 22.12+ untuk Vite, jadi perbarui Node sebelum mengejar error build yang tidak jelas.
npx sv create claude-svelte-demo
cd claude-svelte-demo
npm install
npm run dev
claude
Pada repository baru, mulai dengan plan mode. Mode ini membuat Claude Code membaca file dan menyusun rencana tanpa mengedit source code.
/plan
Saya ingin menambahkan fitur daftar task ke proyek SvelteKit ini.
Pertama baca src/routes dan src/lib, lalu usulkan file yang perlu diubah, alur data, dan rencana test.
Jangan edit file dulu.
claude --permission-mode plan
Aturan proyek sebaiknya ditulis di CLAUDE.md atau .claude/CLAUDE.md: package manager, command validasi, preferensi Svelte 5 runes, konvensi form actions, batasan security, dan “jangan commit kecuali diminta”. Aturan konkret lebih berguna daripada instruksi umum seperti “rapikan kode”.
Komponen kecil dengan Svelte 5
Contoh berikut bisa langsung ditempatkan di src/lib/components/TaskCard.svelte. Komponen ini menerima task dan callback onToggle, menghitung label dengan $derived, dan mempertahankan aria-pressed untuk aksesibilitas.
<!-- src/lib/components/TaskCard.svelte -->
<script lang="ts">
type Task = {
id: string;
title: string;
done: boolean;
estimateMinutes: number;
tags: string[];
};
let {
task,
onToggle
}: {
task: Task;
onToggle: (id: string) => void;
} = $props();
let statusLabel = $derived(task.done ? 'Selesai' : 'Terbuka');
let estimateLabel = $derived(`${Math.ceil(task.estimateMinutes / 15) * 15} menit`);
</script>
<article class:done={task.done} class="task-card">
<div>
<p class="status">{statusLabel}</p>
<h3>{task.title}</h3>
<p>{estimateLabel}</p>
</div>
<ul aria-label="Tag">
{#each task.tags as tag}
<li>{tag}</li>
{/each}
</ul>
<button type="button" aria-pressed={task.done} onclick={() => onToggle(task.id)}>
{task.done ? 'Buka kembali' : 'Tandai selesai'}
</button>
</article>
<style>
.task-card {
display: grid;
gap: 0.75rem;
border: 1px solid #ddd;
border-radius: 0.5rem;
padding: 1rem;
}
.done {
background: #f2fff5;
}
.status {
font-size: 0.875rem;
font-weight: 700;
}
ul {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
list-style: none;
padding: 0;
}
li {
border-radius: 999px;
background: #eef2ff;
padding: 0.2rem 0.6rem;
}
</style>
Prompt yang aman harus menjaga kontrak publik komponen:
Tingkatkan src/lib/components/TaskCard.svelte tetapi pertahankan sintaks Svelte 5 runes.
Syarat:
- Jangan ubah tipe Task atau signature onToggle.
- Pertahankan onclick dan jangan kembali ke on:click.
- Pertahankan aria-pressed pada tombol.
- Perbaiki hanya layout dan empty state.
- Setelah edit, sarankan satu component test.
Dengan cara ini, Claude Code tidak mengubah API komponen atau menyentuh file lain tanpa alasan. Review diff juga menjadi lebih mudah.
Shared state: runes dulu, stores jika perlu
Svelte 5 mendukung runes di file .svelte.ts. File seperti ini cocok untuk shared reactive state dan logic yang dipakai ulang. svelte/store tetap berguna untuk stream async kompleks, manual subscription, atau integrasi dengan library yang sudah memakai store contract.
// src/lib/state/taskFilters.svelte.ts
export type TaskStatus = 'all' | 'open' | 'done';
export const taskFilters = $state({
query: '',
status: 'all' as TaskStatus,
tag: ''
});
export function resetTaskFilters() {
taskFilters.query = '';
taskFilters.status = 'all';
taskFilters.tag = '';
}
<!-- src/lib/components/TaskFilterPanel.svelte -->
<script lang="ts">
import { resetTaskFilters, taskFilters } from '$lib/state/taskFilters.svelte';
</script>
<section aria-label="Filter task">
<label>
Kata kunci
<input bind:value={taskFilters.query} placeholder="Invoice, artikel, review..." />
</label>
<label>
Status
<select bind:value={taskFilters.status}>
<option value="all">Semua</option>
<option value="open">Terbuka</option>
<option value="done">Selesai</option>
</select>
</label>
<button type="button" onclick={resetTaskFilters}>Reset</button>
</section>
Jebakan umum adalah melupakan SSR. window, document, dan localStorage tidak ada di server. Jika perlu menyimpan preferensi di browser, gunakan browser dari $app/environment.
// src/lib/state/theme.svelte.ts
import { browser } from '$app/environment';
export const themeState = $state({
theme: 'system' as 'system' | 'light' | 'dark'
});
export function loadTheme() {
if (!browser) return;
const saved = localStorage.getItem('theme');
if (saved === 'light' || saved === 'dark' || saved === 'system') {
themeState.theme = saved;
}
}
export function saveTheme(nextTheme: typeof themeState.theme) {
themeState.theme = nextTheme;
if (browser) localStorage.setItem('theme', nextTheme);
}
Routing, load, dan batas server
SvelteKit memakai routing berbasis file. src/routes/about menjadi /about, dan src/routes/tasks/[slug] menjadi route dinamis dengan parameter slug. Jika halaman perlu data sebelum render, gunakan +page.server.ts. Database, private key, dan API privileged harus tetap di $lib/server.
// src/lib/server/tasks.ts
export type Task = {
id: string;
slug: string;
title: string;
done: boolean;
estimateMinutes: number;
tags: string[];
};
const tasks: Task[] = [
{
id: 'task-1',
slug: 'write-svelte-guide',
title: 'Membuat draft artikel SvelteKit',
done: false,
estimateMinutes: 45,
tags: ['writing', 'svelte']
}
];
export async function getTaskBySlug(slug: string) {
return tasks.find((task) => task.slug === slug) ?? null;
}
// src/routes/tasks/[slug]/+page.server.ts
import { error } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
import { getTaskBySlug } from '$lib/server/tasks';
export const load: PageServerLoad = async ({ params }) => {
const task = await getTaskBySlug(params.slug);
if (!task) {
error(404, 'Task not found');
}
return { task };
};
<!-- src/routes/tasks/[slug]/+page.svelte -->
<script lang="ts">
import type { PageProps } from './$types';
let { data }: PageProps = $props();
</script>
<svelte:head>
<title>{data.task.title} | Tasks</title>
</svelte:head>
<article>
<p>{data.task.done ? 'Selesai' : 'Terbuka'}</p>
<h1>{data.task.title}</h1>
<p>Estimasi: {data.task.estimateMinutes} menit</p>
</article>
Prompt untuk routing:
Baca src/routes/tasks/[slug] dan src/lib/server/tasks.ts.
Tambahkan field dueDate ke Task dan tampilkan di halaman detail.
Pertahankan akses data server di $lib/server.
Jangan rename folder route [slug].
Jalankan npm run check di akhir.
Form actions dan progressive enhancement
SvelteKit form actions memungkinkan +page.server.ts mengekspor actions, lalu <form method="POST"> mengirim data ke server. Form tetap berjalan tanpa JavaScript; use:enhance hanya memperbaiki pengalaman saat JavaScript tersedia.
// src/routes/contact/+page.server.ts
import { fail } from '@sveltejs/kit';
import type { Actions } from './$types';
export const actions = {
default: async ({ request }) => {
const formData = await request.formData();
const values = {
name: String(formData.get('name') ?? '').trim(),
email: String(formData.get('email') ?? '').trim(),
message: String(formData.get('message') ?? '').trim()
};
const errors: Record<string, string> = {};
if (values.name.length < 2) errors.name = 'Masukkan minimal 2 karakter.';
if (!values.email.includes('@')) errors.email = 'Periksa alamat email.';
if (values.message.length < 10) errors.message = 'Masukkan minimal 10 karakter.';
if (Object.keys(errors).length > 0) {
return fail(400, { values, errors });
}
console.log('New inquiry', values);
return { success: true };
}
} satisfies Actions;
<!-- src/routes/contact/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import type { PageProps } from './$types';
let { form }: PageProps = $props();
</script>
{#if form?.success}
<p role="status">Terkirim. Kami akan membalas dalam 1 sampai 3 hari kerja.</p>
{/if}
<form method="POST" use:enhance>
<label>
Nama
<input name="name" value={form?.values?.name ?? ''} aria-invalid={!!form?.errors?.name} />
</label>
{#if form?.errors?.name}<p>{form.errors.name}</p>{/if}
<label>
Email
<input name="email" type="email" value={form?.values?.email ?? ''} aria-invalid={!!form?.errors?.email} />
</label>
{#if form?.errors?.email}<p>{form.errors.email}</p>{/if}
<label>
Pesan
<textarea name="message" rows="5" aria-invalid={!!form?.errors?.message}>{form?.values?.message ?? ''}</textarea>
</label>
{#if form?.errors?.message}<p>{form.errors.message}</p>{/if}
<button type="submit">Kirim</button>
</form>
Jangan memakai GET untuk side effect, jangan mengekspos private key ke browser, dan jangan render HTML tidak tepercaya dengan {@html} tanpa sanitasi. Pada prompt, sebutkan “pertahankan validasi server”, “tetap berjalan tanpa JavaScript”, dan “jangan hapus atribut aksesibilitas”.
Testing, use case, dan jebakan
Dokumentasi testing Svelte menyebut Vitest sebagai pilihan yang cocok untuk proyek Vite dan SvelteKit. Testing Library membantu menguji perilaku yang terlihat oleh user; Playwright cocok untuk flow end-to-end.
// src/lib/components/TaskCard.test.ts
import { fireEvent, render, screen } from '@testing-library/svelte';
import { describe, expect, it } from 'vitest';
import TaskCard from './TaskCard.svelte';
describe('TaskCard', () => {
it('toggles the task when the button is clicked', async () => {
let toggledId = '';
render(TaskCard, {
task: {
id: 'task-1',
title: 'Menulis artikel SvelteKit',
done: false,
estimateMinutes: 45,
tags: ['writing']
},
onToggle: (id) => {
toggledId = id;
}
});
await fireEvent.click(screen.getByRole('button', { name: 'Tandai selesai' }));
expect(toggledId).toBe('task-1');
});
});
npm run check
npm run test
npm run build
git diff -- src/lib src/routes
| Use case | Cocok untuk Claude Code | Review manusia |
|---|---|---|
| Filter admin | Pisahkan filter dengan $state dan $derived | Query URL dan field berdasarkan permission |
| Blog atau CMS | Hubungkan [slug], load, SEO, dan 404 | Sanitasi HTML, draft, preview |
| Form lead | Actions, validasi, use:enhance, test | Privasi, spam, target notifikasi |
| Migrasi Svelte 4 ke 5 | Ubah komponen tertentu ke runes | Jangan ubah perilaku secara massal |
Kesalahan yang sering terjadi: prompt terlalu besar, sintaks Svelte 4 dan 5 tercampur, komponen mengimpor $lib/server, $effect dipakai untuk kalkulasi biasa, dan test terlalu lemah. Dari sisi monetisasi, SvelteKit mudah menghubungkan artikel teknis ke form konsultasi atau produk. Untuk rollout tim, lihat Claude Code consultation page. Lanjutkan dengan getting started guide, TypeScript tips, dan testing strategies.
Referensi resmi dan hasil uji
Saat perilaku framework penting, gunakan sumber resmi: Svelte docs, SvelteKit docs, SvelteKit form actions, Vite guide, dan Claude Code docs.
Baca struktur SvelteKit saat ini, lalu ubah hanya scope berikut.
File: src/routes/contact/+page.svelte dan src/routes/contact/+page.server.ts
Tujuan: tambahkan field company ke form inquiry.
Syarat:
- Pertahankan Svelte 5 runes.
- Jangan hapus use:enhance.
- Tambahkan validasi server-side.
- Jangan ubah copy CTA atau class layout.
- Jalankan npm run check sebelum selesai.
Akhiri dengan 3 baris: perubahan, risiko, test yang masih kurang.
Masa mencoba workflow ini pada aplikasi task kecil berbasis SvelteKit. Hasil terbaik muncul ketika dimulai dari plan mode. Instruksi “pertahankan runes”, “jalankan npm run check”, dan “jangan commit” membuat diff lebih kecil dan lebih mudah direview oleh pemula Svelte.
PDF gratis: cheatsheet Claude Code
Masukkan email dan unduh satu halaman berisi command, kebiasaan review, dan workflow aman.
Kami menjaga datamu dan tidak mengirim spam.
Tentang penulis
Masa
Engineer yang berfokus pada workflow Claude Code praktis dan adopsi tim.
Artikel terkait
Workflow Obsidian ke CLAUDE.md untuk Claude Code
Ubah catatan kerja Obsidian menjadi operating note CLAUDE.md agar konteks tidak dijelaskan ulang.
Claude Code Revenue CTA Routing: dari artikel ke PDF, Gumroad, dan konsultasi
Workflow Claude Code untuk mengarahkan pembaca ke PDF gratis, Gumroad, atau konsultasi sesuai intent.
Aturan handoff tim Claude Code: bukti review, permission, rollback, dan jalur revenue
Format handoff Claude Code untuk tim: bukti, permission rule, rollback, PDF gratis, Gumroad, dan konsultasi.