Prerequisites
Before you start, you need:- A formcarry account. Sign up is free.
- A form in the dashboard. Its endpoint is on the form’s Setup page. The examples use
https://formcarry.com/s/AbC123xyz; put yours in its place.
The component
To send a form from Svelte, keep the fields in state, post them as JSON, and sendAccept: application/json so the answer comes back as JSON:
email key is the visitor’s address, so it becomes the reply to address of your notification email and the recipient of the auto response. A stored submission answers with code: 200. A refused one answers with the reason in message, see What every form needs.
Send Accept: application/json rather than Content-Type alone, otherwise the answer is the thank you page’s HTML and res.json() throws.
Files
To send files, build aFormData from the form element and leave the content type to the browser:
FormData reads the values from the elements, so every input needs a name, the file input too. Send Accept and nothing else rather than adding Content-Type yourself, otherwise the multipart boundary is missing and the upload fails.
For several files, bind the input’s files, build the FormData yourself and append each file under its own name:
FileReader, and give each file its own key, never an array:
Files are stored on paid plans only. Free plans store the rest of the submission without them.
SvelteKit
To keep the form working before the JavaScript loads, keepaction and method="POST" on the form and read the endpoint from e.currentTarget.action in the handler:
use:enhance, otherwise SvelteKit reads formcarry’s answer as one of its own form action results and form in your page never updates.
To post from the server instead, write a form action in +page.server.js:
+page.svelte, post with method="POST" and no action, and read the action’s answer from form:
form.message. use:enhance belongs here, on a form that posts to your own action, and keeps the page from reloading on each submission.
Every submission now leaves your server, so all your visitors share its IP address and the 1 submission per 15 seconds per form per IP address limit applies to all of them together. The spam blocker token still has to come from the browser, so its widget stays in the form.
Spam blocker
To add a challenge, render the vendor’s widget, keep its token in state, send it under the name formcarry reads, and paste the secret key into the form’s settings under Form Security:
With Turnstile, load its script before the component mounts, then render the widget into an element you bind:
grecaptcha.render and hcaptcha.render. With v3 there is no widget. Ask for the token when the visitor submits:
403 (reCAPTCHA) or 400 (the others). The guard goes at the top of the handler:
localhost to the challenge’s allowed domains while you test.
Validation errors
A422 carries errors, one entry per failing field, each with a message. To show each one next to its field, add a branch to the handler:
What’s next
- Field validations: the rules you can set per field.
- Spam protection: the challenges, the filter and the honeypot.
- Email notifications: the auto response the visitor gets, keyed on
email.