Step 4 of 4
IntegrationsLink the apps you already use to supercharge your workflow. You can always add more later.
1 connected
Onboarding screen with a grid of available integrations showing connection status and connect/skip actions. Perfect for apps that rely on third-party services like Slack, GitHub, and Google.
Onboarding screen that lists available third-party integrations as a responsive grid of rows, each showing an icon, name, and a Connect action (or a "Coming soon" label for services that aren't live yet). Connecting a service toggles local state and rolls into a running " connected" summary line above the CTAs.
Upgrade to Pro
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
Radix flavor
npx shadcn add "https://ui.beste.co/r/onboarding15?email=YOUR_EMAIL&license_key=YOUR_KEY"Base UI flavor
npx shadcn add "https://ui.beste.co/r-base/onboarding15?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the block to components/beste/block/onboarding15.tsx and the badge and button shadcn/ui primitives it depends on.
The installed file exports onboarding15Demo alongside the block: the exact props behind the preview above. Spread it to get a working integrations step in one line.
import { Onboarding15, onboarding15Demo } from "@/components/beste/block/onboarding15";
export default function OnboardingPage() {
return <Onboarding15 {...onboarding15Demo} />;
}Then replace the demo with your own props. Written out, a trimmed setup looks like this:
import { Calendar, Github, MessageSquare } from "lucide-react";
import { Onboarding15 } from "@/components/beste/block/onboarding15";
export default function OnboardingPage() {
return (
<Onboarding15
badge={{ label: "Integrations", variant: "secondary" }}
heading="Connect your tools"
description="Link the apps you already use to supercharge your workflow."
integrations={[
{ id: "slack", name: "Slack", icon: <MessageSquare className="size-5" />, status: "connected" },
{ id: "github", name: "GitHub", icon: <Github className="size-5" />, status: "available" },
{ id: "google", name: "Google Calendar", icon: <Calendar className="size-5" />, status: "available" },
]}
primaryButton={{ label: "Continue", href: "/onboarding/finish" }}
secondaryButton={{ label: "Skip for now", href: "/dashboard" }}
/>
);
}| Prop | Type | Default | Description |
|---|---|---|---|
badge | { label: string; variant?: "default" | "secondary" | "outline" } | – | Small badge above the heading |
heading | string | – | Main headline |
description | string | – | Supporting copy below the heading |
integrations | Integration[] | [] | Grid of connectable services |
primaryButton | { label: string; href: string } | – | Primary CTA link |
secondaryButton | { label: string; href: string } | – | Secondary CTA link |
labels | { stepOf?: string; connected?: string; connect?: string; comingSoon?: string; connectedCount?: string } | – | Template strings for the step counter, button states, and connected-count summary |
currentStep | number | 1 | Current step number, used only to fill the step-of label |
totalSteps | number | 4 | Total step count, used only to fill the step-of label |
className | string | – | Extra classes for the outer <section> |
type ConnectionStatus = "connected" | "available" | "coming-soon";
type Integration = {
id: string;
name: string;
icon: React.ReactNode;
status: ConnectionStatus;
};useState<Set<string>> is seeded once at mount from every integration whose status is "connected". Clicking Connect/Connected only toggles membership in that in-memory set, it never writes back to the integrations prop, so a page refresh resets anything the user toggled during the session.status: "coming-soon" render a plain "Coming soon" text label in place of the button and can't be toggled at all.connected.size is greater than zero; when nothing is connected, an empty spacer <div> fills the same slot so the layout height doesn't jump.integrations.length: 2 or fewer becomes a centered flex row, exactly 3 becomes a 3-column grid, and 4 or more falls back to a responsive sm:grid-cols-2 lg:grid-cols-3 grid.primaryButton and secondaryButton are always-enabled plain links; unlike some sibling onboarding blocks, this screen never disables Continue while integrations remain unconnected.onboarding21
Completion screen shown after finishing onboarding with a summary of what was configured and quick-start action links. Perfect for ending the setup flow with clear next steps.
onboarding24
Onboarding screen displaying essential keyboard shortcuts in a grouped grid layout with key badges. Perfect for productivity tools that want to accelerate user adoption.
onboarding37
A team setup section pairing a soft panel with a working invite row, a role picker, and a plain-language table of what each role can reach, with a bordered list of who is already on the account and whether they have accepted, closing on a seat meter that fills one tick per seat taken.
onboarding33
A setup panel pairing a soft progress column, where an oversized percentage and accent bar react to what is ticked, with hairline checklist rows that each carry a description and a jump-in link.