Bosia Bosia v0.9.5

Server Metadata

Atur judul halaman, meta tag, Open Graph, dan atribut lang dengan fungsi metadata().

Fungsi metadata() memungkinkan Anda mendefinisikan tag SEO dan head untuk tiap halaman, berjalan di server sebelum load().

Penggunaan Dasar

Ekspor sebuah fungsi metadata dari +page.server.ts:

import type { MetadataEvent } from "bosia";

export function metadata({ params }: MetadataEvent) {
	return {
		title: "About — My App",
		description: "Learn more about our app.",
	};
}

Ini merender sebuah tag <title> dan tag <meta name="description"> di <head> halaman.

Open Graph & Tag Sosial

Gunakan array meta untuk menambahkan Open Graph, Twitter Card, atau meta tag kustom apa pun:

export function metadata({ params }: MetadataEvent) {
	return {
		title: "Blog Post",
		description: "A great blog post.",
		meta: [
			{ property: "og:title", content: "Blog Post" },
			{ property: "og:description", content: "A great blog post." },
			{ property: "og:type", content: "article" },
			{ name: "twitter:card", content: "summary_large_image" },
		],
	};
}

Tag dengan property dirender sebagai <meta property="...">, tag dengan name dirender sebagai <meta name="...">.

Atur atribut <html lang> dan tambahkan tag <link> untuk URL kanonik, alternatif hreflang, dan lainnya:

export function metadata() {
	return {
		title: "Mon Blog",
		lang: "fr",
		link: [
			{ rel: "canonical", href: "https://example.com/blog" },
			{ rel: "alternate", href: "https://example.com/en/blog", hreflang: "en" },
			{ rel: "alternate", href: "https://example.com/fr/blog", hreflang: "fr" },
		],
	};
}

Mengoper Data ke load()

Properti data memungkinkan Anda berbagi data yang sudah di-fetch dengan load(), menghindari query ganda:

import type { MetadataEvent, LoadEvent } from "bosia";

export function metadata({ params }: MetadataEvent) {
	const post = await db.getPost(params.slug);
	return {
		title: `${post.title} — Blog`,
		description: post.excerpt,
		meta: [{ property: "og:title", content: post.title }],
		// Pass to load() — avoids a second DB query
		data: { post },
	};
}

export async function load({ params, metadata }: LoadEvent) {
	// Reuse data from metadata(), fall back to fresh query
	const post = metadata?.post ?? (await db.getPost(params.slug));
	return { post };
}

Objek data dari metadata() menjadi event.metadata di load(). Jika tidak ada fungsi metadata(), event.metadata bernilai null.

Properti MetadataEvent

Properti Tipe Deskripsi
params Record<string, string> Parameter route dinamis
url URL URL request
locals Record<string, any> Data yang diset oleh middleware hooks
cookies Cookies Baca/tulis cookie
fetch Function Helper fetch (cookie hanya diteruskan ke same-origin — lihat Server Loaders → Cookie Forwarding)

Tipe Return Metadata

Properti Tipe Deskripsi
title string Tag <title> halaman
description string Tag <meta name="description">
meta Array<{ name?: string; property?: string; content: string }> Meta tag kustom
lang string Atribut <html lang>
link Array<{ rel: string; href: string; hreflang?: string }> Tag <link> (kanonik, hreflang, dll.)
data Record<string, any> Data yang dioper ke load() sebagai event.metadata

Semua properti opsional.

Form Actions

metadata() juga berjalan saat submit <form method="POST"> merender ulang halaman, jadi title, meta tag, lang, dan data yang dioper ke load() sama persis dengan GET biasa.

Redirect & Error

Kamu bisa melempar redirect() atau error() dari dalam metadata() — perilakunya sama seperti di load():

import { redirect } from "bosia";
import type { MetadataEvent } from "bosia";

export function metadata({ locals }: MetadataEvent) {
	if (!locals.user) redirect(303, "/masuk");
	return { title: "Dashboard" };
}

Error lain di dalam metadata() dicatat ke log dan halaman dirender tanpa metadata, bukan menggagalkan request.

Title vs ``

Jangan pernah menetapkan <title> di metadata() dan <svelte:head> sekaligus pada rute yang sama — keduanya tidak digabung, dan pemenangnya berbeda tergantung siapa yang membaca.

Title dari metadata() yang dikirim di HTML SSR, jadi itu yang dibaca scraper, curl, dan crawler tanpa JS. Svelte mengompilasi <svelte:head><title> menjadi penulisan document.title yang jalan saat mount dan setiap kali nilainya berubah, jadi tab browser menampilkan yang itu. Rute yang mendeklarasikan keduanya mengiklankan satu judul tapi menampilkan judul lain.

Pilih metadata() untuk apa pun yang penting bagi tautan berbagi, lalu hapus <svelte:head><title> yang bersaing.

Selama navigasi sisi-klien, Bosia mengirim title, description, meta, link, dan lang dari metadata() dalam respons data. Router klien menyusun ulang <head> dari data itu tanpa reload halaman penuh, jadi og:*, twitter:*, canonical, dan robots sama dengan hasil muat-ulang penuh URL yang sama.

metadata.data sengaja tidak dikirim — ia dipakai load() di server dan bisa berisi nilai yang tidak boleh dilihat browser.

Router hanya mengganti tag yang dihasilkan metadata() (ditandai data-bosia-meta di HTML). Tag yang kamu tambahkan lewat <svelte:head> atau fragmen head sebuah plugin tidak disentuh.

Halaman yang metadata()-nya tidak mengembalikan title akan mempertahankan judul halaman sebelumnya, bukan berkedip ke fallback Bosia App. Beri setiap rute sebuah title kalau itu penting buatmu.

Timeout

Fungsi metadata() punya timeout yang bisa dikonfigurasi melalui variabel environment METADATA_TIMEOUT (dalam milidetik). Jika metadata() terlalu lama, ia timeout dengan anggun dan halaman dirender tanpa metadata.

Interaksi dengan Caching

Jika metadata() memanggil cookies.get() atau cookies.getAll(), respons data otomatis ditandai dengan Cache-Control: private, no-cache. Lihat Server Loaders untuk detailnya.