Getting started
A working Next.js integration in two files — the same one connect-example ships.
This walks through exactly what connect-example
— the reference Next.js integration — actually has in it. Nothing here is
simplified for the guide; if a step needs an argument, so does that app.
Prerequisites
You'll need an application in Connect with SSO enabled, and its Connect client section
open — that's where CONNECT_API_KEY and CONNECT_CLIENT_ID come from.
Install
npm install @cosmoops/connect-sdkOr scaffold straight into an existing Next.js App Router project:
npx @cosmoops/connect-sdk initThis writes the bridge route and a /plans page for you — steps 2 and 5
below. It won't touch your layout or stylesheet, since those already exist
in any real app.
Set your environment variables
CONNECT_API_KEY=sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
CONNECT_CLIENT_ID=cid_xxxxxxxxxxxxxxxxxxxxxxxxxxxxOne credential, three jobs
CONNECT_API_KEY is also your OAuth2 client_secret and the key your
id_token is signed with. Revoking it revokes all three at once.
Only these two — there's no third, browser-visible variable to keep in sync.
The default sign-in flow bounces the browser to your own /signin route,
which already knows client_id server-side; nothing needs it client-side too.
Two optional ones exist — CONNECT_APP_URL to talk to a non-production
Connect instance and CONNECT_SESSION_COOKIE to rename the session cookie.
Both are covered in Environments, along with what a
local setup needs that a deployed one doesn't.
Mount the bridge
export { GET, POST } from "@cosmoops/connect-sdk/next";One file, no arguments — credentials come from the environment you just set.
This single route answers /signin, /callback, and the
/session /entitlements /plans /addons /apps /sessions /logout
reads every component below calls. GET and POST are the same function;
it routes on method and path itself.
Wrap your app in the provider
import { connectProviderProps } from "@cosmoops/connect-sdk/next";
import { ConnectProvider } from "@cosmoops/connect-sdk/client";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<ConnectProvider {...connectProviderProps()}>{children}</ConnectProvider>
</body>
</html>
);
}One session read is shared by every Connect component below it, so a page with a user menu, an apps grid and a plan table makes one request, not three.
The spread is the only prop you need, and it is worth the second import: a root
layout is a Server Component, so connectProviderProps() reads
CONNECT_APP_URL there and hands the browser the same Connect origin your
server is using. Leave it off and the two halves of your integration can
address different instances — see
Environments. In
production it resolves to the public instance, so this line is correct
everywhere and you never edit it again.
Everything else is optional: basePath if you mounted the bridge somewhere
other than /api/connect, and clientId + redirectUri only if you build the
authorize URL in the browser yourself.
Add the theme
@import "tailwindcss";
@import "@heroui/styles";
@import "@cosmoops/connect-sdk/theme.css";Order matters: Tailwind first, HeroUI's token layer next, so the shared
theme's values win. It has to be imported from your own CSS file, not
from node_modules, or Tailwind roots its content scan inside
node_modules and skips your app entirely.
Register the redirect URI
Back in the application's Connect client section, add:
{your origin}/api/connect/callbackThis is the one step nothing here can do for you — it has to happen inside Connect itself. Connect matches a redirect URI exactly, so every origin you run from needs its own entry — localhost included. See Environments.
Drop in a pricing page
export { ConnectPlansPage as default } from "@cosmoops/connect-sdk/client";Readiness banner, current subscription, plan grid, and the full
subscribe / upgrade / downgrade / switch-cadence / cancel flow — one line.
Want your own layout instead? connect-example's /pricing page composes
the same flow by hand from groupPlansByTier, resolveAction,
ConnectSubscribeDialog and useConnectCheckout — read that when this
isn't enough.
That's the whole integration
Run it and you have the full round trip: sign-in, sign-up, session, plans,
subscribe, entitlements, sign-out. Nothing here is next.config.ts
boilerplate either — not even transpilePackages: dist/ already ships
ES2022 with "use client" hoisted, so Turbopack and webpack both consume it
as-is.
See it running
connect-example is
this exact integration, deployed — session handling, readiness gates, the
plan grid, every component from /client shown live with what it renders
when it doesn't apply. Clone it or read the source directly; it installs
@cosmoops/connect-sdk from npm like any other consumer, not from this
monorepo.
Server-side reads
Inside a Server Component, Route Handler, or Server Action, read the same
session with no argument and no headers() dance:
import { requireConnectSession } from "@cosmoops/connect-sdk/next";
export default async function DashboardPage() {
const session = await requireConnectSession(); // UserResponse — redirects if signed out
return <h1>{session.user.workspaceName}</h1>;
}requireConnectSession() is getConnectSession() plus the
if (!session) redirect(...) you'd otherwise write by hand — reach for the
plain getConnectSession() (returns UserResponse | null) instead when
"signed out" means something other than "redirect to sign-in." Both are
memoized per request, so calling one from a layout and again from the page
beneath it costs one round trip to Connect, not two — see
Authentication & authorization for the full shape.
It reads cookies() transitively, so under cacheComponents this belongs
inside a <Suspense> boundary like any other request-scoped read.
Next steps
- Authentication & authorization — the OAuth2
round trip this bridge runs for you,
proxy.tsroute protection, and role/entitlement-based access control. - Reference — every export across every entry point, generated from source.
- Gate a feature on entitlements —
connect.entitlements.getReadinessandblockingBlockers(isomorphic, safe to import in a Client Component too) are covered in the reference underentitlements. - Not on Next.js?
@cosmoops/connect-sdk/server'screateConnectHandlertakes an explicit session store instead of assuming cookies — see its reference page for the shape.