Following my last article on Svelte 5 Runes, there are cases when you need to add custom functionality to a Rune, and share it. This method is pretty simple if you have been following this thread.
Implementation
You can share classes in Svelte, but safely sharing them in SvelteKit, requires a bit more code...
Create Your Class
class MyCounter {
current = $state(0);
constructor(value: number) {
this.current = value;
}
increment() {
this.current += 1;
}
decrement() {
this.current -= 1;
}
}
This could be any class you want to share. This class happens to have a reactive $state()
value, and shows an example of adding custom functionality like increment()
. This method works best when you have complicated classes you want to share.
Create Your Custom Sharable Class
// add SSR protection with Context
import { getContext, hasContext, setContext } from "svelte";
export class _Counter {
readonly #key: symbol;
constructor(name: string) {
this.#key = Symbol(name);
}
exists(): boolean {
return hasContext(this.#key);
}
get(): MyCounter {
return getContext(this.#key);
}
init(initialNumber: number): MyCounter {
// initialize any class
const _value = new MyCounter(initialNumber);
return setContext(this.#key, _value);
}
}
Export the Class
export const useCustomCounter = new _Counter('counter');
Usage
The usage would be the same as the Rune
class from before.
// initialize value
const customCounter = useCustomCounter.init(1);
...
<h1>Hello from Parent Custom: {customCounter.current}</h1>
And use it in children with your custom methods:
const customCounter = useCustomCounter.get();
<button type="button" onclick={() => customCounter.increment()}>
Increment Custom From Child
</button>
Advanced Shared Class
If you know you're always going to be sharing a class, then you can make a generic SharedClass
class to simplify things even more.
import { getContext, hasContext, setContext } from "svelte";
type Constructor<T, Args extends unknown[]> = new (...args: Args) => T;
export class SharedClass<T, Args extends unknown[]> {
readonly #key: symbol;
#class: Constructor<T, Args>;
constructor(name: string, className: Constructor<T, Args>) {
this.#key = Symbol(name);
this.#class = className;
}
exists(): boolean {
return hasContext(this.#key);
}
get(): T {
return getContext(this.#key);
}
init(...args: Args): T {
const _value = new this.#class(...args);
return setContext(this.#key, _value);
}
}
And use it like so:
import { SharedClass } from "./shared-class.svelte";
class MyCounter {
current = $state(0);
constructor(value: number) {
this.current = value;
}
increment() {
this.current += 1;
}
decrement() {
this.current -= 1;
}
}
export const useCustomCounter = new SharedClass('counter', MyCounter);
That's it!
Updated Code
J