Migration Source Selector

Migration

Bring the last ten years with you

Whatever you are on today, the history comes across. We map the fields with you and reconcile the counts before anything goes live.

A calculator and paperwork on a desk

Where you are today

The most common starting point, and the easiest one. Export what you have, we handle the shape of it.

What comes across

  • Member details and contact history
  • Past and future appointments
  • Outstanding balances and open invoices
  • Whatever notes live in free-text columns

Typical timeline

2 days, including the review

Column names never match. That part is our job, not yours.

Plan your migration
About this block

Migration Source SelectorPRO

A migration section where your current system is picked from frosted glass cards floating on a photo panel, swapping the right side to a plain-language summary, a checked list of what carries across, the typical timeline, and a caveat.

Saas105: Migration Source Selector

Migration section where the system you are on today is picked from frosted glass cards floating on a photo panel, swapping the right side to a plain-language summary, a checked list of what carries across, the typical timeline, and the caveat that comes with it.

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.

Upgrade Now

Installation

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

bash
npx shadcn add "https://ui.beste.co/r/saas105?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

bash
npx shadcn add "https://ui.beste.co/r-base/saas105?email=YOUR_EMAIL&license_key=YOUR_KEY"

This installs the block to components/beste/block/saas105.tsx plus the badge23 and button21 components it uses for the eyebrow and the CTA.

Quick start

The installed file exports saas105Demo alongside the block: the exact props behind the preview above. Spread it to get a working selector in one line.

tsx
import { Saas105, saas105Demo } from "@/components/beste/block/saas105";

export default function MigrationPage() {
  return <Saas105 {...saas105Demo} />;
}

Then replace the demo with your own props. Written out, a trimmed setup looks like this:

tsx
import { Saas105 } from "@/components/beste/block/saas105";

export default function MigrationPage() {
  return (
    <Saas105
      badge={{ label: "Migration" }}
      heading="Bring the last ten years with you"
      description="Whatever you are on today, the history comes across."
      image={{ src: "/images/paperwork.jpg", alt: "Paperwork on a desk" }}
      labels={{
        sourcesLabel: "Where you are today",
        carriesLabel: "What comes across",
        timelineLabel: "Typical timeline",
      }}
      items={[
        {
          name: "Spreadsheets",
          summary: "The most common starting point, and the easiest one.",
          timeline: "2 days, including the review",
          carries: ["Member details and contact history", "Outstanding balances"],
          note: "Column names never match. That part is our job.",
        },
        {
          name: "Another practice system",
          summary: "If yours can export, it can move.",
          timeline: "3 to 5 days, run alongside your live system",
          carries: ["Member records with full history", "Invoice and payment ledger"],
        },
      ]}
      button={{ label: "Plan your migration", href: "/migration" }}
    />
  );
}

Props

PropTypeDefaultDescription
badge{ label: string }–Monospace eyebrow above the hairline rule, rendered via Badge23
headingstring–Section heading in the left column
descriptionstring–Supporting paragraph, right-aligned from md up
image{ src: string; alt: string }–Photo behind the source cards
labelsSaas105Labels{}Kicker and column headings, see below
itemsSource[][]Selectable source systems and what each one implies
button{ label: string; href: string }–CTA under the timeline column
classNamestring–Extra classes for the outer <section>
ts
type Source = {
  name: string;
  summary: string;
  timeline: string;
  carries?: string[];
  note?: string;
};

type Saas105Labels = {
  sourcesLabel?: string;
  carriesLabel?: string;
  timelineLabel?: string;
};

Behavior notes

  • The selection is one index in local state starting at 0, so the panel always opens on a real answer rather than an empty prompt.
  • The cards are frosted rather than bordered: backdrop-blur-md over a translucent fill, with the selected card simply carrying more of that fill. Nothing outlines, so the photo stays readable behind them.
  • Those glass classes are applied only when image is set. Without a photo the cards fall back to the flat muted fill the rest of the set uses, so the block never renders blur over a plain background.
  • The scrim is a gradient rather than a flat wash, which keeps the last card in a long list as legible as the first.
  • The photo panel is a fixed dark surface, so the kicker and the card labels use the fixed text-background tokens instead of the adaptive ones.
  • carries and note are both optional per source, so a system with nothing to warn about simply ends at its timeline, and the CTA stays where it is.
  • The panel splits at lg. Below that the photo becomes a band above the detail column, keeping the source cards first in reading order.

More SaaS blocks

View all SaaS
PRO

saas103

Onboarding Split

FREE

saas108

Branching Scoping Form

PRO

saas34

Press Coverage

Get Pro access