Skip to content
Playground Form Builder

Component casting

SJSF provides a rich set of interchangeable components, but some of them cannot be used directly out of the box (tagsField, filesField, nativeFileField, etc.). Let’s look at a few ways to work with them.

You can suppress typing errors if you just want to quickly test an idea or see how a component looks in place.

const uiSchema: UiSchema = {
"ui:components": {
// @ts-expect-error
arrayField: "tagsField"
}
}

You can create a new component that adapts the interface of the original component (for example, arrayField) to the interface of the replacement component (tagsField).

<script lang="ts" module>
import type { SchemaArrayValue } from '@sjsf/form/core';
import type { FieldCommonProps } from '@sjsf/form';
declare module '@sjsf/form' {
interface ComponentProps {
anyTagsField: FieldCommonProps<SchemaArrayValue>;
}
interface ComponentBindings {
anyTagsField: 'value';
}
}
</script>
<script lang="ts">
import type { ComponentProps } from '@sjsf/form';
import TagsField from '@sjsf/form/fields/extra-fields/tags.svelte';
import { assertStrings } from '$lib/form/cast';
let { value = $bindable(), ...rest }: ComponentProps['arrayField'] = $props();
</script>
<TagsField
{...rest}
bind:value={
() => {
assertStrings(value);
return value;
},
(v) => (value = v)
}
/>

Why is this needed? Throwing an error when data formats don’t match is only one possible approach. You should decide on the adaptation strategy and implement it yourself for now, but in the future the library may provide ready-made adapted components.

You can reduce boilerplate code by using the cast utility from @sjsf/form/lib/component:

import { cast } from "@sjsf/form/lib/component";
import type { ComponentDefinition } from "@sjsf/form";
import TagsField from "@sjsf/form/fields/extra-fields/tags.svelte";
import { assertStrings } from '$lib/form/cast';
declare module "@sjsf/form" {
interface ComponentProps {
tagsFieldWrapper: FieldCommonProps<SchemaArrayValue>;
}
interface ComponentBinding {
tagsFieldWrapper: "value";
}
}
const tagsFieldWrapper = cast(TagsField, {
value: {
transform(props) {
assertStrings(props.value);
return props.value;
},
},
}) satisfies ComponentDefinition<"arrayField">;

Instead of proving component compatibility at the type level, you can extend the resolver so that components are applied automatically to suitable JSON Schemas.