How to Add Google reCAPTCHA to Laravel Forms Using Inertia.js and Vue 3

Google reCAPTCHA must frequently be integrated with Laravel Inertia when creating contemporary full-stack applications.js projects that prevent bots from accessing your forms by using Vue 3. If you don’t protect a basic contact form, job application, or sign-up page, it may get hundreds of spam submissions.

In this guide, you will walk through a practical Laravel Inertia Vue 3 reCAPTCHA setup that works smoothly with Laravel 11. You will connect Google reCAPTCHA v3 to your backend, wire it into your Vue 3 Inertia forms, and validate every request on the server. After completing this Laravel Inertia Vue reCAPTCHA v3 tutorial, you will have a simple, reusable method for safeguarding any form in your application without bothering actual users.

Why your Laravel Inertia Vue 3 forms need reCAPTCHA

Imagine a simple “Request a quote” form on your SaaS site. As traffic grows, bots find it and start posting fake data. Your sales team loses time, your inbox fills with junk, and important leads hide in the noise.

reCAPTCHA helps you to filter out automated spam, keep genuine users on a smooth path, protect server resources from abuse

Using Google reCAPTCHA v3 Laravel Inertia with Vue 3 gives you an invisible, user‑friendly shield. Most users never see a challenge; the score runs in the background, and your Laravel app decides what to accept.

Setup Google reCAPTCHA v3 keys

Register your site in the Google reCAPTCHA and get the reCAPTCHA site key and Secret Key.

Open your .env file and add:

RECAPTCHA_SITE_KEY=your_site_key_here
RECAPTCHA_SECRET_KEY=your_secret_key_here

Expose them via config/services.php:

'recaptcha' => [
    'site_key' => env('RECAPTCHA_SITE_KEY'),
    'secret_key' => env('RECAPTCHA_SECRET_KEY'),
],

With this setup, you can access this values anywhere into your application using the config() helper. This small step keeps secrets out of your codebase while giving your Laravel 12 reCAPTCHA Inertia.js Vue 3 integration a clean configuration base.

Build routes and controller in Laravel 12

Let’s create a simple contact form as a practical example. Open routes/web.php and modify it for showing contact form and handle form submmission.

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ContactController;

Route::get('/contact', [ContactController::class, 'create'])->name('contact.create');
Route::post('/contact', [ContactController::class, 'store'])->name('contact.store');

Generate the controller:

php artisan make:controller ContactController

In app/Http/Controllers/ContactController.php:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use Inertia\Inertia;

class ContactController extends Controller
{
    public function create()
    {
        return Inertia::render('Contact/Create', [
            'recaptchaSiteKey' => config('services.recaptcha.site_key'),
            'flash' => [
                'success' => session('success'),
            ],
        ]);
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => ['required', 'string', 'max:191'],
            'email' => ['required', 'email'],
            'message' => ['required', 'string'],
            'recaptcha_token' => ['required', 'string'],
        ]);

        $isValid = $this->verifyRecaptcha($validated['recaptcha_token'], $request->ip());

        if (! $isValid) {
            return back()
                ->withErrors(['recaptcha' => 'reCAPTCHA validation failed. Please try again.'])
                ->withInput();
        }

        return redirect()
            ->route('contact.create')
            ->with('success', 'Thank you for contacting us.');
    }

    protected function verifyRecaptcha(string $token, string $ip): bool
    {
        $response = Http::asForm()->post('https://www.google.com/recaptcha/api/siteverify', [
            'secret' => config('services.recaptcha.secret_key'),
            'response' => $token,
            'remoteip' => $ip,
        ]);

        if (! $response->json('success')) {
            return false;
        }

        $score = $response->json('score', 0);

        return $score >= 0.5;
    }
}

The controller will pass the site key into your Inertia page, Verify the recaptcha_token and form data.
Call Google to confirm the token and check the score and then redirect with a flash message when the submission is valid. This provides a stable backend for any use case where you want to add Google ReCaptcha to Laravel Inertia Vue.

Add Google reCAPTCHA v3 to the Inertia Vue 3 form

Now create the Vue 3 page that talks to reCAPTCHA v3 and your Laravel 12 backend. Create or update contact.vue file at resources/js/Pages/Contact/Create.vue:

<template>
  <div class="max-w-xl mx-auto py-10">
    <h1 class="text-2xl font-bold mb-6">
      Contact us
    </h1>

    <form @submit.prevent="submit">
      <div class="mb-4">
        <label class="block mb-1">Name</label>
        <input
          v-model="form.name"
          type="text"
          class="border rounded w-full p-2"
        />
        <div v-if="form.errors.name" class="text-red-600 text-sm">
          {{ form.errors.name }}
        </div>
      </div>

      <div class="mb-4">
        <label class="block mb-1">Email</label>
        <input
          v-model="form.email"
          type="email"
          class="border rounded w-full p-2"
        />
        <div v-if="form.errors.email" class="text-red-600 text-sm">
          {{ form.errors.email }}
        </div>
      </div>

      <div class="mb-4">
        <label class="block mb-1">Message</label>
        <textarea
          v-model="form.message"
          class="border rounded w-full p-2"
          rows="4"
        ></textarea>
        <div v-if="form.errors.message" class="text-red-600 text-sm">
          {{ form.errors.message }}
        </div>
      </div>

      <div v-if="form.errors.recaptcha" class="text-red-600 text-sm mb-4">
        {{ form.errors.recaptcha }}
      </div>

      <button
        type="submit"
        :disabled="form.processing"
        class="bg-blue-600 text-white px-4 py-2 rounded"
      >
        <span v-if="form.processing">Sending...</span>
        <span v-else>Send message</span>
      </button>
    </form>

    <div v-if="successMessage" class="mt-4 text-green-700">
      {{ successMessage }}
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useForm, usePage } from '@inertiajs/vue3'

const page = usePage()

const form = useForm({
  name: '',
  email: '',
  message: '',
  recaptcha_token: '',
})

const successMessage = ref(page.props.flash?.success || '')

const recaptchaSiteKey = page.props.recaptchaSiteKey

const loadRecaptchaScript = () => {
  if (document.getElementById('recaptcha-script')) {
    return
  }

  const script = document.createElement('script')
  script.id = 'recaptcha-script'
  script.src = 'https://www.google.com/recaptcha/api.js?render=' + recaptchaSiteKey
  script.async = true
  script.defer = true
  document.head.appendChild(script)
}

const getRecaptchaToken = () => {
  return new Promise((resolve, reject) => {
    if (!window.grecaptcha) {
      reject(new Error('reCAPTCHA not loaded'))
      return
    }

    window.grecaptcha.ready(() => {
      window.grecaptcha
        .execute(recaptchaSiteKey, { action: 'contact' })
        .then(token => resolve(token))
        .catch(error => reject(error))
    })
  })
}

const submit = async () => {
  try {
    const token = await getRecaptchaToken()
    form.recaptcha_token = token

    form.post(route('contact.store'))
  } catch (error) {
    form.setError('recaptcha', 'reCAPTCHA could not load. Please try again.')
  }
}

onMounted(() => {
  loadRecaptchaScript()
})
</script>

This page loads the reCAPTCHA script once when the component mounts, then requests a reCAPTCHA v3 token every time the user submits the form. It sends that token to the Laravel 12 controller through Inertia and displays any validation or reCAPTCHA errors right next to the form fields. This creates the essential google recaptcha v3 laravel inertia flow inside a Vue 3 SPA.

Conclusion

You have now walked through the process of Intregrate Google reCAPTCHA in Laravel Inertia.js Vue 3 application. Your backend checks the tokens and scores, your frontend requests a new token each time you submit and your forms are now protected with a robust, invisible barrier against bots.

This same pattern can be applied to registration, login, support, and any other form on your public-facing application. By following this tutorial on laravel inertia vue 3 recaptcha v3, you are protecting your application from bots, saving your team time, and providing a smooth, modern experience for your users.