Svelte makes form handling simple with its two-way binding and reactive declarations.
Basic Form Handling
svelte
<script>
let name = '';
let email = '';
let message = '';
function handleSubmit() {
console.log({ name, email, message });
}
</script>
<form on:submit|preventDefault={handleSubmit}>
<label>
Name:
<input type="text" bind:value={name} />
</label>
<label>
Email:
<input type="email" bind:value={email} />
</label>
<label>
Message:
<textarea bind:value={message}></textarea>
</label>
<button type="submit">Submit</button>
</form>Form Validation
svelte
<script>
let formData = {
name: '',
email: '',
password: ''
};
let errors = {};
function validate() {
errors = {};
if (!formData.name) {
errors.name = 'Name is required';
}
if (!formData.email) {
errors.email = 'Email is required';
} else if (!/^[^@]+@[^@]+.[^@]+$/.test(formData.email)) {
errors.email = 'Invalid email';
}
if (!formData.password) {
errors.password = 'Password is required';
} else if (formData.password.length < 8) {
errors.password = 'Password must be at least 8 characters';
}
return Object.keys(errors).length === 0;
}
function handleSubmit() {
if (validate()) {
console.log('Form submitted:', formData);
}
}
</script>
<form on:submit|preventDefault={handleSubmit}>
<label>
Name:
<input type="text" bind:value={formData.name} />
{#if errors.name}
<span class="error">{errors.name}</span>
{/if}
</label>
<label>
Email:
<input type="email" bind:value={formData.email} />
{#if errors.email}
<span class="error">{errors.email}</span>
{/if}
</label>
<label>
Password:
<input type="password" bind:value={formData.password} />
{#if errors.password}
<span class="error">{errors.password}</span>
{/if}
</label>
<button type="submit">Submit</button>
</form>Checkbox and Radio Groups
svelte
<script>
let isChecked = false;
let selectedOption = 'option1';
let toppings = [];
let allToppings = ['Pepperoni', 'Mushrooms', 'Onions', 'Sausage'];
</script>
<label>
<input type="checkbox" bind:checked={isChecked} />
Accept terms and conditions
</label>
<div>
{#each ['option1', 'option2', 'option3'] as option}
<label>
<input type="radio" bind:group={selectedOption} value={option} />
{option}
</label>
{/each}
</div>
<div>
{#each allToppings as topping}
<label>
<input type="checkbox" bind:group={toppings} value={topping} />
{topping}
</label>
{/each}
</div>Select and Multi-Select
svelte
<script>
let selected = 'apple';
let fruits = ['apple', 'banana', 'orange', 'grape'];
let selectedItems = [];
let allItems = [
{ value: 'item1', label: 'Item 1' },
{ value: 'item2', label: 'Item 2' },
{ value: 'item3', label: 'Item 3' }
];
</script>
<select bind:value={selected}>
{#each fruits as fruit}
<option value={fruit}>{fruit}</option>
{/each}
</select>
<select multiple bind:value={selectedItems}>
{#each allItems as item}
<option value={item.value}>{item.label}</option>
{/each}
</select>File Uploads
svelte
<script>
let files;
async function handleUpload() {
const formData = new FormData();
for (const file of files) {
formData.append('files', file);
}
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
});
const result = await response.json();
console.log(result);
}
</script>
<form on:submit|preventDefault={handleUpload}>
<label>
Upload files:
<input type="file" bind:files={files} multiple />
</label>
{#if files}
<p>Selected {files.length} file(s)</p>
{/if}
<button type="submit">Upload</button>
</form>Form Stores
javascript
// stores/form.js
import { writable, derived } from 'svelte/store';
function createForm(initial, validate) {
const form = writable(initial);
const errors = writable({});
const touched = writable({});
const isValid = derived([form, errors], ([$form, $errors]) => {
return Object.keys($errors).length === 0;
});
function touch(field) {
touched.update(t => ({ ...t, [field]: true }));
}
async function submit(onSubmit) {
const validationErrors = await validate(get(form));
if (Object.keys(validationErrors).length > 0) {
errors.set(validationErrors);
return false;
}
errors.set({});
return onSubmit(get(form));
}
function reset() {
form.set(initial);
errors.set({});
touched.set({});
}
return {
form,
errors,
touched,
isValid,
touch,
submit,
reset
};
}Form Actions
javascript
// actions/form.js
export function formEnhance(node, { onSubmit, validate }) {
async function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const data = Object.fromEntries(formData);
if (validate) {
const errors = await validate(data);
if (errors) {
node.dispatchEvent(new CustomEvent('errors', { detail: errors }));
return;
}
}
await onSubmit(data);
}
node.addEventListener('submit', handleSubmit);
return {
destroy() {
node.removeEventListener('submit', handleSubmit);
}
};
}Svelte provides a simple and powerful way to handle forms with minimal boilerplate.