Vue Forms
Building forms in Vue 3 is clean and declarative. By using the Composition API (<script setup>), we can bind form values with reactive states (ref or reactive) and submit submissions asynchronously to Lucid Forms.
Single File Component (SFC) Example
Section titled “Single File Component (SFC) Example”Here is a full Vue 3 template that captures form input and submits it without reloading the browser window.
<script setup>import { ref, reactive } from 'vue'
const form = reactive({ name: '', email: '', message: '', _honeypot: '' // Spam trap})
const status = ref('idle') // 'idle' | 'loading' | 'success' | 'error'const errorMessage = ref('')
const handleSubmit = async () => { status.value = 'loading' errorMessage.value = ''
// Honeypot spam check if (form._honeypot) { status.value = 'success' return }
try { const response = await fetch('https://api.lucidforms.co/f/{your-form-id}', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ name: form.name, email: form.email, message: form.message }) })
const responseData = await response.json()
if (response.ok) { status.value = 'success' // Reset form form.name = '' form.email = '' form.message = '' } else { status.value = 'error' errorMessage.value = responseData.error || 'Form submission failed.' } } catch (err) { status.value = 'error' errorMessage.value = 'A network error occurred. Please try again.' }}</script>
<template> <div class="form-container"> <h2>Contact Us</h2>
<div v-if="status === 'success'" class="alert-success"> ✓ Thank you! Your submission was received successfully. </div>
<div v-if="status === 'error'" class="alert-error"> ✗ {{ errorMessage }} </div>
<form @submit.prevent="handleSubmit"> <!-- Invisible spam field --> <input type="text" v-model="form._honeypot" class="hidden-honeypot" tabindex="-1" autocomplete="off" />
<div class="form-group"> <label for="name">Name</label> <input type="text" id="name" v-model="form.name" required /> </div>
<div class="form-group"> <label for="email">Email</label> <input type="email" id="email" v-model="form.email" required /> </div>
<div class="form-group"> <label for="message">Message</label> <textarea id="message" v-model="form.message" required rows="4"></textarea> </div>
<button type="submit" :disabled="status === 'loading'"> {{ status === 'loading' ? 'Sending...' : 'Send Message' }} </button> </form> </div></template>
<style scoped>.form-container { max-width: 400px; margin: 0 auto; font-family: system-ui, sans-serif;}.form-group { margin-bottom: 15px;}label { display: block; margin-bottom: 5px; font-weight: 500;}input[type="text"],input[type="email"],textarea { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;}.hidden-honeypot { display: none;}button { padding: 10px 15px; background-color: #42b883; color: white; border: none; border-radius: 4px; cursor: pointer;}button:disabled { opacity: 0.6;}.alert-success { color: #2e7d32; background-color: #e8f5e9; padding: 10px; border-radius: 4px; margin-bottom: 15px;}.alert-error { color: #c62828; background-color: #ffebee; padding: 10px; border-radius: 4px; margin-bottom: 15px;}</style>