Svelte 5 runes: $state, $derived ve attachments
Svelte 4'te reaktivite, derleyicinin let atamasını ve $: ifadesini sessizce dönüştürmesine dayanıyordu. Bu yaklaşım küçük bileşenlerde sihir gibi çalışırken, bileşen dışına çıkartılmak istenen mantıkta (store'lar, paylaşılan durum) hızla kırılganlık üretiyordu. Svelte 5, bu örtük sözleşmeyi açık bir API ile değiştirdi: runes.
Rune nedir, neden var?
Rune'lar derleyici tarafından tanınan özel fonksiyon çağrılarıdır; $state, $derived, $effect gibi. Çalışma zamanında bir fonksiyon olarak bulunmazlar; Svelte derleyicisi bunları derleme aşamasında signals tabanlı koda dönüştürür. Dolayısıyla bir rune çağrısı yazdığınızda aslında derleyiciye "bu değişkeni reaktif yap" demiş oluyorsunuz, ama bunu açıkça, okunabilir şekilde yapıyorsunuz.
Svelte 4'teki $: label'ı iki farklı iş yapıyordu: hem türetilmiş değer hesaplıyordu hem de yan etki çalıştırıyordu. Hangisinin hangisi olduğunu derleyici bağlama bakarak tahmin ediyordu. Bu belirsizlik kaynak kodunu okuyan insanlar için de sorunluydu. Rune'lar bu iki kavramı birbirinden ayırdı: $derived türetilmiş değer, $effect yan etki.
$state ile reaktif değişken tanımlamak
$state, bir değişkeni reaktif signal olarak işaretler. Svelte 4'te let count = 0 yazıp derleyicinin bunu reaktif yapmasını umardınız; şimdi niyet açık:
<script>
let count = $state(0);
let user = $state({ name: 'Ali', score: 0 });
</script>
<button onclick={() => count++}>
Tıklama: {count}
</button>$state ilkel değerler için basit bir signal oluştururken, nesne ve dizilerde derin reaktivite sağlar. Yani user.score++ yazdığınızda Svelte bunu yakalar ve ilgili DOM parçasını günceller. Bu davranış Proxy tabanlıdır; Svelte derleyicisi nesneyi bir Proxy ile sarar.
Derin reaktivite istemediğiniz durumlarda $state.raw kullanılır:
<script>
let items = $state.raw([1, 2, 3]);
// items[0] = 99 reaktif güncelleme tetiklemez
// items = [99, 2, 3] ile referansı değiştirmek gerekir
</script>Bu ayrım performans açısından faydalı. Büyük, salt okunur veri listeleri için $state.raw kullanmak gereksiz Proxy sarmalama maliyetinden kurtarır.
$derived: türetilmiş değerler
Bir değerin başka reaktif değerlerden hesaplanması gerektiğinde $derived kullanılır:
<script>
let width = $state(10);
let height = $state(5);
let area = $derived(width * height);
</script>
<p>Alan: {area}</p>$derived ifadesi her okunduğunda bağımlılıklarını otomatik izler. width veya height değiştiğinde area yeniden hesaplanır. Svelte 4'teki $: area = width * height ile aynı işi yapar, ama şimdi bunun bir türetme olduğu koddan hemen anlaşılır.
Daha karmaşık hesaplamalar için $derived.by kullanılır:
<script>
let items = $state([3, 1, 4, 1, 5]);
let sorted = $derived.by(() => {
return [...items].sort((a, b) => a - b);
});
</script>$derived tek bir ifade alırken, $derived.by bir fonksiyon alır. İkisi arasındaki fark budur. Çok satırlı mantık gerekiyorsa .by kullanılır.
$effect: yan etkiler
$effect, bileşen DOM'a monte edildiğinde çalışır ve içinde okunan reaktif değerler değiştiğinde yeniden çalışır:
<script>
let query = $state('');
$effect(() => {
if (query.length < 2) return;
console.log('Arama yapılıyor:', query);
// fetch çağrısı vb.
});
</script>
<input bind:value={query} />Birkaç nokta dikkat istiyor. $effect içinde $state değişkenine yazma yapılmamalı; bu sonsuz döngüye yol açabilir. Svelte bunu geliştirme modunda uyarıyla bildirir. Türetilmiş bir değer hesaplanacaksa $effect değil $derived kullanılmalı.
Temizleme (cleanup) için $effect içinden bir fonksiyon döndürülür:
<script>
let visible = $state(true);
$effect(() => {
if (!visible) return;
const timer = setInterval(() => console.log('tick'), 1000);
return () => clearInterval(timer);
});
</script>React'teki useEffect cleanup kalıbına benzer, ama bağımlılık dizisi yoktur. Svelte hangi signal'lerin okunduğunu derleme zamanında bildiği için bağımlılıkları manuel listelemek gerekmez. Bu, useEffectEvent hook gibi React dünyasındaki bağımlılık dizisi sorunlarından doğal olarak kaçınmış olur.
Rune'ları bileşen dışında kullanmak
Svelte 5'in en belirgin iyileştirmelerinden biri rune'ların .svelte.js veya .svelte.ts dosyalarında çalışması. Bu, paylaşılan reaktif mantığı bileşenden ayırmayı mümkün kılar:
// counter.svelte.ts
export function createCounter(initial: number) {
let count = $state(initial);
return {
get count() { return count; },
increment() { count++; },
decrement() { count--; },
};
}<!-- App.svelte -->
<script>
import { createCounter } from './counter.svelte.ts';
const counter = createCounter(0);
</script>
<button onclick={counter.increment}>{counter.count}</button>Burada get count() getter'ı dikkat çekici. $state ile oluşturulan signal, fonksiyondan düz değer olarak döndürülürse reaktivite kaybolur (değer kopyalanır). Getter kullanmak, her okumada güncel signal değerini döndürmeyi garantiler.
Bu pattern Svelte 4'teki custom store'ların yerini alıyor. Writable/readable store API'si hâlâ çalışır (geriye uyumluluk) ama Svelte ekibi yeni kodda rune tabanlı yaklaşımı önerir.
$props: bileşen prop'ları
Svelte 5'te export let ile prop tanımlama kalktı. Yerine $props rune'u geldi:
<script>
let { title, count = 0 } = $props();
</script>
<h2>{title} ({count})</h2>TypeScript ile kullanıldığında tip güvenliği doğrudan sağlanır:
<script lang="ts">
interface Props {
title: string;
count?: number;
}
let { title, count = 0 }: Props = $props();
</script>Bu, Svelte 4'teki export let + ayrı tip tanımı ikilisinden çok daha temiz. Zod v4 gibi bir runtime validation kütüphanesiyle birleştirmek de doğal hale geliyor; $props() çağrısının sonucunu doğrudan bir Zod şemasından geçirebilirsiniz.
Actions'tan attachments'a geçiş
Svelte 4'te use:action direktifi, bir DOM elemanına doğrudan erişim veriyordu:
<!-- Svelte 4 -->
<script>
function tooltip(node, text) {
// node'a event listener ekle, temizle vb.
return { destroy() { /* cleanup */ } };
}
</script>
<div use:tooltip="'Merhaba'">Üzerime gel</div>Svelte 5 bu API'yi attachments ile genişletti. Attachment'lar fonksiyondur ve {@attach} tag'i ile bir elemana bağlanır. Svelte 5.29 sürümüyle bu API tanıtıldı.
Bir attachment, bir DOM elemanı alan ve opsiyonel olarak bir cleanup fonksiyonu döndüren fonksiyondur:
<script>
function tooltip(text: string) {
return (node: HTMLElement) => {
const handler = () => { /* tooltip göster */ };
node.addEventListener('mouseenter', handler);
return () => node.removeEventListener('mouseenter', handler);
};
}
</script>
<div {@attach tooltip('Merhaba')}>Üzerime gel</div>Actions ile attachments arasındaki fark birkaç yerde belirginleşir. Attachments herhangi bir yerde tanımlanabilir, .svelte.ts dosyalarında çalışır, birden fazla attachment aynı elemana eklenebilir. Actions use: direktifine bağlıyken, attachments daha kompoze edilebilir bir yapı sunar.
Bir eleman üzerinde birden fazla attachment kullanmak:
<div {@attach tooltip('İpucu')} {@attach trackClick(analytics)}>
İçerik
</div>Mevcut use:action sözdizimi Svelte 5'te hâlâ desteklenir ama uzun vadede attachments'ın tercih edilen yol olacağı Svelte RFC tartışmalarında netleşti.
SvelteKit 3 ve Svelte 5 uyumu
SvelteKit 2, Svelte 4 üzerine inşa edilmişti. SvelteKit 3 (henüz kesin tarih açıklanmadı, ama Svelte ekibi 2025 yılı içinde hedefliyor), Svelte 5'i minimum sürüm olarak gerektirecek. Mevcut SvelteKit 2 projeleri Svelte 5 ile çalışabiliyor; bu geçiş zaten mümkün. Ama SvelteKit 3 ile birlikte rune modu varsayılan hale gelecek ve eski $: sözdizimi uyumluluk moduna düşecek.
Geçiş için Svelte ekibi npx sv migrate svelte-5 komutunu sunuyor. Bu araç let + $: kalıplarını $state + $derived çağrılarına dönüştürüyor. Tam otomatik değil; özellikle store kullanımlarında manuel düzeltme gerekebiliyor.
React signals ile karşılaştırma
Signals kavramı 2025'te birçok framework'te var: Preact Signals, Solid.js, Angular Signals, Vue'nun ref/computed sistemi. Svelte'in farkı, signal'lerin derleme zamanında çözümlenmesi. Çalışma zamanında bir signals kütüphanesi yüklenmez; derleyici her $state çağrısını düz JavaScript'e çevirir.
React tarafında ise React Compiler farklı bir yol izliyor: mevcut hook tabanlı modeli koruyup derleme zamanında otomatik memoization ekliyor. React signal'e geçmedi, geçmeyi de planlamıyor (React ekibinin Şubat 2024'teki açıklaması bunu netleştirdi). İki yaklaşım temelden farklı: Svelte "reaktiviteyi dile göm" derken, React "mevcut modeli derleyiciyle optimize et" diyor.
$inspect ile geliştirme deneyimi
Geliştirme sırasında reaktif değerleri izlemek için $inspect rune'u kullanılır:
<script>
let count = $state(0);
$inspect(count); // count her değiştiğinde konsola yazar
</script>$inspect yalnızca geliştirme modunda çalışır; production build'de tamamen kaldırılır. console.log ile signal değerini izlemeye çalışmak genelde Proxy nesnesini yazdırır; $inspect bu sorunu ortadan kaldırır.
Ne zaman Svelte 5'e geçmeli
Mevcut Svelte 4 projeleri acil bir geçiş baskısı altında değil; Svelte 4 hâlâ güvenlik güncellemeleri alıyor. Ama yeni bir proje başlıyorsanız Svelte 5 ile başlamak mantıklı, çünkü rune tabanlı kod .svelte.ts dosyalarında paylaşılabilir mantık, TypeScript entegrasyonu, attachments gibi iyileştirmelerden yararlanıyor.
Geçiş maliyeti projenin büyüklüğüne bağlı. Küçük projeler sv migrate ile birkaç saat içinde taşınabilir. Çok sayıda custom store kullanan büyük projelerde her store'u rune tabanlı fonksiyona dönüştürmek birkaç gün sürebilir. Store'dan rune'a geçiş mekanik bir iş; kavramsal olarak zor değil ama hacim işi.