Skip to content

Forms

React controlled inputs require two things for every field: a value prop bound to state, and an onChange handler that calls a setter. For large forms this becomes repetitive, which is why react-hook-form exists.

Svelte’s bind:value replaces both in one directive. Write bind:value={name} and Svelte wires up the two-way binding automatically — reading the state on render and writing back on every input event. There is no onChange, no e.target.value, no setter.

For full-page form submission without JavaScript, SvelteKit form actions in +page.server.ts handle POST requests server-side — similar to Remix action functions — with progressive enhancement built in.

React
function ProfileForm() {
const [name, setName] = React.useState('');
const [email, setEmail] = React.useState('');
function handleSubmit(e) {
e.preventDefault();
console.log({ name, email });
}
return (
<form onSubmit={handleSubmit}>
<input
value={name}
onChange={e => setName(e.target.value)}
placeholder="Name"
/>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
placeholder="Email"
/>
<button type="submit">Save</button>
</form>
);
}
Svelte
<script>
let name = $state('');
let email = $state('');
function handleSubmit(e) {
e.preventDefault();
console.log({ name, email });
}
</script>
<form onsubmit={handleSubmit}>
<input bind:value={name} placeholder="Name" />
<input type="email" bind:value={email} placeholder="Email" />
<button type="submit">Save</button>
</form>

bind:value works on input, textarea, and select. For checkboxes and radio buttons, use bind:checked. There is no onChange anywhere.

React
function LoginForm() {
const [password, setPassword] = React.useState('');
const [remember, setRemember] = React.useState(false);
return (
<form onSubmit={e => {
e.preventDefault();
console.log({ password, remember });
}}>
<input
type="password"
value={password}
onChange={e => setPassword(e.target.value)}
/>
<label>
<input
type="checkbox"
checked={remember}
onChange={e => setRemember(e.target.checked)}
/>
Remember me
</label>
<button type="submit">Log in</button>
</form>
);
}
Svelte
<script>
let password = $state('');
let remember = $state(false);
</script>
<form onsubmit={e => {
e.preventDefault();
console.log({ password, remember });
}}>
<input type="password" bind:value={password} />
<label>
<input type="checkbox" bind:checked={remember} />
Remember me
</label>
<button type="submit">Log in</button>
</form>
What does bind:value replace in a React controlled input?
How do you prevent default form submission in Svelte 5?
Which binding directive should you use for a checkbox input in Svelte?