Svelte 4 had a simple writeable store which allowed you to create a global shared state easily. Svelte 5 does not yet have such a method, but it is super easy to create one using the new $state hook. This ref method is almost functionally equivalent in usage to the writeable store from Svelte 4, except that it uses a getter/setter instead of the magic $ syntax. And that is because the original writeable is an observable, whereas $state is a signal. Check out this article to learn the difference. To summarize, an observable is subscribed to, whereas a signal is just a value.
class Ref<T> { _value: T; constructor(initial: T) { let state = $state(initial); this._value = state; } get value(): T { return this._value } set value(newState: T) { this._value = newState }}function ref<T>(initial: T) { return new Ref(initial);}
Getters and setters on classes are more performant than on objects, but if you prefer the object syntax, you can use this version instead.
ts
function ref<T>(initial: T) { let state = $state(initial); return { get value() { return state }, set value(newState: T) { state = newState } };}
History
This is a simple utility function that can be used to create a history object, which tracks the current value and allows you to undo and redo changes to the value. Set the maxLength to 0 to disable the history limit.
function createHistory<T>(initial: T, maxLength = 50) { let history = $state<T[]>([initial]); let index = $state(0); return { get current() { return history[index] }, set current(newState: T) { history.splice(index + 1, history.length - index, newState); if (maxLength > 0) history = history.slice(Math.max(0, history.length - maxLength), history.length); index = history.length - 1; }, get entries() { return history }, get index() { return index }, get canUndo() { return index > 0 }, get canRedo() { return index < history.length - 1 }, undo() { index = Math.max(0, index - 1) }, redo() { index = Math.min(history.length - 1, index + 1) } };}
Fetch Wrapper
This is a simple utility function that can be used to create a fetcher object. It is a simple wrapper around the fetch API that provides reactivity for Svelte 5.
function createFetcher(url) { let result = $state(null); let loading = $state(false); let error = $state(null); const controller = new AbortController(); async function runFetch() { loading = true; try { const resp = await fetch(url, { signal: controller.signal }); result = await resp.json(); } catch { error = "Add error message here!"; result = null; } loading = false; } $effect(async () => { runFetch(); return controller.abort; }); return { get result() { return result }, get loading() { return loading }, get error() { return error }, abort(reason) { controller.abort(reason) }, refetch: runFetch };}
Websocket Wrapper
This is a simple utility function that can be used to create a websocket object. It is a simple wrapper around the WebSocket API that provides reactivity for Svelte 5.
ts
export default function createSocket<T>( url: string | URL, options?: { name?: string; protocols?: string | string[]; onOpen?: (event: Event) => void; onError?: (error: Event) => void; onMessage?: (data: T, requests: T[], event: Event) => void; }) { let requests = $state<T[]>([]); let latest = $derived(requests.at(requests.length - 1)); let websocket: WebSocket | null = null; $effect(() => { websocket = new WebSocket(url, options?.protocols) websocket.onopen = (event) => { console.log(`${options?.name || "Websocket"} connected!`); if (options?.onOpen) options.onOpen(event); }; websocket.onerror = (error) => { console.log(`${options?.name || "Websocket"} error:`, error); if (options?.onError) options.onError(error); }; websocket.onmessage = (event) => { const data = JSON.parse(event.data) as T; requests = requests.concat(data); if (options?.afterMessage) options.onMessage(data, requests, event); }; return () => { if (websocket) websocket.close(); } }); return { get requests() { return requests }, get latest() { return latest }, close() { if (websocket) websocket.close(); }, send(data: T) { if (!websocket) throw new Error("No websocket connection"); if (websocket.readyState !== websocket.OPEN) throw new Error("No websocket connection"); websocket.send(JSON.stringify(data)); } };}