CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-sveltekit-superforms

Making SvelteKit forms a pleasure to use with comprehensive validation, type safety, and progressive enhancement.

Pending
Overview
Eval results
Files

field-proxies.mddocs/

Field Proxies

Type-safe proxy functions for binding form fields with automatic string conversion, supporting various data types including numbers, dates, booleans, arrays, and files. Enables seamless integration between Svelte's reactive system and HTML form elements.

Capabilities

Generic Field Proxy

The base field proxy function that provides type-safe binding for any form field with automatic data type conversion.

/**
 * Creates a generic field proxy for type-safe form field binding
 * @param form - Form store or SuperForm instance
 * @param path - Path to the field in the form data structure
 * @param options - Proxy configuration options
 * @returns FieldProxy with get/set methods and store interface
 */
function fieldProxy<T, Path>(
  form: Writable<T> | SuperForm<T>,
  path: Path,
  options?: ProxyOptions
): FieldProxy<FormPathType<T, Path>>;

interface FieldProxy<T> extends Writable<T> {
  /** Get current field value */
  get(): T;
  /** Set field value and mark as tainted */
  set(value: T): void;
  /** Update field value with function */
  update(fn: (value: T) => T): void;
  /** Subscribe to field value changes */
  subscribe(fn: (value: T) => void): () => void;
}

interface ProxyOptions {
  /** Taint behavior when field value changes */
  taint?: TaintOption;
}

type TaintOption = boolean | 'untaint' | 'untaint-all' | 'untaint-form';

Usage Examples:

<script lang="ts">
  import { fieldProxy } from "sveltekit-superforms/client";
  
  export let data;
  const { form } = superForm(data.form);
  
  // Create proxy for nested field
  const nameProxy = fieldProxy(form, 'user.name');
  const ageProxy = fieldProxy(form, 'user.age', { taint: 'untaint' });
</script>

<input bind:value={$nameProxy} />
<input type="number" bind:value={$ageProxy} />

Number Proxies

Specialized proxies for numeric fields that handle string conversion, validation, and formatting for HTML number inputs.

/**
 * Creates a proxy for integer form fields with string conversion
 * @param form - Form store or SuperForm instance
 * @param path - Path to the integer field
 * @param options - Integer-specific options
 * @returns Writable string store that syncs with integer field
 */
function intProxy<T, Path>(
  form: Writable<T> | SuperForm<T>,
  path: Path,
  options?: {
    /** How to handle empty values */
    empty?: 'null' | 'undefined' | 'zero';
    /** Whether empty field is initially shown as empty when value is zero */
    initiallyEmptyIfZero?: boolean;
    /** Taint behavior */
    taint?: TaintOption;
  }
): Writable<string>;

/**
 * Creates a proxy for number form fields with string conversion and formatting
 * @param form - Form store or SuperForm instance  
 * @param path - Path to the number field
 * @param options - Number-specific options
 * @returns Writable string store that syncs with number field
 */
function numberProxy<T, Path>(
  form: Writable<T> | SuperForm<T>,
  path: Path,
  options?: {
    /** How to handle empty values */
    empty?: 'null' | 'undefined' | 'zero';
    /** Decimal delimiter for display */
    delimiter?: '.' | ',';
    /** Whether empty field is initially shown as empty when value is zero */
    initiallyEmptyIfZero?: boolean;
    /** Taint behavior */
    taint?: TaintOption;
  }
): Writable<string>;

Usage Examples:

<script lang="ts">
  const { form } = superForm(data.form);
  
  // Integer field with empty handling
  const ageProxy = intProxy(form, 'age', {
    empty: 'zero',
    initiallyEmptyIfZero: true
  });
  
  // Decimal number with comma delimiter
  const priceProxy = numberProxy(form, 'price', {
    delimiter: ',',
    empty: 'null'
  });
</script>

<input 
  type="number" 
  bind:value={$ageProxy}
  placeholder="Enter age"
/>

<input 
  type="number" 
  step="0.01"
  bind:value={$priceProxy}
  placeholder="0,00"
/>

Boolean Proxy

Proxy for boolean fields that handles checkbox and radio button interactions with customizable string representations.

/**
 * Creates a proxy for boolean form fields with string conversion
 * @param form - Form store or SuperForm instance
 * @param path - Path to the boolean field
 * @param options - Boolean-specific options
 * @returns Writable string store that syncs with boolean field
 */
function booleanProxy<T, Path>(
  form: Writable<T> | SuperForm<T>,
  path: Path,
  options?: {
    /** String value representing true */
    trueStringValue?: string;
    /** Taint behavior */
    taint?: TaintOption;
  }
): Writable<string>;

Usage Examples:

<script lang="ts">
  const { form } = superForm(data.form);
  
  // Standard checkbox
  const agreeProxy = booleanProxy(form, 'agreed');
  
  // Custom true value for radio buttons
  const roleProxy = booleanProxy(form, 'isAdmin', {
    trueStringValue: 'admin'
  });
</script>

<label>
  <input type="checkbox" bind:checked={$agreeProxy} />
  I agree to the terms
</label>

<label>
  <input 
    type="radio" 
    name="role" 
    value="admin"
    bind:group={$roleProxy}
  />
  Administrator
</label>

Date Proxy

Specialized proxy for date fields supporting various date formats and timezone handling for HTML date/time inputs.

/**
 * Creates a proxy for date form fields with format conversion
 * @param form - Form store or SuperForm instance
 * @param path - Path to the date field
 * @param options - Date-specific options
 * @returns Writable string store that syncs with date field
 */
function dateProxy<T, Path>(
  form: Writable<T> | SuperForm<T>,
  path: Path,
  options?: {
    /** Date format for HTML input compatibility */
    dateFormat?: 
      | 'date'           // YYYY-MM-DD
      | 'datetime'       // YYYY-MM-DDTHH:mm
      | 'time'           // HH:mm
      | 'date-utc'       // UTC date
      | 'datetime-utc'   // UTC datetime  
      | 'time-utc'       // UTC time
      | 'date-local'     // Local date
      | 'datetime-local' // Local datetime
      | 'time-local'     // Local time
      | 'iso';           // Full ISO string
    /** Taint behavior */
    taint?: TaintOption;
  }
): Writable<string>;

Usage Examples:

<script lang="ts">
  const { form } = superForm(data.form);
  
  // Date input
  const birthdateProxy = dateProxy(form, 'birthdate', {
    dateFormat: 'date'
  });
  
  // Datetime-local input
  const appointmentProxy = dateProxy(form, 'appointment', {
    dateFormat: 'datetime-local'
  });
  
  // Time input
  const timeProxy = dateProxy(form, 'meetingTime', {
    dateFormat: 'time'
  });
</script>

<input type="date" bind:value={$birthdateProxy} />
<input type="datetime-local" bind:value={$appointmentProxy} />
<input type="time" bind:value={$timeProxy} />

Array Proxy

Advanced proxy for array fields that provides methods for adding, removing, and reordering array items with type safety.

/**
 * Creates a proxy for array form fields with manipulation methods
 * @param form - Form store or SuperForm instance
 * @param path - Path to the array field
 * @param options - Array-specific options
 * @returns ArrayProxy with array manipulation methods
 */
function arrayProxy<T, Path>(
  form: Writable<T> | SuperForm<T>,
  path: Path,
  options?: ProxyOptions
): ArrayProxy<T, Path>;

interface ArrayProxy<T, Path> extends Writable<FormPathType<T, Path>[]> {
  /** Add new item to the end of array */
  push(item: FormPathType<T, Path>): void;
  /** Remove and return last item from array */
  pop(): FormPathType<T, Path> | undefined;
  /** Add new item to the beginning of array */
  unshift(item: FormPathType<T, Path>): void;
  /** Remove and return first item from array */
  shift(): FormPathType<T, Path> | undefined;
  /** Insert item at specific index */
  insert(index: number, item: FormPathType<T, Path>): void;
  /** Remove item at specific index */
  remove(index: number): FormPathType<T, Path> | undefined;
  /** Move item from one index to another */
  move(fromIndex: number, toIndex: number): void;
  /** Replace item at specific index */
  replace(index: number, item: FormPathType<T, Path>): void;
  /** Clear all items from array */
  clear(): void;
  /** Get current array length */
  length: Readable<number>;
}

Usage Examples:

<script lang="ts">
  const { form } = superForm(data.form);
  
  const tagsProxy = arrayProxy(form, 'tags');
  
  function addTag() {
    $tagsProxy = [...$tagsProxy, ''];
  }
  
  function removeTag(index: number) {
    tagsProxy.remove(index);
  }
</script>

{#each $tagsProxy as tag, i}
  <div class="tag-input">
    <input bind:value={$tagsProxy[i]} placeholder="Enter tag" />
    <button type="button" on:click={() => removeTag(i)}>Remove</button>
  </div>
{/each}

<button type="button" on:click={addTag}>Add Tag</button>

File Proxies

Specialized proxies for file upload fields supporting single and multiple file handling with validation and metadata access.

/**
 * Creates a proxy for single file upload fields
 * @param form - Form store or SuperForm instance
 * @param path - Path to the file field
 * @param options - File-specific options
 * @returns Writable store for File objects
 */
function fileProxy<T, Path>(
  form: Writable<T> | SuperForm<T>,
  path: Path,
  options?: ProxyOptions
): Writable<File | null>;

/**
 * Creates a proxy for single file upload fields with form integration
 * @param form - SuperForm instance
 * @param path - Path to the file field
 * @param options - File-specific options with validation
 * @returns FormFieldProxy with file handling
 */
function fileFieldProxy<T, Path>(
  form: SuperForm<T>,
  path: Path,
  options?: ProxyOptions
): FormFieldProxy<T, Path>;

/**
 * Creates a proxy for multiple file upload fields
 * @param form - Form store or SuperForm instance
 * @param path - Path to the files field
 * @param options - Files-specific options
 * @returns Writable store for FileList or File arrays
 */
function filesProxy<T, Path>(
  form: Writable<T> | SuperForm<T>,
  path: Path,
  options?: ProxyOptions
): Writable<FileList | File[] | null>;

/**
 * Creates a proxy for multiple file upload fields with form integration
 * @param form - SuperForm instance
 * @param path - Path to the files field
 * @param options - Files-specific options with validation
 * @returns FormFieldProxy with multiple file handling
 */
function filesFieldProxy<T, Path>(
  form: SuperForm<T>,
  path: Path,
  options?: ProxyOptions
): FormFieldProxy<T, Path>;

interface FormFieldProxy<T, Path> extends FieldProxy<FormPathType<T, Path>> {
  /** Field validation errors */
  errors: Readable<string[]>;
  /** Field constraints */
  constraints: Readable<InputConstraint>;
  /** Whether field is tainted */
  tainted: Readable<boolean>;
}

Usage Examples:

<script lang="ts">
  const { form } = superForm(data.form);
  
  // Single file upload
  const avatarProxy = fileProxy(form, 'avatar');
  
  // Multiple file upload with field integration
  const documentsProxy = filesFieldProxy(form, 'documents');
  
  $: console.log('Avatar:', $avatarProxy?.name, $avatarProxy?.size);
  $: console.log('Documents:', $documentsProxy?.length);
</script>

<!-- Single file -->
<input 
  type="file" 
  accept="image/*"
  on:change={(e) => $avatarProxy = e.target.files?.[0] || null}
/>

<!-- Multiple files with validation -->
<input 
  type="file" 
  multiple
  accept=".pdf,.doc,.docx"
  bind:files={$documentsProxy}
/>
{#if $documentsProxy.errors.length > 0}
  <div class="errors">
    {#each $documentsProxy.errors as error}
      <p class="error">{error}</p>
    {/each}
  </div>
{/if}

String Proxy

Basic proxy for string fields with customizable behavior and validation integration.

/**
 * Creates a proxy for string form fields
 * @param form - Form store or SuperForm instance
 * @param path - Path to the string field
 * @param options - String-specific options
 * @returns Writable string store
 */
function stringProxy<T, Path>(
  form: Writable<T> | SuperForm<T>,
  path: Path,
  options?: ProxyOptions
): Writable<string>;

/**
 * Creates a proxy for string form fields with form integration
 * @param form - SuperForm instance
 * @param path - Path to the string field
 * @param options - String-specific options with validation
 * @returns FormFieldProxy with string handling
 */
function formFieldProxy<T, Path>(
  form: SuperForm<T>,
  path: Path,
  options?: ProxyOptions
): FormFieldProxy<T, Path>;

Usage Examples:

<script lang="ts">
  const { form } = superForm(data.form);
  
  // Basic string proxy
  const nameProxy = stringProxy(form, 'name');
  
  // String proxy with validation integration
  const emailProxy = formFieldProxy(form, 'email');
</script>

<input bind:value={$nameProxy} />

<input 
  type="email" 
  bind:value={$emailProxy}
  class:error={$emailProxy.errors.length > 0}
/>
{#if $emailProxy.errors.length > 0}
  <span class="error">{$emailProxy.errors[0]}</span>
{/if}

Core Types

interface FieldProxy<T> extends Writable<T> {
  /** Get current field value */
  get(): T;
  /** Set field value */
  set(value: T): void;
  /** Update field value with function */
  update(fn: (value: T) => T): void;
  /** Subscribe to value changes */
  subscribe(fn: (value: T) => void): () => void;
}

interface FormFieldProxy<T, Path> extends FieldProxy<FormPathType<T, Path>> {
  /** Field validation errors */
  errors: Readable<string[]>;
  /** HTML input constraints */
  constraints: Readable<InputConstraint>;
  /** Whether field has been modified */
  tainted: Readable<boolean>;
}

interface ArrayProxy<T, Path> extends Writable<FormPathType<T, Path>[]> {
  /** Array manipulation methods */
  push(item: FormPathType<T, Path>): void;
  pop(): FormPathType<T, Path> | undefined;
  unshift(item: FormPathType<T, Path>): void;
  shift(): FormPathType<T, Path> | undefined;
  insert(index: number, item: FormPathType<T, Path>): void;
  remove(index: number): FormPathType<T, Path> | undefined;
  move(fromIndex: number, toIndex: number): void;
  replace(index: number, item: FormPathType<T, Path>): void;
  clear(): void;
  /** Array length as readable store */
  length: Readable<number>;
}

interface ProxyOptions {
  /** Taint configuration */
  taint?: TaintOption;
}

interface InputConstraint {
  /** Minimum value/length */
  min?: number;
  /** Maximum value/length */
  max?: number;
  /** Step value for numbers */
  step?: number; 
  /** Minimum string length */
  minlength?: number;
  /** Maximum string length */
  maxlength?: number;
  /** Regular expression pattern */
  pattern?: string;
  /** Whether field is required */
  required?: boolean;
}

type TaintOption = boolean | 'untaint' | 'untaint-all' | 'untaint-form';

type FormPath<T, Type = any> = string; // String path to field in T
type FormPathType<T, Path extends string> = any; // Type at path in T

Install with Tessl CLI

npx tessl i tessl/npm-sveltekit-superforms

docs

client-management.md

field-proxies.md

index.md

server-processing.md

validation-adapters.md

tile.json