diff --git a/components/Captcha/Captcha.tsx b/components/Captcha/Captcha.tsx
new file mode 100644
index 00000000..4743539b
--- /dev/null
+++ b/components/Captcha/Captcha.tsx
@@ -0,0 +1,38 @@
+import { memo } from "react";
+import { useTheme } from "next-themes";
+import HCaptcha from "@hcaptcha/react-hcaptcha";
+
+type CaptchaProps = {
+ size?: "normal" | "compact" | "invisible";
+ theme?: "light" | "dark";
+ id?: string;
+
+ // callbacks pulled verbatim from node_modules/@hcaptcha/react-hcaptcha/types/index.d.ts
+ /* eslint-disable @typescript-eslint/no-explicit-any */
+ onExpire?: () => any;
+ onOpen?: () => any;
+ onClose?: () => any;
+ onChalExpired?: () => any;
+ onError?: (event: string) => any;
+ onVerify?: (token: string) => any;
+ onLoad?: () => any;
+ /* eslint-enable @typescript-eslint/no-explicit-any */
+};
+
+const Captcha = ({ size = "normal", theme, id, ...rest }: CaptchaProps) => {
+ const { resolvedTheme } = useTheme();
+
+ return (
+
+ );
+};
+
+export default memo(Captcha);
diff --git a/components/ContactForm/ContactForm.tsx b/components/ContactForm/ContactForm.tsx
index ce718d1e..b75d29bc 100644
--- a/components/ContactForm/ContactForm.tsx
+++ b/components/ContactForm/ContactForm.tsx
@@ -1,9 +1,8 @@
import { useState } from "react";
-import { useTheme } from "next-themes";
import classNames from "classnames/bind";
import { Formik, Form, Field } from "formik";
-import HCaptcha from "@hcaptcha/react-hcaptcha";
import Link from "../Link/Link";
+import Captcha from "../Captcha/Captcha";
import { SendIcon, CheckOcticon, XOcticon } from "../Icons";
import type { FormikHelpers } from "formik";
@@ -22,8 +21,6 @@ type ContactFormProps = {
};
const ContactForm = ({ className }: ContactFormProps) => {
- const { resolvedTheme } = useTheme();
-
// status/feedback:
const [submitted, setSubmitted] = useState(false);
const [success, setSuccess] = useState(null);
@@ -137,12 +134,7 @@ const ContactForm = ({ className }: ContactFormProps) => {
- setFieldValue("h-captcha-response", token)}
- />
+ setFieldValue("h-captcha-response", token)} />