--- name: composable-svelte-forms description: Form patterns and validation for Composable Svelte. Use when building forms, validating user input, or integrating Zod schemas. Covers FormConfig, createFormReducer, field-level validation, async validation, form state management, and reactive wrapper patterns. --- # Composable Svelte Forms This skill covers form patterns, Zod validation integration, and state management for forms in Composable Svelte applications. --- ## FORMS SYSTEM ### Integrated Mode (Recommended) **When**: Complex apps where parent needs to observe form submission, validation, and integrate with other state. --- ## COMPLETE EXAMPLE ### 1. Define Zod Schema ```typescript import { z } from 'zod'; const contactSchema = z.object({ name: z.string().min(1, 'Name is required'), email: z.string().email('Invalid email address'), message: z.string().min(10, 'Message must be at least 10 characters') }); type ContactData = z.infer; ``` ### 2. Create Form Config ```typescript import type { FormConfig } from '@composable-svelte/core/components/form'; export const contactFormConfig: FormConfig = { schema: contactSchema, initialData: { name: '', email: '', message: '' }, mode: 'all', // Validate on blur, change, and submit debounceMs: 500, onSubmit: async (data) => { const result = await api.submitContact(data); return result; } }; ``` ### 3. Parent State ```typescript interface AppState { contactForm: FormState; submissions: Submission[]; successMessage: string | null; } ``` ### 4. Parent Actions ```typescript type AppAction = | { type: 'contactForm'; action: FormAction } | { type: 'clearSuccessMessage' }; ``` ### 5. Parent Reducer ```typescript import { createFormReducer, scope } from '@composable-svelte/core'; const formReducer = createFormReducer(contactFormConfig); const appReducer: Reducer = (state, action, deps) => { switch (action.type) { case 'contactForm': { const [formState, formEffect] = formReducer( state.contactForm, action.action, deps ); const newState = { ...state, contactForm: formState }; const effect = Effect.map(formEffect, (fa): AppAction => ({ type: 'contactForm', action: fa })); // Observe submission success if (action.action.type === 'submissionSucceeded') { return [ { ...newState, submissions: [...state.submissions, { id: crypto.randomUUID(), data: formState.data, timestamp: Date.now() }], successMessage: 'Thanks for contacting us!' }, Effect.batch( effect, Effect.afterDelay(3000, (d) => d({ type: 'clearSuccessMessage' })) ) ]; } return [newState, effect]; } case 'clearSuccessMessage': return [{ ...state, successMessage: null }, Effect.none()]; default: return [state, Effect.none()]; } }; ``` ### 6. Component - Reactive Wrapper **CRITICAL**: Use reactive wrapper pattern for forms to integrate form state with parent state. ```svelte {#if $store.successMessage}
{$store.successMessage}
{/if}
{ e.preventDefault(); formStore.dispatch({ type: 'submit' }); }}> formStore.dispatch(action)} state={formStore.state} > formStore.dispatch(action)} state={formStore.state} > formStore.dispatch(action)} state={formStore.state} >