Connect Interstitials
Shared layout guidance for focused authorisation, invite, marketplace, CLI, and credit redemption flows.
Connect interstitials are focused, single-card flows that sit outside the main
Studio shell. Use the shared InterstitialLayout family instead of building
bespoke centered cards, logos, account rows, or organisation selectors.
import { Button } from 'ui'
import {
AccountRow,
InterstitialShell,
LogoPair,
SignOutButton,
StripeLogo,
SupabaseLogo,
} from './connect-interstitial-shared'
export function ConnectInterstitialDemo() {
return (
<InterstitialShell
logo={<LogoPair left={<StripeLogo />} right={<SupabaseLogo />} />}
title="Authorize Stripe Projects"
description="This will create an organization on your behalf in Supabase"
>
<div className="flex flex-col gap-4">
<AccountRow displayName="alex@example.com" action={<SignOutButton />} />
<Button variant="primary" block>
Authorize Stripe Projects
</Button>
<Button variant="text" block>
Cancel
</Button>
</div>
</InterstitialShell>
)
}Use this pattern for
This pattern fits short-lived connect flows: partner authorisation and consent (OAuth, MCP, Stripe Projects), organisation invites, marketplace and billing connections (AWS Marketplace, Vercel install, credit redemption), and CLI or device-code sign-in. Use the same shell for their loading, error, success, and wrong-account states.
Do not use it for normal authenticated Studio pages. Those should use the standard page layout patterns.
Source of truth
import { OrganizationSelector } from '@/components/interfaces/Connect/OrganizationSelector'
import {
InterstitialAccountRow,
InterstitialLayout,
LogoBox,
LogoPair,
PartnerLogo,
SupabaseLogo,
} from '@/components/layouts/InterstitialLayout'Basic shape
Use InterstitialLayout for the outer card, then put route-specific content in
px-6 pb-6. Widen the card only when the flow embeds a real tool, such as
project linking.
<InterstitialLayout
logo={
<LogoPair
left={<PartnerLogo src={`${BASE_PATH}/img/icons/stripe-icon.svg`} alt="Stripe" />}
right={<SupabaseLogo />}
/>
}
title="Authorize Stripe Projects"
description="This will create an organization on your behalf in Supabase"
>
<div className="px-6 pb-6">
<InterstitialAccountRow displayName={displayName} />
<Button variant="primary" block>
Continue
</Button>
</div>
</InterstitialLayout><InterstitialLayout
logo={<LogoPair left={<VercelLogo />} right={<SupabaseLogo />} />}
title="Connect Vercel project"
containerClassName="items-start"
cardClassName="max-w-[900px]"
>
<div className="px-6 pb-6">{projectLinker}</div>
</InterstitialLayout>Logos
Use LogoPair when the user is connecting two services, and SupabaseLogo
alone for first-party flows. PartnerLogo fills the 48px box edge-to-edge;
LogoBox is for custom inset marks or logos that need their own background.
Store new partner icons in apps/studio/public/img/icons.
import { Button } from 'ui'
import {
AccountRow,
InterstitialShell,
LogoPair,
SignOutButton,
StripeLogo,
SupabaseLogo,
} from './connect-interstitial-shared'
export function ConnectInterstitialLogoPair() {
return (
<InterstitialShell
logo={<LogoPair left={<StripeLogo />} right={<SupabaseLogo />} />}
title="Authorize Stripe Projects"
description="This will create an organization on your behalf in Supabase"
>
<div className="flex flex-col gap-4">
<AccountRow displayName="alex@example.com" action={<SignOutButton />} />
<Button variant="primary" block>
Authorize Stripe Projects
</Button>
</div>
</InterstitialShell>
)
}import { Button } from 'ui'
import { Admonition } from 'ui-patterns/admonition'
import { AccountRow, InterstitialShell, SupabaseLogo } from './connect-interstitial-shared'
export function ConnectInterstitialLogoSingle() {
return (
<InterstitialShell
logo={<SupabaseLogo />}
title="Join organization"
description="You have been invited to Acme Labs"
>
<div className="flex flex-col gap-4">
<Admonition
type="warning"
title="Wrong account"
description="Sign in with the Supabase account that received this invite, then open the link again."
/>
<AccountRow displayName="alex@example.com" />
<Button variant="primary" block>
Sign out and continue
</Button>
</div>
</InterstitialShell>
)
}const AwsLogo = () => (
<LogoBox className="border-[#232f3e] bg-[#232f3e]">
<img alt="AWS" src={`${BASE_PATH}/img/icons/aws-icon.svg`} className="w-8" />
</LogoBox>
)
<LogoPair left={<AwsLogo />} right={<SupabaseLogo />} />Account row
Use InterstitialAccountRow for signed-in context. Do not recreate it locally.
<InterstitialAccountRow avatarUrl={avatarUrl} displayName={displayName} action={signOutButton} />Organisation selection
Use OrganizationSelector when the flow needs an organisation pick. Extend it
for new states instead of inventing a parallel card style.
<OrganizationSelector
organizations={linkableOrganizations}
selectedSlug={selectedOrgSlug}
onSelect={setSelectedOrgSlug}
createLabel="Create new organization"
onCreate={() => setShowOrgCreationDialog(true)}
/>Actions
Prefer one full-width primary action. A full-width text button is fine for a secondary action that still belongs in the flow.
States
Keep loading, invalid, error, and success states inside the same card when the
route can explain them. Use ShimmeringLoader for loading, and Admonition
for warning, error, note, and success copy.
Copy
Use sentence case. Prefer sign in over login. Titles and primary actions
should follow Verb -> Thing, for example Authorize Stripe Projects or
Install Vercel.
Keep the layout title static across states and put state-specific copy in the body. Header descriptions should stay short and should not end with a full stop.