Form actions
Since this is a Svelte library, you may want to use it with the SvelteKit.
With this package you will be able to perform server-side validation of the form data
even if the user has JavaScript disabled.
Installation
Section titled “Installation”npm i @sjsf/sveltekityarn add @sjsf/sveltekitpnpm add @sjsf/sveltekitbun add @sjsf/sveltekitExample
Section titled “Example”See details in the sections below
<script lang="ts"> import { createMeta, SvelteKitForm } from "@sjsf/sveltekit/client"; // WARN: You must export this ID Builder in your `defaults` file import { createFormIdBuilder } from "@sjsf/sveltekit";
import * as defaults from "$lib/form-defaults";
import type { ActionData, PageData } from "./$types";
const meta = createMeta<ActionData, PageData>().form;</script>
<SvelteKitForm {...defaults} {meta} idBuilder={createFormIdBuilder} onSuccess={(result) => { if (result.type === "success") { console.log(result.data?.post); } }}/>import type { Actions } from "@sveltejs/kit";import type { InitialFormData } from "@sjsf/sveltekit";import { createAction } from "@sjsf/sveltekit/server";
import * as defaults from "$lib/form-defaults";import { schema, type CreatePost } from "$lib/post";
export const load = async () => { return { // Should match action name form: { schema, initialValue: { title: "New post", content: "" }, } satisfies InitialFormData<CreatePost>, };};
export const actions = { default: createAction( { ...defaults, name: "form", schema, sendData: true, }, ({ title, content }: CreatePost) => { if (title.length > 100) { return [{ path: ["title"], message: "Title is too long" }]; } // Your logic here return { post: { id: "new-post", title, content } }; } ),} satisfies Actions;import type { FromSchema } from "json-schema-to-ts";import type { Schema } from "@sjsf/form";
export const schema = { title: "Post", type: "object", properties: { title: { title: "Title", type: "string", }, content: { title: "Content", type: "string", minLength: 5, }, }, required: ["title", "content"],} as const satisfies Schema;
export type CreatePost = FromSchema<typeof schema>;Server
Section titled “Server”If you want to populate the form from the database,
you can return InitialFormData object inside the load function.
import type { InitialFormData } from "@sjsf/sveltekit";
export const load = async () => { return { // Should match action name form: { schema, initialValue: { title: "New post", content: "" }, } satisfies InitialFormData<CreatePost>, };};You can define an action
using the createAction function.
In this case, the callback provided as the second argument
will only be called if the form data passes validation successfully.
export const actions = { default: createAction( { /* options */ }, (data, event, meta) => { /* logic */ } ),} satisfies Actions;If any errors occur during processing,
you can return an array of ValidationError objects.
if (title.length > 100) { return [{ path: ["title"], message: "Title is too long" }];}Advanced API
Section titled “Advanced API”By using the createFormHandler API, you can gain more control over the action handler, for example to:
- run checks before processing the form data
- work with dynamic schemas (call
createFormHandlerinside the action handler)
import { fail, type Actions } from "@sveltejs/kit";import type { InitialFormData } from "@sjsf/sveltekit";import { createFormHandler } from "@sjsf/sveltekit/server";
import * as defaults from "$lib/form-defaults";import { type CreatePost, schema } from "$lib/post";
export const load = async () => { return { // Should match action name form: { schema, initialValue: { title: "New post", content: "" }, } satisfies InitialFormData<CreatePost>, };};
const handleForm = createFormHandler<CreatePost, true>({ ...defaults, schema, sendData: true,});
export const actions = { default: async ({ request }) => { const [form, , invalid] = await handleForm( request.signal, await request.formData() ); if (!form.isValid) { return fail(400, { form }); } const { title, content } = form.data; if (title.length > 100) { return fail(400, { form: invalid([{ path: ["title"], message: "Title is too long" }]), }); } // Your logic here return { form, post: { id: "new-post", title, content } }; },} satisfies Actions;import type { FromSchema } from "json-schema-to-ts";import type { Schema } from "@sjsf/form";
export const schema = { title: "Post", type: "object", properties: { title: { title: "Title", type: "string", }, content: { title: "Content", type: "string", minLength: 5, }, }, required: ["title", "content"],} as const satisfies Schema;
export type CreatePost = FromSchema<typeof schema>;Client
Section titled “Client”On the client side you can use SvelteKitForm component (see example).
Or use more flexible solution:
<script lang="ts"> import { BasicForm } from "@sjsf/form"; // WARN: You must export this ID Builder in your `defaults` file import { createFormIdBuilder } from "@sjsf/sveltekit"; import { createMeta, setupSvelteKitForm } from "@sjsf/sveltekit/client";
import * as defaults from "$lib/form-defaults";
import type { ActionData, PageData } from "./$types";
const meta = createMeta<ActionData, PageData>().form; const { form } = setupSvelteKitForm(meta, { ...defaults, idBuilder: createFormIdBuilder, onSuccess: (result) => { if (result.type === "success") { console.log(result.data?.post); } }, });</script>
<BasicForm {form} method="POST" />The form data type will be inferred from the ActionData and PageData type.
If the value of the form cannot be inferred from action and
you did not use load function, then the third generic parameter of
the createMeta function will be used as the form data type
(default type is SchemaValue).
According to Svelte documentation your form should always use POST requests.
Progressive enhancement
Section titled “Progressive enhancement”By default, the form will work even with JavaScript disabled, but you should consider the following limitations of this mode of operation:
actionshould be created withsendData: trueto persist form data between page updates- Form fields for
oneOf,anyOf,dependencies,additionalPropertiesandadditionalItemswill not expand/switch their state. - Some widgets (like multiselect, depends on the theme) may will not work, because they require
JavaScript.
Comparison with Superforms
Section titled “Comparison with Superforms”Unlike Superforms, SJSF focuses on generating forms from JSON Schemas and managing their state, while the SvelteKit integration is just an additional layer. However, feel free to open feature requests if something important is still missing.
Missing features
Section titled “Missing features”Tainted fields
Section titled “Tainted fields”SJSF does not track the difference between the current and initial form state.
The form.isChanged property becomes true after any interaction with a form field
until the form is either successfully submitted or reset.
You can use the following code to check whether any changes have been made.
import { isSchemaValueDeepEqual } from '@sjsf/form/core';import { getValueSnapshot } from '@sjsf/form';import { preventPageReload } from '@sjsf/form/prevent-page-reload.svelte';
preventPageReload({ get isChanged() { return form.isChanged && !isSchemaValueDeepEqual(initialValue, getValueSnapshot(form)) }})Available events
Section titled “Available events”onSuccess— triggered when a request completes successfully, receiving anActionResult<NonNullable<ActionData>>as the resultonFailure— triggered when a request fails, receiving aFailedTask<unknown>as the argumentonSubmitError— triggered when client-side validation errors are presentonSubmissionFailure— triggered when an error occurs during validation
Loading timers
Section titled “Loading timers”Use request.isProcessed and request.isDelayed to access loading timers.
const { form, request } = setupSvelteKitForm(meta, { delayedMs: 500, timeoutMs: 8000, ...,})Multiple forms
Section titled “Multiple forms”Use idPrefix to create multiple forms.
Client
const { form, request } = setupSvelteKitForm(meta, { idPrefix: "foo", ...,})Server
export const actions = { default: createAction( { /* options */ }, (data: Model, _event, { idPrefix }) => { console.log(idPrefix, data); } )} satisfies ActionsSubmit behavior
Section titled “Submit behavior”Use the combinator option to control the behavior of multiple form submissions.
forgetPreviousabortPreviouswaitPreviousthrottle
import { waitPrevious } from '@sjsf/form/lib/task.svelte'
const { form, request } = setupSvelteKitForm(meta, { combinator: waitPrevious ...,})