Vue and Nuxt forms to Google Sheets
A composition API fetch, a Nuxt server route, or a plain form action - every submission lands as a row in Google Sheets or Excel, with no automation tool replaying webhooks in the middle.
Vue gives you clean ownership of a form's state, and Nuxt adds a server when you want one. Neither ships a place for submissions to land. SheetLink Forms fills exactly that gap: every form gets a permanent endpoint at https://sheetlinkforms.com/f/{token} that accepts JSON, urlencoded, or multipart POSTs (bodies up to 256KB) and files each accepted submission as a row in Google Sheets or an Excel Online table.
Delivery is direct: an asynchronous worker writes to the Google Sheets API or Microsoft Graph with retries at 5 minutes, 30 minutes, and 2 hours, and every attempt shows up in a delivery log. Field names that match your column headers map automatically (case and punctuation insensitive - "Email", "email", and "E-mail" all land in the same column), so the keys you already bind with v-model usually just work. The full pipeline is described on how it works.
The product is free during beta and invite-gated - join the waitlist - and there are no per-submission fees, ever.
The best path for Vue and Nuxt
For a Vue SPA or a statically generated Nuxt site, the shortest path is a composition API fetch straight from the component. The endpoint is built for cross-origin browser posts: send Content-Type: application/json and it answers {"ok":true,"id":"..."} instead of the 303 redirect meant for classic HTML forms, which is exactly what you want to flip a ref into a success state. The per-form origin allowlist can pin submissions to your domains.
If your Nuxt app runs a server, a route in server/api makes a tidy relay - your component posts to /api/contact, the route validates or enriches the payload, then forwards it. There is no CORS to think about and one obvious place to change later. And when a page needs no JavaScript at all, a plain <form action> pointed at the endpoint still works, using the 303-redirect thank-you flow.
1. Connect a sheet and get your endpoint
Connect Google Sheets with one-click OAuth (the drive.file scope means the product only sees sheets you pick or create - never the rest of your Drive) or connect Excel Online and pick a table. You get a permanent endpoint of the form https://sheetlinkforms.com/f/{token}. On an empty sheet the header row is seeded for you; rows then append under it.
2. Write the composition API submit handler
Use the snippet above. Keep the keys in your reactive state named after the sheet's column headers so mapping is automatic; explicit per-field mapping exists in the dashboard for the cases where they differ. Include "_slhp": "" in the JSON body - it is the honeypot, and leaving it empty is what marks you human.
3. Render success and errors from the response
The endpoint answers JSON posts with {"ok":true,"id":"..."}, so a single ref flipped on result.ok covers the happy path. Non-2xx statuses are worth a branch too: 429 means rate limited, 403 means the origin allowlist rejected the page. Nothing here is coupled to a build mode - the same component works in a Vite SPA, an SSR Nuxt page, or output from nuxi generate.
4. Or relay through a Nuxt server route
Create server/api/contact.post.ts as in the commented block of the snippet: readBody, then $fetch the payload onward. Your component now posts to /api/contact on your own origin, so there is no CORS in the picture, and the route is the natural place for server-side validation or added fields before the row is filed.
5. Or skip JavaScript entirely
On a server-rendered or generated Nuxt page, a plain <form action="https://sheetlinkforms.com/f/{token}" method="POST"> works with zero client code. Include the off-screen honeypot input (<input name="_slhp" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px">) and the browser follows a 303 redirect to a thank-you page or your custom URL after submitting.
6. Test and read the delivery log
Send a test submission and watch the row land - or try the live demo first. The dashboard's delivery log records every attempt, and the docs list the error responses worth handling: 404 unknown token, 410 paused form, 403 origin not allowed, 413 payload too large, 429 rate limited, 400 unreadable body.
FAQ
Client fetch or Nuxt server route - which should I use?
Use the client fetch when the form is simple and the site may not have a server at all (SPA, nuxi generate). Use the server route when you want validation, enrichment, or one place to change the destination later. Both receive the same {"ok":true,"id":"..."} response, and both file the same row.
Does this work with a statically generated Nuxt site?
Yes. The endpoint lives outside your app, so pages produced by nuxi generate can post to it directly from the browser - either with the composition API fetch or with a plain form action and the 303 redirect flow. No server, no serverless functions, nothing to deploy beyond the static output.
Can I keep ad attribution with a Vue SPA?
Yes - put the sl.js embed on the page. It captures utm_* plus gclid, wbraid, gbraid, fbclid, and msclkid from the URL, persists them in localStorage across client-side navigations, and binds forms marked data-sheetlink or whose action points at /f/. A MutationObserver picks up forms Vue renders after the initial load.
Does the Options API change anything?
No. The integration is one HTTP POST, so a methods: { submit() } block, a composition API handler, a Pinia action, or a Nuxt server route all work identically. There is no SDK to install and nothing version-specific.
What happens if the Google Sheets API is briefly down?
Acceptance and delivery are decoupled. The endpoint accepts the submission, then an asynchronous worker writes the row and retries at 5 minutes, 30 minutes, and 2 hours if needed. The delivery log in the dashboard shows the outcome of every attempt.
Also on: Next.js · React · Plain HTML