Quickstart

Pluralize gives your single-player app multi-tenant auth, data, files and billing without a rewrite. This page takes you from an empty Next.js project to the running app below in about ten minutes.

yourapp.com

Recipes

ada@example.com
A
Ramen — 20 minjust now
Focaccia — 90 minjust now
Tacos al pastor — 45 minjust now
New recipe…
Add

1. Create a workspace

Sign up at pluralize.app/signup, create your first app, and copy the App ID (app_xxx) and publishable key (pk_live_xxx) from the dashboard. You'll need both; keep the publishable key out of server-only code because it's designed to ship to browsers.

2. Install the SDK

npm install @pluralize/sdk

The SDK works in browsers, Node.js, and React. A React-specific entry lives under @pluralize/sdk/react.

3. Configure environment variables

# .env.local
NEXT_PUBLIC_PLURALIZE_APP_ID=app_xxx
NEXT_PUBLIC_PLURALIZE_API_KEY=pk_live_xxx

NEXT_PUBLIC_ is required for client-side access in Next.js App Router. These values are not secrets — the key is scoped by appId and only works from the origins you allowlist in the dashboard.

4. Initialize the SDK once

Create a singleton you import from every component that talks to Pluralize. There's no Provider — just a plain module export:

// lib/pluralize.ts
import { Pluralize } from '@pluralize/sdk';
 
export const app = Pluralize.init({
  appId: process.env.NEXT_PUBLIC_PLURALIZE_APP_ID!,
  apiKey: process.env.NEXT_PUBLIC_PLURALIZE_API_KEY!,
});

app is a singleton: one HTTP client, one token store, one cross-tab BroadcastChannel. Initialize it once at the module level, never inside components.

5. Add a tiny useCurrentUser hook

The SDK exposes app.auth.onChange(cb) for session reactivity. A two-line hook wraps it so every component can read the current user:

// lib/use-current-user.ts
'use client';
import { useEffect, useState } from 'react';
import type { TenantInfo } from '@pluralize/sdk';
import { app } from '@/lib/pluralize';
 
export function useCurrentUser() {
  const [user, setUser] = useState<TenantInfo | null | undefined>(undefined);
  useEffect(() => app.auth.onChange(setUser), []);
  return { user, loading: user === undefined };
}

undefined means we haven't checked yet; null means signed out. That's what prevents the "logged-in flash" on first paint.

6. Ship a first page

The page below signs a visitor up, stores a recipe scoped to them, and lists what's there. Every user gets their own rows automatically — no tenant provisioning, no database migrations.

// app/page.tsx
'use client';
import { useEffect, useState } from 'react';
import { app } from '@/lib/pluralize';
import { useCurrentUser } from '@/lib/use-current-user';
import type { DataRecord } from '@pluralize/sdk';
 
export default function Home() {
  const { user, loading } = useCurrentUser();
  const [recipes, setRecipes] = useState<DataRecord[]>([]);
 
  useEffect(() => {
    if (!user) return;
    app.db.collection('recipes').find().then((res) => setRecipes(res.records));
  }, [user]);
 
  if (loading) return <p>Loading…</p>;
  if (!user) return <SignupButton />;
 
  return (
    <main className="mx-auto max-w-xl p-6">
      <h1 className="mb-4 text-xl font-semibold">Hi {user.email}</h1>
      <ul className="space-y-1">
        {recipes.map((r) => (
          <li key={r.id}>{(r.data as { title: string }).title}</li>
        ))}
      </ul>
    </main>
  );
}
 
function SignupButton() {
  return (
    <button
      onClick={() => app.auth.signup('ada@example.com', 'correct horse battery staple')}
      className="rounded-md bg-black px-4 py-2 text-white"
    >
      Sign up
    </button>
  );
}

That's every piece: a singleton, a one-line session hook, and a collection call. The same shape extends to files and billing.

Prefer drop-in components?

If you'd rather not write your own forms, the SDK ships pre-styled <PluralizeLogin>, <PluralizeSignup>, and <PluralPricing> — each takes the singleton app as a prop:

'use client';
import { PluralizeSignup } from '@pluralize/sdk/react';
import { app } from '@/lib/pluralize';
 
export default function SignupPage() {
  return <PluralizeSignup app={app} onSuccess={() => location.assign('/')} />;
}

Plain JavaScript (no React)

If you're not using React, the same singleton works directly — no hook needed:

import { app } from './lib/pluralize';
 
await app.auth.signup(email, password);
const { records } = await app.db.collection('recipes').find();

Next steps

  • Authentication — login forms, protected routes, session hooks.
  • Data — filters, sort, pagination, unique fields, and share links.
  • Files — per-tenant uploads with MIME and size guardrails.
  • Billing — Stripe checkout, the customer portal, and plan entitlements.