# SaaS Starter

> One-file SaaS landing composition. Editable CONTENT — compose sections after install.

A shadcn/ui block from the [@atroui](https://www.atroui.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/atroui/saas-starter).

- Page, with a live preview: https://shadcn.exchange/r/atroui/saas-starter
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/atroui/saas-starter-light-2dc3c7e9d8018a92.png
- Registry item: https://www.atroui.com/r/saas-starter.json
- Listed: 2026-10-25, source last changed 2026-10-25

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/atroui/saas-starter.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `lucide-react`, `next`

## Files

### `registry/default/blocks/saas-starter.tsx`

```tsx
import { ArrowRight } from "lucide-react"
import Link from "next/link"

/**
 * SaasStarter — one-file SaaS landing composite.
 * Edit CONTENT below to match your product. No sibling imports;
 * after `npx shadcn add @atroui/saas-starter` this file lives in your repo.
 * Token-aware (role tokens only), soft-rect chrome, reduced-motion safe
 * (single pulse dot guarded with motion-reduce:animate-none).
 */
const CONTENT = {
  announcement: "v2.0 is live — AI summaries included on every plan",
  announcementHref: "/updates",
  stamp: "Acme · SaaS starter",
  headlineBefore: "Ship your SaaS",
  headlineAccent: "this week",
  headlineAfter: ".",
  subhead:
    "One file, every section: hero, logos, features, stats, pricing teaser, and CTA. Edit CONTENT and deploy.",
  primaryCta: { label: "Start free trial", href: "/contact" },
  secondaryCta: { label: "View pricing", href: "#saas-starter-pricing" },
  logos: ["Northline", "Cascade", "Harbor", "Kiln", "Fieldwork", "Orbit"],
  features: [
    {
      title: "Auth in minutes",
      description: "Email + OAuth flows with guarded routes out of the box.",
    },
    {
      title: "Billing built in",
      description: "Plans, trials, and webhooks wired to your provider.",
    },
    {
      title: "AI summaries",
      description: "Stream per-workspace digests on your own API keys.",
    },
    {
      title: "Deploy anywhere",
      description: "Static-first pages with zero-config hosting handoff.",
    },
  ],
  stats: [
    { value: "7 days", label: "Median time to launch" },
    { value: "99.9%", label: "Uptime across starter deploys" },
    { value: "$0", label: "Extra UI spend — you own the files" },
  ],
  pricing: {
    name: "Starter",
    price: "$49",
    per: "/mo",
    note: "Cancel anytime. Fixed scope, no hourly theater.",
    cta: { label: "Choose Starter", href: "/contact?plan=starter" },
  },
  ctaBand: {
    title: "Launch before Friday.",
    body: "Copy this file, edit CONTENT, and hand the link to your team.",
    cta: { label: "Get the starter", href: "/contact" },
  },
  footerNote: "MIT — you own every file this block copies into your repo.",
}

export function SaasStarter() {
  return (
    <section aria-label="SaaS starter" className="@container w-full bg-background text-foreground">
      {/* Hero — announcement lives below the fold as gradual revelation */}
      <div className="border-y border-border-subtle">
        <div className="mx-auto flex max-w-7xl flex-col items-center space-y-5 border-x border-border-subtle px-6 py-12 text-center @min-[40rem]:px-10 @min-[40rem]:py-14">
          <p className="atro-chip">
            <span className="size-1.5 rounded-full bg-brand" aria-hidden />
            {CONTENT.stamp}
          </p>
          <h2 className="max-w-3xl text-4xl leading-[1.05] font-medium tracking-tight text-balance @min-[40rem]:text-5xl">
            {CONTENT.headlineBefore}{" "}
            <span className="italic text-brand">{CONTENT.headlineAccent}</span>
            {CONTENT.headlineAfter}
          </h2>
          <p className="max-w-xl text-[15px] leading-relaxed text-muted-foreground @min-[40rem]:text-base">
            {CONTENT.subhead}
          </p>
          <div className="flex w-full max-w-xl flex-col items-stretch gap-3 @min-[40rem]:max-w-none @min-[40rem]:flex-row @min-[40rem]:items-center @min-[40rem]:justify-center">
            <Link href={CONTENT.primaryCta.href} className="atro-btn w-full justify-center @min-[40rem]:w-auto">
              {CONTENT.primaryCta.label}
              <ArrowRight className="size-4" aria-hidden />
            </Link>
            <Link href={CONTENT.secondaryCta.href} className="atro-btn-ghost w-full justify-center @min-[40rem]:w-auto">
              {CONTENT.secondaryCta.label}
            </Link>
          </div>
        </div>
      </div>

      {/* Announcement — after hero (gradual revelation) */}
      <div className="border-b border-border-subtle">
        <div className="mx-auto flex max-w-7xl items-center justify-center gap-2 border-x border-border-subtle px-6 py-2.5 text-center @min-[40rem]:px-10">
          <span className="size-1.5 shrink-0 animate-pulse rounded-full bg-brand motion-reduce:animate-none" aria-hidden />
          <p className="truncate text-xs text-muted-foreground">
            {CONTENT.announcement}{" "}
            <Link href={CONTENT.announcementHref} className="font-medium text-brand underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring">
              See what&apos;s new
            </Link>
          </p>
        </div>
      </div>

      {/* Logo row */}
      <div className="border-b border-border-subtle">
        <div className="mx-auto max-w-7xl border-x border-border-subtle px-6 py-8 @min-[40rem]:px-10">
          <ul aria-label="Trusted by" className="grid grid-cols-2 gap-px overflow-hidden rounded-[var(--atro-panel-radius,var(--radius))] border border-border-subtle bg-border-subtle @min-[40rem]:grid-cols-3 @min-[64rem]:grid-cols-6">
            {CONTENT.logos.map((name) => (
              <li key={name} className="flex min-h-14 items-center justify-center bg-background px-3 py-3">
                <span className="text-sm font-medium tracking-tight text-muted-foreground">{name}</span>
              </li>
            ))}
          </ul>
        </div>
      </div>

      {/* Feature grid */}
      <div className="border-b border-border-subtle">
        <div className="mx-auto grid max-w-7xl grid-cols-1 gap-3 border-x border-border-subtle px-6 py-10 @min-[40rem]:grid-cols-2 @min-[40rem]:px-10 @min-[64rem]:grid-cols-4">
          {CONTENT.features.map((f) => (
            <div key={f.title} className="group relative flex flex-col overflow-hidden rounded-[var(--atro-panel-radius,var(--radius))] border border-border-subtle bg-card p-5">
              <h3 className="text-[0.9375rem] font-medium tracking-tight text-foreground">{f.title}</h3>
              <p className="mt-1.5 text-sm leading-relaxed text-muted-foreground">{f.description}</p>
              <div aria-hidden className="pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-[color-mix(in_oklch,var(--brand)_40%,transparent)] to-transparent opacity-0 transition-opacity duration-200 group-hover:opacity-100" />
            </div>
          ))}
        </div>
      </div>

      {/* Stats */}
      <div className="border-b border-border-subtle">
        <dl className="mx-auto grid max-w-7xl grid-cols-1 divide-y divide-border-subtle border-x border-border-subtle @min-[40rem]:grid-cols-3 @min-[40rem]:divide-x @min-[40rem]:divide-y-0">
          {CONTENT.stats.map((s) => (
            <div key={s.label} className="px-6 py-6 @min-[40rem]:px-8">
              <dd className="text-2xl font-medium tracking-tight tabular-nums text-foreground @min-[40rem]:text-3xl">{s.value}</dd>
              <dt className="mt-1 text-xs text-muted-foreground">{s.label}</dt>
            </div>
          ))}
        </dl>
      </div>

      {/* Pricing teaser */}
      <div id="saas-starter-pricing" className="border-b border-border-subtle scroll-mt-16">
        <div className="mx-auto flex max-w-7xl flex-col gap-4 border-x border-border-subtle px-6 py-10 @min-[40rem]:flex-row @min-[40rem]:items-center @min-[40rem]:justify-between @min-[40rem]:px-10">
          <div>
            <p className="text-[11px] font-semibold tracking-[0.14em] text-muted-foreground uppercase">Pricing teaser</p>
            <p className="mt-2 text-xl font-medium text-foreground">
              {CONTENT.pricing.name} — {CONTENT.pricing.price}
              <span className="text-sm font-normal text-muted-foreground">{CONTENT.pricing.per}</span>
            </p>
            <p className="mt-1 text-sm text-muted-foreground">{CONTENT.pricing.note}</p>
          </div>
          <Link href={CONTENT.pricing.cta.href} className="atro-btn w-full justify-center @min-[40rem]:w-auto">
            {CONTENT.pricing.cta.label}
            <ArrowRight className="size-4" aria-hidden />
          </Link>
        </div>
      </div>

      {/* CTA band */}
      <div className="border-b border-border-subtle">
        <div className="mx-auto max-w-7xl border-x border-border-subtle px-6 py-10 @min-[40rem]:px-10">
          <div className="flex flex-col gap-4 rounded-[var(--atro-panel-radius,var(--radius))] border border-border-subtle bg-card p-6 @min-[40rem]:flex-row @min-[40rem]:items-center @min-[40rem]:justify-between @min-[40rem]:p-8">
            <div>
              <h3 className="text-xl font-medium tracking-tight text-foreground @min-[40rem]:text-2xl">{CONTENT.ctaBand.title}</h3>
              <p className="mt-1.5 text-sm leading-relaxed text-muted-foreground">{CONTENT.ctaBand.body}</p>
            </div>
            <Link href={CONTENT.ctaBand.cta.href} className="atro-btn shrink-0">
              {CONTENT.ctaBand.cta.label}
              <ArrowRight className="size-4" aria-hidden />
            </Link>
          </div>
        </div>
      </div>

      {/* Footer note */}
      <div>
        <div className="mx-auto max-w-7xl border-x border-border-subtle px-6 py-4 @min-[40rem]:px-10">
          <p className="text-center text-xs text-muted-foreground">{CONTENT.footerNote}</p>
        </div>
      </div>
    </section>
  )
}
```
