Routing
Routing berbasis file dengan parameter dinamis, catch-all routes, route groups, dan layouts.
Bosia menggunakan routing berbasis file. File-file di dalam src/routes/ dipetakan langsung ke URL.
Route Statis
src/routes/+page.svelte → /
src/routes/about/+page.svelte → /about
src/routes/blog/+page.svelte → /blogSetiap file +page.svelte menjadi halaman pada path direktorinya.
Route Dinamis
Bungkus nama direktori dalam tanda kurung siku untuk membuat segmen dinamis:
src/routes/blog/[slug]/+page.svelte → /blog/hello-world
/blog/my-post
/blog/anythingAkses nilai yang cocok melalui params:
// +page.server.ts
export async function load({ params }: LoadEvent) {
const post = await getPost(params.slug);
return { post };
}Catch-All Routes
Gunakan [...rest] untuk mencocokkan beberapa segmen path:
src/routes/all/[...catchall]/+page.svelte → /all/a
/all/a/b/c
/all/anything/hereparams.catchall berisi seluruh sub-path yang cocok (misalnya "a/b/c").
Catch-all bisa ada setelah param: [lang]/[...rest] cocok dengan /en/a/b. Catch-all harus jadi bagian terakhir route — docs/[...path]/edit membuat build gagal.
Nama param mengikuti aturan nama variabel TypeScript, hanya ASCII: huruf, angka, _ dan $, tidak diawali angka ([slug], [x123], [user_id], [_id], [...path]). Selain itu, seperti [post-id] atau [file.name], membuat build gagal. Kurung harus membungkus satu segmen utuh: [id].json, post-[slug], optional [[lang]] atau [id?], dan matcher seperti [id=integer] tidak didukung dan membuat build gagal.
Route Groups
Direktori yang dibungkus dalam tanda kurung tidak terlihat di URL tetapi memungkinkan Anda berbagi layout:
src/routes/(public)/+layout.svelte ← layout bersama
src/routes/(public)/+page.svelte → /
src/routes/(public)/about/+page.svelte → /about
src/routes/(admin)/+layout.svelte ← layout berbeda
src/routes/(admin)/dashboard/+page.svelte → /dashboardGrup (public) dan (admin) tidak pernah muncul di URL. Mereka hanya mengontrol +layout.svelte mana yang membungkus halaman di dalamnya.
Karena grup tidak terlihat, dua file bisa berakhir di URL yang sama — misalnya src/routes/+page.svelte dan src/routes/(public)/+page.svelte sama-sama /. Begitu juga blog/[id] dan (app)/blog/[slug]. Seperti SvelteKit dan Next.js, Bosia menolaknya: bun run build gagal dan bun run dev menampilkan error yang menyebut kedua file. Hapus atau pindahkan salah satunya.
Prioritas Route
Ketika beberapa route bisa cocok dengan URL, Bosia membandingkannya segmen demi segmen, dari kiri ke kanan. Pada segmen pertama yang berbeda, yang lebih spesifik menang:
- Statis — nama tetap seperti
label - Dinamis —
[param] - Catch-all —
[...rest]
Jadi /label/[id] menang atas /[folder]/[thread] untuk /label/inbox, dan /blog/[...slug] menang atas /[lang]/about untuk /blog/about.
Layouts
+layout.svelte membungkus semua halaman dan child layout di dalam direktorinya:
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import "../app.css";
let { children, data } = $props();
</script>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
<main>
{@render children()}
</main>Layout bersarang secara otomatis — layout root membungkus layout grup, yang membungkus layout halaman. Konten anak dirender di mana {@render children()} berada.
Data Layout
Pasangkan layout dengan +layout.server.ts untuk memuat data:
// src/routes/+layout.server.ts
import type { LoadEvent } from "bosia";
export async function load({ locals }: LoadEvent) {
return {
appName: "My App",
user: locals.user,
};
}Semua halaman dan layout anak dapat mengakses data ini melalui parent() di loader mereka sendiri.
Halaman Error
Buat +error.svelte untuk menangani error yang dilempar oleh loader:
<!-- src/routes/+error.svelte -->
<script lang="ts">
import type { ErrorProps } from "./$types";
let { error }: ErrorProps = $props();
</script>
<h1>{error.status}</h1><p>{error.message}</p>Halaman error menerima HttpError yang dilempar oleh error() di dalam loader. Tempatkan halaman error pada level route di mana Anda ingin menangkap error — halaman ini menangkap error dari semua route anak. Tipe ErrorProps dan PageError di-generate ke ./$types — tidak perlu mendeklarasikan tipe prop secara manual.
Halaman error juga menerima params dari route yang gagal (let { error, params }: ErrorProps = $props()). Karena halaman error menangkap route anak juga, params milik anak (misalnya params.slug di bawah /blog) bertipe string | undefined, dan 404 mendapat {}.
Kerangka Loading
Buat +loading.svelte di folder route untuk menampilkan kerangka (skeleton) saat berpindah ke halaman tersebut, alih-alih membiarkan halaman lama tetap tampil sampai kode dan data halaman baru selesai dimuat:
<!-- src/routes/blog/+loading.svelte -->
<div class="skeleton">
<div class="line"></div>
<div class="line"></div>
</div>Seperti +error.svelte, ini dirender di dalam layout yang dibagi antara halaman saat ini dan tujuan — jadi nav, header, dan tab di sekelilingnya tetap terpasang dan hanya area konten yang menampilkan kerangka. Layout yang ditambahkan tujuan belum terpasang, sehingga +loading.svelte menggambar chrome itu sendiri (sama seperti loading.tsx di Next.js).
Aturan:
- Hanya muncul saat URL benar-benar berubah — bukan saat
invalidate()atau rerun path yang sama. - Tanpa
+loading.svelte⇒ perilaku tidak berubah (halaman lama tetap sampai pertukaran). - Menurun ke route anak, yang terdekat menang — satu
+loading.sveltedi puncak sebuah section mencakup semua halaman di bawahnya. Tambahkan satu lagi lebih dalam untuk memberi subtree-nya kerangka sendiri.
Opsi Halaman
Atur perilaku rendering per halaman dengan mengekspor flag dari +page.server.ts:
// src/routes/dashboard/+page.server.ts
export const ssr = false; // lewati server render, kirim shell + hydrate di client
export const csr = false; // lewati hydration client, hanya HTML server-rendered
export const prerender = true; // build ke HTML statis saat `bosia build`ssr = false— serverload()tetap berjalan dan hasilnya diinjeksi sebagai page data; client melakukan hydrate dan render. Cocok untuk halaman dengan dependensi khusus browser (window, chart, widget pihak ketiga) atau halaman auth-gated di mana SSR menambah latensi tanpa nilai SEO.csr = false— tidak ada JS untuk halaman ini. HTML statis saja.prerender = true— di-capture saat build. Untuk route dinamis, ekspor jugaentries()yang mengembalikan nilai param yang akan di-prerender. Nilainya hanya boleh huruf ASCII, angka,-,_,.dan~(plus/di catch-all), seperti50-off; selain itu build gagal.
ssr = false bersama csr = false tidak akan merender apapun dan otomatis di-override ke csr = true (dengan peringatan di mode dev). ssr = false bersama prerender = true bertentangan; route akan dilewati saat prerender. Begitu juga halaman yang mengekspor actions: file statis tidak bisa menjalankan form action, jadi halaman tetap dirender langsung.
bosia build dan bosia dev membaca flag ini dengan meng-import setiap +page.server.ts, +layout.server.ts dan +server.ts, jadi penulisan apa pun berlaku (export { p as prerender }, let, konstanta bersama). Kode top-level di file itu ikut jalan saat build: kalau ada yang melempar error (misalnya konek ke database yang tidak terjangkau dari mesin build), bosia build gagal (bosia dev hanya memberi peringatan dan memakai flag terakhir file itu; dicoba lagi di rebuild berikutnya, atau setelah file disimpan kalau timeout). Pindahkan kerja itu ke load() atau ke dalam fungsi. Di bosia dev, flag dibaca ulang saat file server route disimpan; setelah mengubah konstanta yang di-import dari src/lib, simpan file route-nya (atau restart dev; mengubah .env juga membaca ulang flag). File ini jalan dari salinan hasil bundle, jadi import.meta.dir menunjuk ke output build, bukan src/routes (build memberi peringatan). Di entries(), yang hanya jalan saat build, baca file di samping route dengan path dari process.cwd(), misalnya src/routes/blog/posts. load() yang jalan live tidak boleh membaca src/: image production hanya berisi dist/.