svelte-effect-runtime

Form

Build progressive forms with submit, validate, enhance, and field errors.

Use Form when the browser should submit user input with normal form semantics while your server handler stays inside Effect. It is the best fit for validation, field errors, and progressive enhancement.

When to use this

Use forms for sign in, profile editing, settings, creation flows, and anything that should still have a clear HTML shape. Use Command for non-form mutations.

Minimal working example

import { Form } from "svelte-effect-runtime/server";
import { Effect, Schema } from "effect";

const ProfileInput = Schema.Struct({
  name: Schema.String,
});

export const update_name = Form(
  ProfileInput,
  ({ data }) =>
    Effect.succeed({ name: data.name }),
);
<script lang="ts">
  import { update_name } from "./profile.remote";
</script>

<form {...update_name}>
  <input name="name" />
  <button>Save</button>
</form>

Realistic variant

Validate before committing the mutation:

import { Form } from "svelte-effect-runtime/server";
import { Effect, Schema } from "effect";

const ProfileInput = Schema.Struct({
  name: Schema.String,
});

export const update_name = Form(
  ProfileInput,
  ({ data, invalid }) =>
    Effect.gen(function* () {
      if (data.name.trim().length === 0) {
        return yield* invalid.name("Name is required");
      }

      return { name: data.name.trim() };
    }),
);

On the client, the generated form object keeps SvelteKit's form descriptors. Programmatic helpers such as validate, submit, and enhance are wrapped so they can be yielded from Effect-aware code.

Common mistakes

  • Using a command for complex form validation and then rebuilding form behavior manually.
  • Trusting client-side validation without validating again in the server handler.
  • Returning one generic message when users need field-specific errors.
  • Forgetting that enhance is progressive; the server handler should still be correct without JavaScript.

On this page