[{"data":1,"prerenderedAt":127},["ShallowReactive",2],{"doc:\u002Fdocs\u002Fsdks\u002Fnextjs":3},{"page":4,"toc":118,"updated":126},{"path":5,"title":6,"seoTitle":7,"description":8,"blocks":9},"\u002Fdocs\u002Fsdks\u002Fnextjs","Next.js","Next.js SDK: App Router and Server Actions","Identify visitors in Next.js 15 and newer. A provider that works from a Server Component layout, a hidden form field, and a Server Action helper that reads it.",[10,13,18,23,26,39,42,44,50,52,55,57,66,71,74,76,81,84,99,103,106,108,116],{"type":11,"text":12},"p","`@fingerly\u002Fnext` builds on [`@fingerly\u002Freact`](\u002Fdocs\u002Fsdks\u002Freact) for the App Router and the Pages Router. It adds a provider you can render from a Server Component, a form field that carries the request ID, and a server entry point with helpers that never import React.",{"type":14,"level":15,"text":16,"id":17},"heading",2,"Requirements","requirements",{"type":19,"items":20},"list",[21,22],"Next.js 15 or newer, with React 19.","A [public key](\u002Fdocs\u002Fapi-keys) with your site in its allowed origins.",{"type":14,"level":15,"text":24,"id":25},"Install","install",{"type":27,"samples":28},"code",[29,33,36],{"label":30,"lang":31,"code":32},"npm","bash","npm install @fingerly\u002Fnext",{"label":34,"lang":31,"code":35},"pnpm","pnpm add @fingerly\u002Fnext",{"label":37,"lang":31,"code":38},"yarn","yarn add @fingerly\u002Fnext",{"type":14,"level":15,"text":40,"id":41},"Add the provider","add-the-provider",{"type":11,"text":43},"Read the key in your root layout, which stays a Server Component, and pass it to the provider.",{"type":27,"samples":45},[46],{"label":47,"lang":48,"code":49},"app\u002Flayout.tsx","tsx","import { FingerlyProvider } from '@fingerly\u002Fnext'\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    \u003Chtml lang=\"en\">\n      \u003Cbody>\n        \u003CFingerlyProvider apiKey={process.env.FINGERLY_PUBLIC_KEY}>{children}\u003C\u002FFingerlyProvider>\n      \u003C\u002Fbody>\n    \u003C\u002Fhtml>\n  )\n}",{"type":11,"text":51},"The provider also reads `NEXT_PUBLIC_FINGERLY_API_KEY`, `NEXT_PUBLIC_FINGERLY_ENDPOINT` and `NEXT_PUBLIC_FINGERLY_ENDPOINTS` (a JSON array) when a prop is not given. Passing the key as a prop is the arrangement that always works. With the Pages Router, render the same provider in `_app.tsx`.",{"type":14,"level":15,"text":53,"id":54},"Carry the request ID through a Server Action","carry-the-request-id-through-a-server-action",{"type":11,"text":56},"`FingerlyRequestId` renders a hidden input that fills itself once the identification finishes. `readRequestId` reads it back on the server.",{"type":27,"samples":58},[59,62],{"label":60,"lang":48,"code":61},"app\u002Fsignup\u002Fpage.tsx","import { FingerlyRequestId } from '@fingerly\u002Fnext'\nimport { signUp } from '.\u002Factions'\n\nexport default function Page() {\n  return (\n    \u003Cform action={signUp}>\n      \u003Cinput name=\"email\" type=\"email\" \u002F>\n      \u003CFingerlyRequestId \u002F>\n      \u003Cbutton>Create account\u003C\u002Fbutton>\n    \u003C\u002Fform>\n  )\n}",{"label":63,"lang":64,"code":65},"app\u002Fsignup\u002Factions.ts","ts","'use server'\nimport { readRequestId } from '@fingerly\u002Fnext\u002Fserver'\nimport { load } from '@fingerly\u002Fnode'\n\nconst fingerly = load({ secretKey: process.env.FINGERLY_SECRET_KEY! })\n\nexport async function signUp(formData: FormData) {\n  const requestId = readRequestId(formData)   \u002F\u002F null when the form carried none\n  if (!requestId) return { error: 'unverified' }\n\n  const event = await fingerly.events.get(requestId)\n  if (event.suspect_level === 'high') return { error: 'review' }\n  \u002F\u002F create the account\n}",{"type":19,"items":67},[68,69,70],"The field is named `fingerly_request_id` (`REQUEST_ID_FIELD`). Pass `name` to change it.","`readRequestId` returns `null` for a missing, empty or malformed value and never throws.","Several fields on one page still make one identification.",{"type":14,"level":15,"text":72,"id":73},"Hooks","hooks",{"type":11,"text":75},"`useIdentify`, `useVerdict` and `useFingerly` are re-exported from `@fingerly\u002Freact` and behave identically. See the [React SDK](\u002Fdocs\u002Fsdks\u002Freact#useidentify).",{"type":27,"samples":77},[78],{"label":79,"lang":48,"code":80},"app\u002Fcheckout\u002FCheckout.tsx","'use client'\nimport { useIdentify } from '@fingerly\u002Fnext'\n\nexport function Checkout() {\n  const { identify, isLoading } = useIdentify()\n  \u002F\u002F …\n}",{"type":14,"level":15,"text":82,"id":83},"Entry points","entry-points",{"type":85,"columns":86,"rows":90},"table",[87,88,89],"Import","Runs in","Contains",[91,95],[92,93,94],"`@fingerly\u002Fnext`","The browser","The provider, `FingerlyRequestId` and the hooks. A `'use client'` module.",[96,97,98],"`@fingerly\u002Fnext\u002Fserver`","The server","`readRequestId`, `REQUEST_ID_FIELD` and `createFingerlyProxy`. No React.",{"type":100,"tone":101,"text":102},"callout","warning","Import server helpers from `@fingerly\u002Fnext\u002Fserver` only. Importing them from the main entry inside a Server Action gives a client reference that fails at runtime.",{"type":14,"level":15,"text":104,"id":105},"Proxy through a route handler","proxy-through-a-route-handler",{"type":11,"text":107},"`createFingerlyProxy` is the [Node.js SDK's](\u002Fdocs\u002Fsdks\u002Fnode#serve-the-browser-sdk-from-your-domain) `createProxy`, re-exported. Mount it on a catch-all route and point the provider at it.",{"type":27,"samples":109},[110,113],{"label":111,"lang":64,"code":112},"app\u002Fmetrics\u002F[...path]\u002Froute.ts","import { createFingerlyProxy } from '@fingerly\u002Fnext\u002Fserver'\n\nexport const POST = createFingerlyProxy({\n  proxyKey: process.env.FINGERLY_PROXY_KEY!,\n  prefix: '\u002Fmetrics',\n  clientIp: (request) => request.headers.get('x-real-ip') ?? '',\n})",{"label":47,"lang":48,"code":114,"check":115},"\u003CFingerlyProvider apiKey={process.env.FINGERLY_PUBLIC_KEY} endpoints=\"\u002Fmetrics\">",false,{"type":11,"text":117},"Resolve the client address only from a header your hosting platform sets. See [proxy integrations](\u002Fdocs\u002Fproxy-integrations).",[119,120,121,122,123,124,125],{"id":17,"text":16,"level":15},{"id":25,"text":24,"level":15},{"id":41,"text":40,"level":15},{"id":54,"text":53,"level":15},{"id":73,"text":72,"level":15},{"id":83,"text":82,"level":15},{"id":105,"text":104,"level":15},"2026-09-17T16:59:11.000Z",1789667797791]