Skip to navigation

Components Quickstart

Four steps take you from an empty page to a live billing portal for the signed-in company. The same setup serves every drop-in component and every hook.

Using an agent? Paste this into Claude Code, Cursor, or Codex and it does the setup on this page:

Add Schematic billing to my app: docs.schematichq.com/components/skill.md

1. Install the packages

npm install @schematichq/schematic-react @schematichq/schematic-components @stripe/stripe-js @stripe/react-stripe-js

The React package carries the provider and the hooks. The components package carries the drop-ins and the derivations the reference code uses. The two Stripe packages are peers of the components; Checkout and Payment Methods load them when a payment form opens.

2. Mint access tokens on your server

Components read a company’s own billing state, so they authenticate with a short-lived, company-scoped temporary access token. Your server issues it with your secret key; the browser only ever sees the token. Put the endpoint behind your own auth and resolve the company from the signed-in session, never from the request.

// GET /api/accessToken
import { SchematicClient } from "@schematichq/schematic-typescript-node";
const client = new SchematicClient({ apiKey: process.env.SCHEMATIC_SECRET_KEY });
export async function GET() {
const session = await getSession(); // your own auth
if (!session?.companyId) return new Response("Unauthorized", { status: 401 });
const response = await client.accesstokens.issueTemporaryAccessToken({
lookup: { id: session.companyId }, // a company key configured in Schematic
});
return Response.json({
token: response.data.token,
expiresAt: response.data.expiredAt,
});
}

3. Wrap your app in the provider

// app/providers.tsx
"use client";
import { SchematicProvider } from "@schematichq/schematic-react";
async function fetchAccessToken() {
const response = await fetch("/api/accessToken");
return response.json(); // { token, expiresAt }
}
export function Providers({ children, company, user }) {
return (
<SchematicProvider
publishableKey={process.env.NEXT_PUBLIC_SCHEMATIC_PUBLISHABLE_KEY}
session={{ company: company.id, user: user.id, token: fetchAccessToken }}
>
{children}
</SchematicProvider>
);
}

session says whose billing is being read. Pass null when nobody is signed in and undefined while your auth is still resolving; Session and access tokens explains why the three differ. Leave user out when your tokens are company-wide.

4. Render the portal, or a hook

import {
CheckoutLauncherProvider, CreditUsage, IncludedFeatures, Invoices, MeteredFeatures,
PaymentMethods, PlanManager, SchematicStyles, UnsubscribeButton, UpcomingBill,
useCheckoutLauncher,
} from "@schematichq/schematic-components/elements";
export default function BillingPage() {
return (
<CheckoutLauncherProvider>
<SchematicStyles />
<Portal />
</CheckoutLauncherProvider>
);
}
function Portal() {
const { open } = useCheckoutLauncher();
return (
<>
<PlanManager onChangePlan={() => open()} />
<IncludedFeatures />
<MeteredFeatures onAddMore={() => open({ steps: { initial: "usage" } })} />
<CreditUsage onBuyMore={() => open({ steps: { initial: "credits" } })} />
<UpcomingBill />
<PaymentMethods />
<Invoices />
<UnsubscribeButton onManagePlan={() => open()} />
</>
);
}

Render <SchematicStyles /> once and drop any card you do not need. CheckoutLauncherProvider renders one <Checkout /> for the page, and the cards show their purchase actions only when you hand them open; the Drop-in components overview walks through the portal card by card.

The company endpoints answer 404 for an account that has not enabled them. If a correctly configured page shows a drop-in’s unavailable message, such as “Your plan is not available for this account.”, or a hook’s error is a 404 reading “Not found” (httpStatus(error) === 404), ask Schematic to enable company context (the company-context-api flag) for your account.

Where to go next