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/reactToken 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>;
}| Hook | What 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
baseUrlonly when Perkamo
support supplies a different API URL.
- Read Security and keys before shipping a browser
integration.