Skip to content
Playground

Custom form

Hook

Even with a simple setup, resulting code is very verbose. Therefore, after choosing a theme, validator and translation, it is convenient to create a wrapper around the useForm2.

custom-form.ts
import type { ErrorObject } from "ajv";
import { useForm2, type FormAPI, type UseFormOptions2 } from "@sjsf/form";
import { translation } from "@sjsf/form/translations/en";
import { theme } from "@sjsf/form/basic-theme";
import { createValidator2 } from "@sjsf/ajv8-validator";
type Defaults = "widgets" | "components" | "validator" | "translation";
export type CustomOptions<T, E> = Omit<UseFormOptions2<T, E>, Defaults> &
Partial<Pick<UseFormOptions2<T, E>, Defaults>>;
export function useCustomForm<T, E = ErrorObject>(
options: CustomOptions<T, E>
): FormAPI<T, E> {
const validator = createValidator2();
const defaults: Pick<UseFormOptions2<T, ErrorObject>, Defaults> = {
...theme,
validator,
translation,
};
return useForm2(
new Proxy(options, {
get(target, p, receiver) {
if (!(p in target)) {
return defaults[p as Defaults];
}
return Reflect.get(target, p, receiver);
},
}) as UseFormOptions2<T, E>
);
}

Component

You also can create your own form component.

custom-form.svelte
<script lang="ts" generics="T">
import { type Schema, type UiSchemaRoot, FormContent, SubmitButton } from '@sjsf/form'
import { useCustomForm } from '@/components/custom-form'
interface Props {
schema: Schema
uiSchema?: UiSchemaRoot
initialValue?: T
onSubmit?: (value: T | undefined) => void
}
const { schema, uiSchema, initialValue, onSubmit }: Props = $props()
const form = useCustomForm({
schema,
uiSchema,
initialValue,
onSubmit,
})
</script>
<form use:form.enhance style="display: flex; flex-direction: column; gap: 1rem">
<FormContent bind:value={form.formValue} />
<SubmitButton />
</form>