SDKsPreview · 3 min read

React SDK


Use @perkamo/react when a React app needs Perkamo customer state and event tracking without Next.js. It works with React 18.2+ and 19 in Vite, Remix, React Router and other setups.

Next.js apps should use the Next.js SDK instead — it ships the same hooks plus server route helpers.

Install

bash

npm install @perkamo/react

Token endpoint on your backend

Frontend code never holds a server API key. Your backend exposes a route that verifies the user's session and returns a short-lived client token. Create a signing key in the Perkamo console under Settings → Security → Signing keys, then self-sign tokens with any Perkamo server SDK:

ts

// Your backend route, e.g. POST /auth/perkamo/token
import { signClientToken } from "@perkamo/sdk";

app.post("/auth/perkamo/token", async (request, response) => {
const userId = await requireSessionUserId(request);
const token = await signClientToken({
kid: process.env.PERKAMO_SIGNING_KID!,
secret: process.env.PERKAMO_SIGNING_SECRET!, // backend-only
subject: userId,
scope: ["customer:read", "events:write"],
ttlSeconds: 600,
});
response.json({ token });
});

PHP backends can use ClientTokenSigner::sign(...) from the PHP SDK the same way.

Provider

Point the provider at your token endpoint. It is POSTed with credentials included and must return { "token": "..." }:

tsx

import { PerkamoProvider } from "@perkamo/react";

export function App() {
return (
<PerkamoProvider tokenEndpoint="/auth/perkamo/token">
<LoyaltyPanel />
</PerkamoProvider>
);
}

Alternatively pass a getToken function for full control, or a prebuilt client:

tsx

<PerkamoProvider getToken={async () => fetchMyPerkamoToken()}>
{children}
</PerkamoProvider>

Hooks

tsx

import { usePerkamoCustomerJson } from "@perkamo/react";

function LoyaltyPanel() {
const { customer, loading, error, refresh } = usePerkamoCustomerJson();

if (loading) return <p>Loading...</p>;
if (error) return <button onClick={refresh}>Retry</button>;

return <p>{customer?.wallets.points ?? 0} points</p>;
}
HookWhat it returns
usePerkamo()The underlying browser client for emit() and direct calls.
usePerkamoCustomer()Full customer profile with loading, error and refresh().
usePerkamoCustomerJson()JSON-safe customer view; traits omitted unless includeTraits: true.
usePerkamoCustomerStream()Live customer updates over SSE; needs stream-token wiring.

Emit events

tsx

import { usePerkamo } from "@perkamo/react";

function ReadTracker({ articleId }: { articleId: string }) {
const perkamo = usePerkamo();

return (
<button onClick={() => perkamo.emit("article.read", { article_id: articleId })}>
Mark as read
</button>
);
}

Client events are validated against the token's scope and optional event allow-list, and business-critical events such as purchases should stay on your backend with the JavaScript SDK.

Realtime streams

Add a streamTokenEndpoint (or getStreamToken) to enable usePerkamoCustomerStream():

tsx

<PerkamoProvider
tokenEndpoint="/auth/perkamo/token"
streamTokenEndpoint="/auth/perkamo/stream-token"
>
{children}
</PerkamoProvider>

See Realtime streaming for stream tokens and scopes.

Security boundary

  • Server API keys and signing secrets stay on your backend.
  • The provider accepts only token providers or endpoints; passing server

secrets to the browser client throws at runtime.

  • Helpers default to the hosted Perkamo API. Set baseUrl only when Perkamo

support supplies a different API URL.

integration.