Skip to main content
It binds the fields to refs with v-model, posts them as JSON and shows formcarry’s answer, and the same request carries files, a spam blocker token and validation errors. The form’s Setup page in the dashboard opens a CodeSandbox with every example on this page pointed at your endpoint. Its components put setup() inside export default; the snippets here use <script setup>, and the refs and the requests are the same.

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 Vue, bind each field to a ref with v-model, post them as JSON, and send Accept: application/json so the answer comes back as JSON:
The keys of the JSON body are the field names formcarry stores. The email key is the visitor’s address, so it becomes the reply to address of your notification 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. Only a request that never reaches formcarry, such as a dropped connection, ends in the catch. Use @submit.prevent rather than @submit, otherwise the browser reloads the page before the answer arrives.

Files

To send files, keep the input’s File objects in a ref, append each to a FormData and leave the content type to the browser:
Each append name is the field name formcarry stores, so a multiple input’s files each get their own. Send Accept and nothing else rather than adding Content-Type yourself, otherwise the multipart boundary is missing and the upload fails. To take files from a dropzone, put the File objects that vue3-dropzone passes to onDrop in files:
To stay with JSON for small files, read each one with FileReader and send its data URL under its own key, never an array:
Use data URLs for small files. Using them with larger files bloats the request by about a third and reaches the 50 MB limit sooner.
Files are stored on paid plans only. Free plans store the rest of the submission without them.

Spam blocker

To add a challenge, render it in the component, send its token in the body under the name formcarry reads, and paste the secret key into the form’s settings under Form Security: To use reCAPTCHA v2, render the widget and keep the token from its verify event in a ref:
Clear the token on expire rather than keeping it, otherwise formcarry answers 403 to the stale one. To use reCAPTCHA v3, register the plugin with your site key in main.js:
Then ask for a token when the visitor submits and pass it to send below; there is no widget:
To use hCaptcha, keep the token from its verify event:
To use Turnstile, bind the token with v-model:
To send the token, put it in the body under the name from the table, with a guard for an empty one:
Refuse to send while the token is empty rather than posting anyway, otherwise formcarry answers 403 for reCAPTCHA or 400 for the others and the visitor sees an error they could have avoided. Add localhost to the challenge’s allowed domains while you test.

Validation errors

A 422 carries errors, one entry per failing field, each with a message. To show each one next to its field, keep them in a ref keyed by field name:
The keys of errors are your field names, so fieldErrors.email is the message for the email field. To show the answer in a toast instead, register vue-toastification in main.js with app.use(Toast) and read title and message from the answer:
Every answer carries title and message, so the same line covers every refused submission.

What’s next

Stuck? Write to help@formcarry.com. Include the form id.