Logo

JavaScript SDK

Complete guide to using Quotient's JavaScript SDK

Quotient makes it easy to integrate our platform into your website or node.js application.

Packages

Quotient ships three packages depending on where you're integrating:

PackageEnvironmentAPI KeyExposes
@quotientjs/clientBrowserPublic (pk_)analytics, audience.people, blog, flow, store, auth
@quotientjs/reactBrowser (React)Public (pk_)Same as client, plus QuotientProvider, useQuotient, useBlogs
@quotientjs/serverNode.jsPrivate (sk_)analytics, audience (people, companies, lists), blog, flow, memory, auth

Client SDK (@quotientjs/client, @quotientjs/react) runs in the browser with a public API key. It tracks analytics, identifies users via audience.people.upsert(), renders blogs, and triggers flows. Public keys are origin-restricted so they're safe to ship in frontend code.

Server SDK (@quotientjs/server) runs in Node.js with a private API key. It has full access to the Quotient API — the complete audience API (people, companies, and lists), memory, and everything the client SDK can do. Use it for backend integrations, cron jobs, and data syncs.

Installation

npm / pnpm / yarn

# Client SDK (browsers)
npm install @quotientjs/client

# React bindings
npm install @quotientjs/react

# Server SDK (Node.js)
npm install @quotientjs/server

CDN

Drop a single script tag into any HTML page — no build step required:

<script src="https://unpkg.com/@quotientjs/client@0.4.10/dist/quotient.browser.js"></script>

<script>
  const client = await Quotient.QuotientClient.init({
    apiKey: "pk_your_public_api_key",
  });

  // Track a page view
  client.analytics.event({ eventType: "pageView" });
</script>

Quick Start

Vanilla JavaScript

import { QuotientClient } from "@quotientjs/client";

const quotient = await QuotientClient.init({
  apiKey: "pk_your_public_api_key",
});

// Track a page view
await quotient.analytics.event({ eventType: "pageView" });

// Identify a user from a form submission
await quotient.audience.people.upsert({
  emailAddress: "user@example.com",
  firstName: "Jane",
  lastName: "Doe",
});

React

import { QuotientProvider, useQuotient } from "@quotientjs/react";

function App() {
  return (
    <QuotientProvider
      clientOptions={{ apiKey: "pk_your_public_api_key" }}
      autoTrackPageViews={true}
    >
      <YourApp />
    </QuotientProvider>
  );
}

function ContactForm() {
  const { client } = useQuotient();

  const handleSubmit = () => {
    client?.audience.people.upsert({
      emailAddress: "user@example.com",
      firstName: "Jane",
      lastName: "Doe",
    });
  };

  return <button onClick={handleSubmit}>Submit</button>;
}

Server (Node.js)

import { QuotientServer } from "@quotientjs/server";

const quotient = new QuotientServer({
  privateKey: "sk_your_private_api_key",
});

// Manage lists, people, memory, and more
const { lists } = await quotient.audience.lists.list();

Customizing fetch behavior

Every server SDK method accepts an optional second argument, fetchOptions: RequestInit. Whatever you pass here is forwarded to the underlying fetch call, so you can tune how an individual request behaves — its caching, request cancellation, custom headers, and anything else fetch understands.

The most common reason to reach for this is caching in a Next.js app. By default the SDK requests cache: "no-store", which is the safe choice for personalized or frequently changing data. But if a route can tolerate slightly stale data — a sitemap, say, that only changes a few times a week — you can opt into Incremental Static Regeneration (ISR) instead of re-fetching on every request:

// Sitemap route — revalidate hourly instead of re-fetching on every request
const { blogs } = await quotient.blog.list(
  { statuses: ["PUBLISHED"], limit: 1000 },
  { cache: "force-cache", next: { revalidate: 3600 } },
);

Caching is just one example. Because fetchOptions is a standard RequestInit object, you can also pass an AbortSignal to cancel a request, set next: { tags } for tag-based revalidation, or attach your own request headers.

The SDK does keep control of the fields it needs to function, though. The HTTP method, the request body, and the SDK's own headers — Authorization, Content-Type, and the platform header — always take precedence over anything you pass. That means you can't accidentally break authentication or change a request's verb by supplying your own headers or method.

Next Steps