Skip to main content

Remote Forms

For forms that submit to server actions using form from $app/server.

Define the Remote Function

// user.remote.ts
import { form } from '$app/server'
import { z } from 'zod/v4'

export const userSchema = z.object({
name: z.string(),
email: z.string().email()
})

export const createUserForm = form(userSchema, async (data) => {
await db.insert(users).values(data)
redirect(303, '/users')
})

Basic Usage

<script lang="ts">
import { TypedForm } from '@repo/form'
import { createUserForm, userSchema } from './user.remote'
</script>

<TypedForm.Remote form={createUserForm} schema={userSchema} />

Pre-populate for Edit

Fetch data and set it on the form before rendering.

<script lang="ts">
import { TypedForm } from '@repo/form'
import { updateUserForm, getUserQuery, userSchema } from './user.remote'

const { params } = $props()
const user = await getUserQuery({ id: params.id })
updateUserForm.fields.set(user)
</script>

<TypedForm.Remote form={updateUserForm} schema={userSchema} />

No-Args Form

For forms without input validation (e.g., delete buttons).

// user.remote.ts
export const deleteUserForm = form(async () => {
const { id } = await getValidatedParams(z.object({ id: z.string() }))
await db.delete(users).where(eq(users.id, id))
redirect(303, '/users')
})
<TypedForm.Remote form={deleteUserForm}>
{#snippet button({ submitting })}
<Button variant="destructive" disabled={submitting}>Delete</Button>
{/snippet}
</TypedForm.Remote>