SDKs

React

A provider and two hooks for React 19. One shared identification per page, server-rendering safe, with the result as plain state.

@fingerly/react wraps the JavaScript SDK in a provider and hooks. However many components ask, a page makes one identification and every component reads the same answer.

Requirements

  • React 19 or newer.
  • A public key with your site in its allowed origins.

Install

npm install @fingerly/react

Add the provider

main.tsx
import { createRoot } from 'react-dom/client'
import { FingerlyProvider } from '@fingerly/react'

createRoot(document.getElementById('root')!).render(
  <FingerlyProvider apiKey="fly_pk_us_production_…">
    <App />
  </FingerlyProvider>,
)

The provider collects nothing when it mounts. It accepts apiKey, endpoint, endpoints, fallbackToDefaultEndpoint, submit and consent, with the meanings the JavaScript agent gives them. Changing consent takes effect without remounting, and setConsent() is also on the store from useFingerly().

Identify on an action

Checkout.tsx
import { useIdentify } from '@fingerly/react'

export function Checkout({ orderId }: { orderId: string }) {
  const { identify, isLoading, error } = useIdentify({ tag: () => 'checkout:' + orderId })

  async function onSubmit() {
    const { requestId } = await identify()
    await submitOrder({ orderId, requestId })
  }

  return (
    <>
      <button onClick={onSubmit} disabled={isLoading}>
        {isLoading ? 'Checking…' : 'Pay now'}
      </button>
      {error && <p>{error.message}</p>}
    </>
  )
}

Identify on mount

RiskBanner.tsx
const { suspectLevel, isReady } = useIdentify({ immediate: true })

immediate runs in an effect, so it never runs during server rendering. suspend: true suspends the component instead, for use inside <Suspense>.

useIdentify

Options

  • immediatebooleanDefault false
    Identify when the component mounts.
  • suspendbooleanDefault false
    Suspend during render until the identification finishes.
  • tagstring | () => string | undefined
    The tag to send. A function is read at the moment of submission. A tag passed to identify() overrides it.

Returns

  • requestIdstring | null
    Identifies this identification. Send it to your server with the action it protects.
  • visitorIdstring | null
    The stable identifier the server resolved for this browser.
  • visitorIsNewboolean
    Whether your organization is seeing this visitor for the first time.
  • visitorConfidencenumber
    How sure the identification is, from 0 to 100.
  • identifiableboolean
    false when the browser gave too little to identify anyone.
  • duplicateboolean
    true when the server had already answered this request.
  • statestring | null
    enriched, or unavailable when the network lookup could not run.
  • suspectScorenumber | null
    The server's weighted score. null when nothing was scored, which is not the same as 0.
  • suspectLevelstring | null
    low, medium or high.
  • triggersIdentifyTrigger[]
    The signals the server scored, heaviest first.
  • verdictsVerdicts | null
    The local, advisory client-side verdicts.
  • errorError | null
    The last failure, if the identification failed.
  • isLoadingboolean
    An identification is in flight.
  • isReadyboolean
    The identification has finished, successfully or not.
  • isSuspiciousboolean
    Any local verdict matched at medium confidence or above.
  • identify(options?: { tag?: string; force?: boolean }) => Promise<IdentifyResult>
    Identify, or return the page's existing identification. force collects afresh.
  • refresh(options?: { tag?: string }) => Promise<IdentifyResult>
    Identify again, ignoring the shared result.

identify and refresh are stable across renders. A failed identification is forgotten, so the next call tries again.

useVerdict

Reads one local verdict, with a confidence floor. It never triggers identification itself.

AutomationNotice.tsx
import { useVerdict } from '@fingerly/react'

function AutomationNotice() {
  const { matched, confidence } = useVerdict('automation', { min: 'high' })
  if (!matched) return null
  return <aside>Automated access suspected ({confidence})</aside>
}

Returns matched, verdict, confidence and reasons. min defaults to 'medium'. Verdict names are incognito, shields, tor, emulator, automation and farm.

Server rendering

  • On the server the hooks return the unidentified state, so hydration always matches.
  • Calling identify() during server rendering rejects with FingerlyServerError. Call it from an effect or an event handler.
  • For a server that renders many requests, create a store per request with createFingerlyStore(options) and pass it as <FingerlyProvider store={store}>.

Errors

Failures are stored in error and rethrown from identify(). TransportError is re-exported with its status and retryable fields. Using a hook outside a provider throws an error that says where to add one.