Skip to content

Instantly share code, notes, and snippets.

@dualjack
Created April 4, 2026 17:18
Show Gist options
  • Select an option

  • Save dualjack/36e1ad4fd01ec9e754bfba9aa5ce9719 to your computer and use it in GitHub Desktop.

Select an option

Save dualjack/36e1ad4fd01ec9e754bfba9aa5ce9719 to your computer and use it in GitHub Desktop.
SuperFormRunes - Svelte5 SuperForms with Runes support.
import {type FormOptions, superForm, type SuperValidated} from "sveltekit-superforms";
import {fromStoreTwoWayBinding} from "@dualjack/svelte-tools";
import {tick, untrack} from "svelte";
import {createAttachmentKey, fromAction} from "svelte/attachments";
export function superFormRunes<
T extends Record<string, unknown> = Record<string, unknown>,
M = any,
In extends Record<string, unknown> = T
>(formValidated: SuperValidated<T, M, In>|(() => SuperValidated<T, M, In>), formOptions?: FormOptions<T, M, In>){
const initialValidated = typeof formValidated === 'function' ? formValidated() : formValidated;
const formProxy = superForm<T, M, In>(
initialValidated,
formOptions
);
const form = fromStoreTwoWayBinding(formProxy.form);
const formId = fromStoreTwoWayBinding(formProxy.formId);
const submitting = fromStoreTwoWayBinding(formProxy.submitting);
const message = fromStoreTwoWayBinding(formProxy.message);
const allErrors = fromStoreTwoWayBinding(formProxy.allErrors);
const errors = fromStoreTwoWayBinding(formProxy.errors);
const delayed = fromStoreTwoWayBinding(formProxy.delayed);
const posted = fromStoreTwoWayBinding(formProxy.posted);
const timeout = fromStoreTwoWayBinding(formProxy.timeout);
const tainted = fromStoreTwoWayBinding(formProxy.tainted);
const constraints = fromStoreTwoWayBinding(formProxy.constraints);
// Logic for reacting to changes in the form validated
// data (for example page invalidation).
let isFirstCycle = true;
$effect(() => {
if(typeof formValidated !== 'function') return;
const next = formValidated();
if(isFirstCycle){
isFirstCycle = false;
return;
}
untrack(() => {
formProxy.formId.set(next.id);
formProxy.form.set(next.data);
formProxy.errors.set(next.errors);
formProxy.message.set(next.message);
// constraints are optional in SuperValidated payloads
if(next.constraints){
formProxy.constraints.set(next.constraints);
}
});
});
const enhanceKey = createAttachmentKey();
// Return wrapped rune-based stores.
return {
...formProxy,
get form() { return form.current },
set form(x) { form.current = x },
get data() { return form.current },
set data(x) { form.current = x },
get formId() { return formId.current },
set formId(x) { formId.current = x },
get submitting() { return submitting.current },
set submitting(x) { submitting.current = x },
get message() { return message.current },
set message(x) { message.current = x },
get allErrors() { return allErrors.current },
set allErrors(x) { allErrors.current = x },
get errors() { return errors.current },
set errors(x) { errors.current = x },
get delayed() { return delayed.current },
set delayed(x) { delayed.current = x },
get posted() { return posted.current },
set posted(x) { posted.current = x },
get timeout() { return timeout.current },
set timeout(x) { timeout.current = x },
get tainted() { return tainted.current },
set tainted(x) { tainted.current = x },
get constraints() { return constraints.current },
set constraints(x) { constraints.current = x },
enhance: {
[enhanceKey]: fromAction(formProxy.enhance)
},
async submit(){
await tick();
formProxy.submit();
}
} satisfies { [key in keyof typeof formProxy]: any } & any
}
export type SuperFormRunes<
T extends Record<string, unknown> = Record<string, unknown>,
M = any,
In extends Record<string, unknown> = T
> = ReturnType<typeof superFormRunes<T, M, In>>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment