hCaptcha component for Svelte
Inspired by https://github.com/rodneylab/sveltekit-hcaptcha-form - Thank you, it really helped a lot!
A simple component to use hCaptcha in your Svelte powered app.
Tested with SvelteKit but I see no reason why it shouldn't work with plain Svelte with some minor modifications mainly around the use of $app/env.
If you were able to get this working with plain Svelte, please let me know!
Hcaptcha.svelte
<script>
	import { onMount, onDestroy } from 'svelte'
	import { browser } from '$app/env'
	let hcaptcha
	let hcaptchaWidgetID
	export let token = null
	// forces 1-way binding
	let captchaToken
	$: token = captchaToken
	export let isValid = false
	// forces 1-way binding
	let captchaValid
	$: isValid = captchaValid
	onMount(() => {
		setTimeout(function () {
			if (browser) {
				hcaptcha = window.hcaptcha
				if (hcaptcha.render) {
					hcaptchaWidgetID = hcaptcha.render('hcaptcha', {
						sitekey: import.meta.env.VITE_CAPTCHA_KEY,
						size: 'normal',
						callback: onValidCaptcha,
						'error-callback': onErrorCaptcha,
						theme: 'dark'
					})
				}
			}
		}, 500)
	})
	onDestroy(() => {
		if (browser) {
			hcaptcha = null
		}
	})
	function onValidCaptcha(e) {
		//console.log('verified event', e)
		captchaToken = e
		captchaValid = true
	}
	function onErrorCaptcha(e) {
		//console.log('error event', {error: e.error})
		captchaToken = null
		captchaValid = false
		hcaptcha.reset(hcaptchaWidgetID)
	}
</script>
<svelte:head>
	<script src='https://js.hcaptcha.com/1/api.js?render=explicit' async defer></script>
</svelte:head>
<div id='hcaptcha' class='h-captcha border-0'/>
Import the component into your page:
import Hcaptcha from './Hcaptcha.svelte'
Set local variables that will be bound to the component:
let captchaValid = false // use this to check frontend validity
let captchaToken  // generated token will be stored here - send this to hCaptcha's validator via POST in your onSubmit method for your form
Add the component to your markup:
<Hcaptcha bind:token={captchaToken} bind:isValid={captchaValid} />
export async function post({request}) {
	const data = await request.json()
	const postData = data.formData
	try {
		const captchaToken = postData.captchaToken
		// validate captcha
		const captchaData = {'secret': process.env.CAPTCHA_SECRET, 'response': captchaToken}
		const searchParams = new URLSearchParams(captchaData)
		const submit = await fetch(process.env.CAPTCHA_VERIFY_URL, {
			method: 'POST',
			body: searchParams
		})
		const captchaResults = await submit.json()
		if (!captchaResults.success) {
			throw new Error("Robot check failed. Please try again.")
		}
		
		// do stuff with submitted form data
		
		return {
			body: {status: "success"}
		}
	} catch (e) {
		console.error("error: ", e)
		console.error(e.message)
		if (e.response) {
			console.error(e.response.body)
		}
		return {
			status: 400,
			body: {status: "error", msg: e.message}
		}
	}
}