React SDK
React hooks and components for integrating Quotient into React applications
Installation
npm install @quotientjs/react
QuotientProvider
Context provider that gives child components the Quotient client. Create the
client once with QuotientClient.init() in a file of its own, then pass it to
the provider. Creating it at the top of a module is safe even in apps that
render on the server: the client does nothing until it's used in the browser.
// lib/quotient.ts
import { QuotientClient } from "@quotientjs/react";
export const quotient = QuotientClient.init({
apiKey: "pk_your_public_api_key",
});
import { QuotientProvider } from "@quotientjs/react";
import { quotient } from "./lib/quotient";
function App() {
return <QuotientProvider client={quotient}>{children}</QuotientProvider>;
}
| Prop | Type | Required | Description |
|---|---|---|---|
client | QuotientClient | Yes | The client created with QuotientClient.init() |
Use the client in effects and event handlers, not while a component renders. On the server the client exists but can't do anything, so calling it during a server render throws.
Page views
The client tracks page views when it starts and on every navigation. Choose
which pages are tracked with the pageViews option of QuotientClient.init():
export const quotient = QuotientClient.init({
apiKey: "pk_your_public_api_key",
// Track every page except the logged-in app
pageViews: (url) => !url.pathname.startsWith("/app"),
});
Set pageViews: false if another integration already sends Quotient page views
from the same app.
Passing client options (deprecated)
clientOptions and autoTrackPageViews are deprecated. Create the client with
QuotientClient.init() and pass it as client instead.
useQuotient
Access the Quotient client and context within React components.
import { useQuotient } from "@quotientjs/react";
function MyComponent() {
const {
client, // The QuotientClient passed to the provider
error, // Error or null if initialization failed
reset, // () => void: Clear storage and start fresh
} = useQuotient();
}
| Field | Type | Description |
|---|---|---|
client | QuotientClient | null | The client passed to the provider. null only with the deprecated clientOptions, until the client is created |
error | Error | null | Set if initialization failed |
reset | () => void | Clear all stored data and give the browser a new device ID |
useBlogs
Fetch a paginated list of blogs within a client component. Must be inside a <QuotientProvider>.
| Param | Type | Required | Description |
|---|---|---|---|
page | number | No | Page number |
limit | number | No | Results per page |
statuses | BlogStatus[] | No | Filter by status |
"use client";
import { BlogStatus, useBlogs } from "@quotientjs/react";
export default function BlogList({
page,
limit,
statuses,
}: {
page: number;
limit: number;
statuses: BlogStatus[];
}) {
const { data, isLoading } = useBlogs({
page,
limit,
statuses,
});
const { blogs, pageData } = data;
return (
{/* Render blog list */}
);
}
Common Patterns
User Identification on Login
"use client";
import { useQuotient } from "@quotientjs/react";
function LoginHandler() {
const { client } = useQuotient();
const handleLogin = async (user) => {
await client?.audience.people.upsert({
emailAddress: user.email,
firstName: user.firstName,
lastName: user.lastName,
lists: ["customers"],
});
};
return <button onClick={() => handleLogin(user)}>Login</button>;
}
Marketing Preference Center
"use client";
import { useQuotient } from "@quotientjs/react";
import { useState } from "react";
function EmailPreferences({ userEmail }) {
const [state, setState] = useState("SUBSCRIBED");
const [lists, setLists] = useState(["newsletter", "promotions"]);
const { client } = useQuotient();
const updatePreferences = async () => {
try {
await client.audience.people.upsert({
emailAddress: userEmail,
emailSubscriptionStatus: state,
lists: state === "SUBSCRIBED" ? lists : [],
});
toast.success("Preferences updated");
} catch (error) {
toast.error("Failed to update preferences");
}
};
return (
<div>
<label>
<input
type="radio"
checked={state === "SUBSCRIBED"}
onChange={() => setState("SUBSCRIBED")}
/>
Subscribed - Receive marketing emails
</label>
<label>
<input
type="radio"
checked={state === "UNSUBSCRIBED"}
onChange={() => setState("UNSUBSCRIBED")}
/>
Unsubscribed - No marketing emails
</label>
{state === "SUBSCRIBED" && (
<div>
<h3>Email Lists:</h3>
<label>
<input
type="checkbox"
checked={lists.includes("newsletter")}
onChange={(e) => {
if (e.target.checked) {
setLists([...lists, "newsletter"]);
} else {
setLists(lists.filter((l) => l !== "newsletter"));
}
}}
/>
Weekly Newsletter
</label>
{/* More list options... */}
</div>
)}
<button onClick={updatePreferences}>Save Preferences</button>
</div>
);
}