All articles Integration

Add Device Fingerprinting to a React App

Adding device intelligence to a React app is mostly about doing three things in the right order: load the agent without blocking your UI, get a stable visitor ID at the moments that matter, and verify that ID on your server before you trust it. Get the order wrong and you either slow down first paint or, worse, make security decisions on a value the client can fake.

This guide walks through a clean integration with a stable visitor ID and a confidence score, then shows how to feed the signals into a login and checkout flow. The patterns apply to any modern React setup; where Next.js differs, we note it and point to the Next.js integration guide.

Load the agent once, expose it through context

The agent should initialize a single time for the whole app, not per component. A small provider makes the result available anywhere via a hook and keeps identification off the render path.

import { createContext, useContext, useEffect, useState } from 'react';
import Prynt from '@prynt/browser';

const FpContext = createContext(null);

export function FingerprintProvider({ children }) {
  const [agent, setAgent] = useState(null);

  useEffect(() => {
    let active = true;
    Prynt.load({ endpoint: import.meta.env.VITE_PRYNT_URL })
      .then((a) => { if (active) setAgent(a); });
    return () => { active = false; };
  }, []);

  return <FpContext.Provider value={agent}>{children}</FpContext.Provider>;
}

export const useFingerprintAgent = () => useContext(FpContext);

Wrap your app once. Because it lives in useEffect, it runs after mount and never blocks first paint. Self-hosting Prynt means endpoint points at your own first-party origin, which sidesteps ad blockers and keeps data on your infrastructure; see first-party agent serving for why that matters.

Identify at the moments that matter

Do not fingerprint on every render. Request identification at decision points, login submit, signup, checkout, so you spend the work where a signal changes an outcome.

import { useFingerprintAgent } from './FingerprintProvider';

export function useIdentify() {
  const agent = useFingerprintAgent();
  return async () => {
    if (!agent) return null;
    const { visitorId, confidence, requestId } = await agent.get();
    return { visitorId, confidence, requestId };
  };
}

Two return values drive everything downstream. The visitorId is the stable device fingerprint that persists across cookie clears and incognito. The confidence is how sure the match is; a confidence score below your threshold means treat the identification as uncertain rather than authoritative. The requestId is the handle your server uses to fetch the sealed, verified result.

Verify on the server, always

This is the rule that separates a real integration from a toy: the browser value is a hint, never a decision. Anything client-side can be replayed or forged, so your server re-fetches the result from the API using the requestId and makes the call from that verified copy. This mirrors the Node server-side verification pattern.

// Express handler
app.post('/api/login', async (req, res) => {
  const { requestId, email } = req.body;

  const result = await fetch(`${PRYNT_API}/events/${requestId}`, {
    headers: { 'Auth-API-Key': process.env.PRYNT_SECRET },
  }).then((r) => r.json());

  const { visitorId, confidence } = result.products.identification.data;
  const bot = result.products.botd?.data?.bot?.result;

  if (confidence < 0.8) return stepUp(res);        // uncertain device
  if (bot === 'bad') return res.status(403).end();  // automation
  return continueLogin(res, { email, visitorId });
});

The requestId is single-use and time-bound, so an attacker cannot capture one and replay it later. Everything you act on, the visitor ID, the confidence, the bot signal, comes from the server-fetched result, not from the request body.

Wire signals into real flows

With a verified visitor ID and confidence in hand, the integration becomes a set of small policy decisions rather than a big rewrite.

A compact policy table keeps the React side honest about what each decision needs:

FlowTrust thresholdAction on fail
Loginconfidence >= 0.8, no bot flaglight step-up
Signupdevice seen < N accountsmanual review
Checkouttrusted device history3-D Secure or hold

Common mistakes to avoid

A few pitfalls turn a good integration into a fragile one.

  • Blocking render on identification — never await the agent before first paint. Load async, identify on interaction.
  • Deciding client-side — a gate written purely in React is bypassed with the console open. Decide on the server.
  • Ignoring confidence — treating a 0.5 match as certain manufactures false positives. Thread confidence through every decision.
  • Fingerprinting on mount everywhere — wasteful and noisy. Identify at decision points only.
  • Forgetting consent and privacy — collect what you need, document it, and follow GDPR guidance for your jurisdiction.

Frequently asked questions

Can I trust the visitor ID returned in the browser?

Never for a security decision. Always re-verify the ID server-side against the vendor API, because anything the client sends can be forged; the browser value is only useful for UX hints.

Where should I call the fingerprinting SDK in a React app?

Load the agent once at app startup and expose the result through context or a hook, then request a fresh identification at the specific moments that matter, such as login submit or checkout.

Does fingerprinting hurt React performance?

The agent is small and runs asynchronously, so identify off the render path and after interaction rather than blocking first paint, and cache the result for the session.

A clean React integration is small: one provider, one hook, and a server route that verifies before it trusts. From there, the visitor ID and confidence score become inputs to whatever fraud logic your product needs, from login step-up to checkout review. Browse the SDKs, try the playground, and read the docs to go from this skeleton to production.

Run it yourself

Prynt is open-source, self-hostable device intelligence — visitor IDs, bot & fraud Smart Signals, and behavioral biometrics you own end to end.

Keep reading