> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shieldlabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# React

> Identify a visitor, send a Request ID with your application action and verify the result on your server.

Identify a visitor, send a Request ID with your application action and verify the result on your server. This guide uses the supported ShieldLabs package API, not a standalone generated client.

## Before you start

An existing React 18 or 19 application and a Public Key. The examples use Vite and TypeScript.

Register and verify the website domain in your [analytics dashboard](https://app.shieldlabs.ai/). Use credentials from the same domain and environment as the browser check. For a fresh account, start with [Quick Start](/quickstart).

## Get your credentials

Copy the Public Key for your domain from **Integration > API keys**. A public frontend environment variable may contain this key, but never a Private API Key or webhook signing secret.

## Install the SDK

```bash theme={null}
npm install @shieldlabs-ai/react @shieldlabs-ai/js
```

Use `yarn add` or `pnpm add` with the same package names if your project uses that package manager. Framework packages remain the application's responsibility.

## Add the integration

The snippets use Vite with TypeScript; other bundlers expose environment variables their own way.
Put the Public Key of your domain in `.env` (the value below is a placeholder):

```bash theme={null}
# .env
VITE_SHIELDLABS_PUBLIC_KEY=0123456789abcdef0123456789abcdef
```

Render `ShieldLabsProvider` once, near the root of your app, around the components that identify:

```tsx theme={null}
// main.tsx
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { ShieldLabsProvider } from '@shieldlabs-ai/react';
import { SignupForm } from './SignupForm';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <ShieldLabsProvider publicKey={import.meta.env.VITE_SHIELDLABS_PUBLIC_KEY}>
      <SignupForm />
    </ShieldLabsProvider>
  </StrictMode>,
);
```

Run an identification when the user submits a protected action, and send the `requestId` with it:

```tsx theme={null}
// SignupForm.tsx
import type { SyntheticEvent } from 'react';
import { useIdentify } from '@shieldlabs-ai/react';

export function SignupForm() {
  const { identify, isLoading } = useIdentify();

  async function onSubmit(event: SyntheticEvent<HTMLFormElement>) {
    event.preventDefault();
    const email = new FormData(event.currentTarget).get('email');
    // null when there is no identification (the reason is in `error`). The signup goes out anyway,
    // and your server treats it as unverified.
    const result = await identify();
    await fetch('/api/signup', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, requestId: result?.requestId ?? null }),
    });
  }

  return (
    <form onSubmit={onSubmit}>
      <input name="email" type="email" required />
      <button disabled={isLoading}>Sign up</button>
    </form>
  );
}
```

`identify()` never rejects: it resolves the result, or `null` with the reason in `error`. A second
submit while the identification runs gets the same one, so a double click costs one identification.

On your server, read the verdict for `requestId` with a server SDK, for example
`identifications.get(requestId)` in [`@shieldlabs-ai/node`](https://github.com/ShieldLabs-ai/shieldlabs-node),
which waits until the identification has been scored. The History row appears about 1-3 seconds
after `identify()` resolves and can be refined for up to about 10 seconds as follow-up checks
finish, so starting the identification when the user begins the action (see
[Protect a form ](https://github.com/ShieldLabs-ai/shieldlabs-react/blob/09b09f731e569cf9885f28385e8296c206c697b9/README.md#protect-a-form)) gets your server the verdict sooner. Accept each request ID once
and only within your freshness window (the examples use 5 minutes): one identification authorizes
one protected action.

> **Keep the page alive after `identify()` resolves.** The agent posts the identification right
> after it hands over the request ID. Sending your request with `fetch()`, as above, keeps the page
> open. If you navigate right after the submit (a full-page form post or a redirect), start the
> identification early instead (see [Protect a form ](https://github.com/ShieldLabs-ai/shieldlabs-react/blob/09b09f731e569cf9885f28385e8296c206c697b9/README.md#protect-a-form)).

> **Test on a registered domain.** ShieldLabs records identifications only for the domains
> registered in your account. On `localhost` the page still receives a `requestId`, but the
> identification is rejected with `401` and your backend never finds it. Test on a development
> domain with its own keys, as described in [Environments](https://docs.shieldlabs.ai/setup/environments).

## Test the complete flow

1. Run the application on the registered HTTPS domain with its Public Key.
2. Trigger the protected form once and check that a Request ID is sent to your own backend.
3. Retrieve that same ID using a server SDK or locate it in the analytics dashboard.
4. Test a missing ID and an agent load failure: your backend must treat the action as unverified.

The `/api/signup` routes shown in examples belong to your application; they are not ShieldLabs API endpoints. Connect one of the [server quick starts](/api/sdks#server-packages) before testing the full action. A successful browser call does not prove scoring is complete.

## Troubleshooting

* No History row: confirm the registered domain, credential/environment match and that the browser remained open while collectors posted.
* Missing or pending verdict: scoring is asynchronous. The server helper waits within a bounded budget; handle a missing result and API errors explicitly.
* Authentication error: use the Private API Key for History, not a Public Key, Management Secret Key or MCP OAuth token.
* Invalid webhook signature: verify the original raw bytes with the endpoint's full signing secret, before trusting parsed JSON.

## Next steps

* [Runnable example](https://github.com/ShieldLabs-ai/shieldlabs-react/tree/09b09f731e569cf9885f28385e8296c206c697b9/examples/vite)
* [SDK reference and changelog](https://github.com/ShieldLabs-ai/shieldlabs-react)
* [Identification flow](/api/identification-flow)
* [Server API](/api/server-api)
* [Webhook setup](/setup/webhooks)
* [Content Security Policy](/setup/csp)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.