# Integration guides

> Add WhiskerJS to your page in the framework you use, then check its result on your server. Every code sample here was built and run.

Source: https://whiskerjs.com/guides/ (last updated 2026-10-26)

> **In plain words:**
>
> WhiskerJS runs in your web page and gives you one JSON object describing the browser and device. These guides show how to get that object at the right moment (when someone signs in, signs up or pays), send it to your server with the request you already make, and check on the server that it is fresh and was not changed on the way. What you then decide with it is up to your own rules.

## How the pieces fit

Every guide uses the same small contract between the page and your server, so you can mix any browser guide with any server guide.

```text
browser                                   your server
-------                                   -----------
GET /api/whisker-nonce            ->      make a nonce (128 random bits) and note the time,
                                  <-      store both for this session: { nonce, timestamp }
user presses "Sign in"
collect({ timeoutMs: 3000, binding })     (in the browser, 47 signals, no network)
POST /api/login { email, password, device }
                                  ->      verify the binding: nonce known, unused, recent,
                                          versions and component names as shipped, digest matches
                                  <-      then your own decision (password, rules, history)
```

| Endpoint | Method | Body | Answer |
| --- | --- | --- | --- |
| `/api/whisker-nonce` | GET | none | `{ "nonce": "<32 hex>", "timestamp": <ms> }` |
| `/api/login` (your existing form post) | POST | your fields plus `device`: the WhiskerJS result | your usual answer; a refused binding gives an error code such as `unknown_or_used_nonce` or `digest_mismatch` |

The names are examples: use your own routes. The only rule is that the nonce endpoint and the form post belong to the same session, so that a nonce issued to one visitor cannot be used by another.

## Browser side

- [Plain JavaScript](https://whiskerjs.com/guides/javascript/): The dist files as ES modules, or a bundler import.
- [React](https://whiskerjs.com/guides/react/): A login form component that collects on submit.
- [Next.js](https://whiskerjs.com/guides/nextjs/): A client component plus route handlers in the same app.
- [Vue](https://whiskerjs.com/guides/vue/): A single-file component with script setup.
- [Svelte](https://whiskerjs.com/guides/svelte/): A Svelte 5 component with runes.
- [Astro](https://whiskerjs.com/guides/astro/): A client script in an Astro page.

## Server side

- [Node](https://whiskerjs.com/guides/verify-node/): Uses the library's own bindingInput and COMPONENT_NAMES.
- [Go](https://whiskerjs.com/guides/verify-go/): Standard library only, numbers kept exact.
- [PHP](https://whiskerjs.com/guides/verify-php/): Core PHP, nonces kept in the session.
- [Python](https://whiskerjs.com/guides/verify-python/): Standard library only.

## Rules every guide follows

- **Collect at the moment you need it.** On submit of a login, sign-up or checkout form, not on every page view. `collect()` takes up to `timeoutMs` (the guides use 3000 ms) and briefly works on the main thread.
- **Only in the browser.** WhiskerJS reads the browser it runs in. In frameworks that render on the server (Next.js, Nuxt, SvelteKit, Astro), call it only from an event handler or an effect that runs in the browser, never while rendering.
- **A secure context.** Hashing uses Web Crypto, which browsers offer only on https pages (and on `http://localhost` while you develop). Without it `visitorId` and `structuredId` are `null` and large components report `unavailable` with ruleId `no_digest`.
- **`collect()` never throws and never rejects.** You do not need a try block around it. You do need one around your own `fetch` calls, which can fail like any request.
- **Send the result unchanged.** The binding digest covers every field, so do not trim or reorder it before it reaches the server check. It is about 4 KB of JSON.
- **The server decides.** WhiskerJS gives signals and integrity flags, not verdicts. A valid binding proves the result is fresh and unchanged since it was made, not that the browser told the truth. See [what never to trust from the client](https://whiskerjs.com/docs/integration/#what-never-to-trust-from-the-client).

## Install (all guides)

WhiskerJS is not on npm yet; the npm package is coming. Build it from source and install the tarball, or copy the built files:

```sh
cd whiskerjs                 # the WhiskerJS source folder
pnpm install && pnpm build   # writes dist/
pnpm pack                    # writes whiskerjs-0.1.0.tgz
cd ../my-app
npm install ../whiskerjs/whiskerjs-0.1.0.tgz
```

[Getting started](https://whiskerjs.com/docs/getting-started/) covers the build in more detail.

## How these guides were tested

Each code sample on these pages was first run on 2026-10-26 with WhiskerJS 0.1.0 (signalsVersion 5, idVersion 2), built from source:

- every browser sample was built with its framework, served next to the server sample from the matching guide, and driven by headless Chromium 140 (Playwright 1.55.0): it filled in the form, submitted it, and checked that the server accepted the binding and refused the same result when it was sent a second time;
- every server sample was also run against three real results made by headless Chromium, Firefox and WebKit, and refused a result with one changed component value, a reused nonce and a result older than 60 seconds;
- at signalsVersion 9 (commit 3618012), whose binding text is `whiskerjs/bind/2` with the four previous-id fields, the four server modules were updated and run again against a result made by the library: each accepted it and refused a reused nonce, a changed `previousVisitorId` and a result older than 60 seconds. The browser samples do not depend on the signalsVersion and were not run again.

Each page names the runtime versions it was tested with.

## Questions and answers

### Which guide do I need?

Usually two: one for the browser side (the framework your login or checkout page uses) and one for the server side (the language your API is written in). The two talk through two endpoints that every guide uses the same way.

### Can I use WhiskerJS without the anti-replay binding?

Yes. Leave out the binding option and the result has binding: null. You then lose the protection against a recorded result being sent again later, so the server guides recommend using it for logins, sign-ups and payments.

### Is WhiskerJS on npm?

Not yet; the npm package is coming. Until then you build it from source, run pnpm pack and install the tarball it writes, or serve the files in dist/ as ES modules.

## Related pages

- [Getting started](https://whiskerjs.com/docs/getting-started/): Build the library and collect a first result.
- [Integration reference](https://whiskerjs.com/docs/integration/): Sending results, storing ids, server checks.
- [Anti-replay binding](https://whiskerjs.com/docs/integration/#anti-replay-binding): The nonce, the timestamp and the digest in detail.
- [API reference](https://whiskerjs.com/docs/api/): Every export, option and type.
