hapiform-with-recaptcha-new
v0.0.31
Published
HAPI Form Mixin for Nuxt with reCAPTCHA + DataLayer
Readme
hapiform-with-recaptcha
A Vue / Nuxt mixin that handles form submission to HAPI Form, Google reCAPTCHA v2 verification, Google Tag Manager DataLayer push, webhook integration, and file uploads — all in one drop-in mixin.
Features
- Submit form data to your HAPI Form endpoint
- Google reCAPTCHA v2 verification (with optional disable)
- Auto-push
form_submissionevent towindow.dataLayeron success - Optional webhook POST to any external URL after submission
- File upload support via native
<input type="file">or FilePond - Automatic
busystate — disables all buttons while submitting - i18n-aware — appends
localequery param when$i18nis available - Redirect on success via path string or custom function
Installation
npm install hapiform-with-recaptcha
# or
yarn add hapiform-with-recaptchaQuick Start
1. Add the reCAPTCHA render element
Place an element with the recaptcha-el attribute anywhere inside your form. The mixin will automatically render the reCAPTCHA widget into it.
<div recaptcha-el></div>2. Use the mixin in your component
<template>
<div>
<form @submit.prevent="submit">
<!-- Validation errors -->
<ul v-if="Object.keys(errors).length > 0">
<li v-for="(messages, field) in errors" :key="field">
{{ field }}: {{ messages[0] }}
</li>
</ul>
<!-- Fields -->
<div>
<label for="name">Name</label>
<input id="name" type="text" v-model="fields.name" />
<span v-if="errors.name">{{ errors.name[0] }}</span>
</div>
<div>
<label for="message">Message</label>
<textarea id="message" v-model="fields.message"></textarea>
<span v-if="errors.message">{{ errors.message[0] }}</span>
</div>
<!-- reCAPTCHA widget renders here -->
<div recaptcha-el></div>
<span>{{ recaptchaError }}</span>
<button type="submit" :disabled="busy">
{{ busy ? 'Submitting…' : 'Submit' }}
</button>
</form>
</div>
</template>
<script>
import hapiMixins from 'hapiform-with-recaptcha';
export default {
mixins: [hapiMixins],
data() {
return {
// Required — your HAPI Form UUID
hapiformID: 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx',
// Optional — redirect after successful submission
redirectTo: '/thank-you/',
// Optional — POST form data to an external URL after submission
integrationScriptUrl: '',
// Optional — payload format for the webhook: 'FormData' | 'Json'
integrationDataFormat: 'FormData',
// Optional — set to true to disable reCAPTCHA entirely
recaptchaDisabled: false,
// Optional — reCAPTCHA widget theme: 'light' | 'dark'
recaptchaTheme: 'light',
// Optional — reCAPTCHA widget size: 'normal' | 'compact'
recaptchaSize: 'normal',
// Your form fields
fields: {
name: '',
message: '',
},
};
},
methods: {
// Override to run custom logic after a successful submission
// onSuccess(res) {},
// Override to run custom logic after a failed submission
// onFailed(err) {},
},
};
</script>Configuration
| Property | Type | Default | Description |
|---|---|---|---|
| hapiformID | String | "" | Required (unless endpoint is set). Your HAPI Form UUID. |
| endpoint | String | — | Override the full submission URL instead of using hapiformID. |
| redirectTo | String \| Function | "" | Path to redirect to after success, or a callback function. |
| recaptchaDisabled | Boolean | false | Set to true to skip reCAPTCHA verification. |
| recaptchaTheme | String | "light" | reCAPTCHA widget theme: "light" or "dark". |
| recaptchaSize | String | "normal" | reCAPTCHA widget size: "normal" or "compact". |
| integrationScriptUrl | String | "" | Webhook URL to POST form data to after a successful submission. |
| integrationDataFormat | String | "FormData" | Payload format for the webhook: "FormData" or "Json". |
| filepond | String | "" | $refs key of a FilePond instance for managed file uploads. |
Reactive State
These data properties are available in your template:
| Property | Type | Description |
|---|---|---|
| busy | Boolean | true while a submission is in progress. All form buttons are automatically disabled. |
| errors | Object | Validation errors returned from the server, keyed by field name. |
| statusCode | Number | HTTP status code from the last submission response. |
| recaptchaError | String | reCAPTCHA error message, if any. |
| fields | Object | Your form field values. Bind with v-model. |
Events
| Event | Payload | Description |
|---|---|---|
| hapi:success | AxiosResponse | Emitted after a successful submission. |
| hapi:error | Object | Emitted on submission failure; contains the error response data. |
<ContactForm @hapi:success="onFormSuccess" @hapi:error="onFormError" />Lifecycle Hooks
Override these methods in your component to add custom logic:
methods: {
onSuccess(res) {
console.log('Submitted!', res);
},
onFailed(err) {
console.error('Failed:', err);
},
}File Uploads
Native <input type="file">
<input type="file" multiple ref="files" @change="handleFiles" />The handleFiles method is provided by the mixin. Selected files will be included in the submission under files[0], files[1], etc.
FilePond
Set the filepond data property to the $refs key of your FilePond component:
<file-pond ref="myPond" ... />data() {
return {
filepond: 'myPond', // must match the ref name
};
}DataLayer (Google Tag Manager)
On every successful submission the mixin automatically pushes to window.dataLayer:
window.dataLayer.push({
event: 'form_submission',
enhanced_conversion_data: {
// all fields from this.fields
// phone-like fields (contact, phone, mobile, etc.) are normalised to phone_number
},
});To provide your own DataLayer logic, define addFormDataToDataLayer as a method in your component and the default push will be skipped.
Webhook Integration
Set integrationScriptUrl to POST the form data to a secondary endpoint after a successful submission. Requests from localhost / 127.0.0.1 are silently skipped.
data() {
return {
integrationScriptUrl: 'https://your-crm.example.com/webhook',
integrationDataFormat: 'Json', // 'FormData' (default) or 'Json'
};
}Disabling reCAPTCHA
data() {
return {
recaptchaDisabled: true,
};
}When disabled, the recaptcha-el element is not required.
Multiple Forms on One Page
Each component instance creates its own reCAPTCHA widget independently. Add a recaptcha-el element inside each form and they will render and reset separately.
