Skip to content

Next.js CAPTCHA Integration

This recipe shows how to integrate TrustCaptcha into a Next.js application. The frontend uses our React wrapper, and verification happens server-side — either inside an App Router Route Handler or a Server Action. This page focuses on the server-side validation.

The setup section gets you to a working integration in three small steps using a Route Handler directly. Below it, an optional refactor section shows how to extract the verification into a reusable utility (and use it from a Server Action).

You should have already completed the following steps before you wire TrustCaptcha into your Next.js application.

  1. Read Get-Started: Get a quick overview of the concepts behind TrustCaptcha and the integration process in get started.

  2. Existing CAPTCHA: If you don’t have a CAPTCHA yet, sign in or create a new user account. Then create a new CAPTCHA.


Use our React wrapper inside your form component. The widget appends a hidden tc-verification-token field on submit (which Server Actions and Route Handlers receive as form data).

Terminal window
npm i @trustcomponent/trustcaptcha-react@^3.0.0
app/contact/contact-form.tsx
"use client";
import { TrustcaptchaComponent } from "@trustcomponent/trustcaptcha-react";
export function ContactForm() {
return (
<form method="post" action="/api/contact">
<label>Email <input type="email" name="email" required /></label>
<TrustcaptchaComponent sitekey="<your_site_key>" />
<button type="submit">Send</button>
</form>
);
}

See the Widget Overview for the full property reference.

Terminal window
npm i @trustcomponent/trustcaptcha-nodejs@^3.0.0
app/api/contact/route.ts
import { NextResponse } from "next/server";
import { TrustCaptcha } from "@trustcomponent/trustcaptcha-nodejs";
export async function POST(request: Request) {
// In production, load from env: process.env.TRUSTCAPTCHA_API_KEY
const apiKey = "<your_api_key>";
const formData = await request.formData();
const token = String(formData.get("tc-verification-token") ?? "");
try {
const result = await TrustCaptcha.getVerificationResult(apiKey, token);
if (!result.verificationPassed || result.score > 0.5) {
return NextResponse.json({ error: "CAPTCHA verification failed." }, { status: 400 });
}
} catch (err) {
return NextResponse.json({ error: "CAPTCHA verification failed." }, { status: 400 });
}
// CAPTCHA passed — request data is safe to use.
// ... your business logic ...
return NextResponse.json({ status: "ok" });
}

That’s it — the form is now protected. For real deployments, move the API key out of the source code (see the comment) and consider explicit failover handling — see Failover Behavior for the reasoning and a code template.


If you protect more than one Route Handler — or want to call the verification from a Server Action — extract the call into a small server-only utility.

.env.local
TRUSTCAPTCHA_API_KEY=<your_api_key>
lib/trust-captcha.ts
import "server-only";
import { TrustCaptcha } from "@trustcomponent/trustcaptcha-nodejs";
export async function verifyTrustCaptcha(token: string): Promise<void> {
try {
const result = await TrustCaptcha.getVerificationResult(
process.env.TRUSTCAPTCHA_API_KEY!,
token,
);
if (!result.verificationPassed || result.score > 0.5) {
throw new Error("CAPTCHA verification failed.");
}
} catch {
throw new Error("CAPTCHA verification failed.");
}
}

The import "server-only"; line guards against accidentally bundling this utility (and the API key) into a client component.

app/api/contact/route.ts
import { NextResponse } from "next/server";
import { verifyTrustCaptcha } from "@/lib/trust-captcha";
export async function POST(request: Request) {
const formData = await request.formData();
const token = String(formData.get("tc-verification-token") ?? "");
try {
await verifyTrustCaptcha(token);
} catch {
return NextResponse.json({ error: "CAPTCHA verification failed." }, { status: 400 });
}
return NextResponse.json({ status: "ok" });
}
app/contact/actions.ts
"use server";
import { verifyTrustCaptcha } from "@/lib/trust-captcha";
export async function submitContact(formData: FormData) {
const token = String(formData.get("tc-verification-token") ?? "");
try {
await verifyTrustCaptcha(token);
} catch {
return { error: "CAPTCHA verification failed." };
}
// ... your business logic ...
return { status: "ok" };
}
app/contact/contact-form.tsx
"use client";
import { TrustcaptchaComponent } from "@trustcomponent/trustcaptcha-react";
import { submitContact } from "./actions";
export function ContactForm() {
return (
<form action={submitContact}>
<label>Email <input type="email" name="email" required /></label>
<TrustcaptchaComponent sitekey="<your_site_key>" />
<button type="submit">Send</button>
</form>
);
}

App Router vs Pages Router. The examples target the App Router (app/api/.../route.ts). For the Pages Router, put the same logic into pages/api/contact.ts and read the token from req.body["tc-verification-token"] — the SDK call is identical.

Edge runtime. The Node.js SDK uses axios and Node APIs — run Route Handlers and Server Actions in the default Node runtime. If you’ve set export const runtime = "edge" on the route, switch it back to "nodejs" for endpoints that verify CAPTCHA.

Frontend wrapper. The form component above uses @trustcomponent/trustcaptcha-react. If you’d rather load the widget from our CDN as a <script> tag, see the JavaScript Guide — the server-side code is unchanged.

Configured SDK options. For custom timeouts, proxy, or a custom API host, construct a single TrustCaptcha instance with new TrustCaptcha({ apiKey, apiHost, proxy, ... }) inside your verifier utility. See the Node.js Guide for the full constructor options.


Once you have wired TrustCaptcha into your Next.js application, you can use TrustCaptcha to its full extent. However, we still recommend the following additional technical and organizational measures:

  • Security rules: You can find many security settings for your CAPTCHA in the CAPTCHA settings. These include, for example, authorized websites, CAPTCHA bypass for specific IP addresses, bypass keys, IP based blocking, geoblocking, individual difficulty and duration of the CAPTCHA, and much more. Learn more about the security rules.

  • Privacy & GDPR compliance: Include a passage in your privacy policy that refers to the use of TrustCaptcha. We also recommend that you enter into a data processing agreement with us to stay GDPR-compliant. Learn more about data protection.

  • Accessibility & UX: Customize TrustCaptcha to your website so that your website is as accessible as possible and offers the best possible user experience. More about accessibility.

  • Failover behavior: Decide how your backend should behave when our service is temporarily unreachable. This is particularly important for high-availability flows where blocking real users during an outage is worse than letting through a small amount of unverified traffic. Learn more about failover behavior.

  • Testing: If you use automated testing, make sure that the CAPTCHA does not block it. Learn more about testing.