Files
dotfiles/dot_claude/skills/turnstile-spin/references/nextjs-app.md
T
Prad NukalaandClaude Fable 5 20891f3910 config(git,claude,ssh): templated gitconfig; claude settings/commands/hooks/skills; ssh config
gitconfig: name/email from chezmoi data, gh credential helper by PATH
lookup (portable to darwin). claude: no credentials, history, projects,
sessions, or plugin caches. ssh config as private_ (0600).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 14:53:46 -04:00

111 lines
2.8 KiB
Markdown

# Next.js (App Router)
For `app/`-directory Next.js projects. The widget needs to run on the client, so the page or component must be `"use client"`.
```tsx title="app/signup/page.tsx"
"use client";
import Script from "next/script";
import { useEffect, useState } from "react";
declare global {
interface Window {
onTurnstileSuccess?: (token: string) => void;
}
}
export default function SignupPage() {
const [token, setToken] = useState("");
useEffect(() => {
window.onTurnstileSuccess = (t: string) => setToken(t);
}, []);
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const res = await fetch("https://YOUR_WORKER_URL/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});
const data = await res.json();
if (data.success) {
// proceed
}
}
return (
<>
<Script
src="https://challenges.cloudflare.com/turnstile/v0/api.js"
strategy="afterInteractive"
/>
<form onSubmit={handleSubmit}>
<input name="email" type="email" required />
<div
className="cf-turnstile"
data-sitekey="YOUR_SITEKEY"
data-action="turnstile-spin-v1"
data-callback="onTurnstileSuccess"
/>
<button type="submit" disabled={!token}>
Sign up
</button>
</form>
</>
);
}
```
`data-callback` expects a string referencing a global function. The `useEffect` wires `window.onTurnstileSuccess` so the widget can call back into React state.
## Variant: Server Action
If you are using Server Actions, do the siteverify call from the action itself. The widget still goes in a client component, but the verify call moves server-side:
```tsx title="app/signup/actions.ts"
"use server";
export async function submitSignup(formData: FormData) {
const token = formData.get("cf-turnstile-response") as string;
const res = await fetch("https://YOUR_WORKER_URL/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ token }),
});
const data = await res.json();
if (!data.success) {
return { error: "Verification failed" };
}
// process signup
return { ok: true };
}
```
```tsx title="app/signup/page.tsx"
"use client";
import { submitSignup } from "./actions";
export default function SignupPage() {
return (
<form action={submitSignup}>
<input name="email" type="email" required />
<div
className="cf-turnstile"
data-sitekey="YOUR_SITEKEY"
data-action="turnstile-spin-v1"
/>
<button type="submit">Sign up</button>
</form>
);
}
```
## Substitutions
| Placeholder | Replace with |
| ------------------ | ------------------------------------------- |
| `YOUR_WORKER_URL` | Deployed Worker URL from Step 5 |
| `YOUR_SITEKEY` | Widget site key from Step 4 |