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 Developer → Client 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.