# Auth Split

> Split login/signup shell with editable CONTENT. No auth backend.

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/auth-split).

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

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/atroui/auth-split.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/auth-split.tsx`

```tsx
"use client"

import Link from "next/link"
import { ArrowRight, Lock } from "lucide-react"

/**
 * Edit CONTENT to match your auth page. Split layout — sign-in panel on
 * the left, product preview on the right. DashboardShell is exported from
 * this same file so the block stays self-contained.
 * After install this file lives in your repo.
 */
const CONTENT = {
  stamp: "Welcome back",
  headlineBefore: "Pick up",
  headlineAccent: "where you left off",
  headlineAfter: ".",
  lede: "Sign in to your workspace. New here? Create an account instead.",
  emailLabel: "Work email",
  emailPlaceholder: "you@company.com",
  passwordLabel: "Password",
  submitLabel: "Sign in",
  switchNote: "New to the product?",
  switchCta: { label: "Create an account", href: "/signup" },
  shellTitle: "Workspace overview",
  shellCaption: "Live preview — replace with your own dashboard.",
  metrics: [
    { label: "Active users", value: "8,412" },
    { label: "Conversion", value: "4.2%" },
    { label: "MRR", value: "$96k" },
  ],
  rows: [
    { name: "Acme launch", status: "Live", progress: "100%" },
    { name: "Billing portal", status: "In review", progress: "80%" },
    { name: "Mobile PWA", status: "Building", progress: "45%" },
  ],
}

/** Placeholder product preview — swap for your real dashboard. */
export function DashboardShell() {
  return (
    <div className="@container overflow-hidden rounded-[var(--atro-panel-radius,var(--radius))] border border-border-subtle bg-background">
      <div className="flex items-center gap-3 border-b border-border-subtle bg-muted/70 px-3 py-2.5 @min-[40rem]:px-4">
        <div className="flex gap-1.5" aria-hidden>
          <span className="size-2.5 rounded-full bg-[#FF5F57]/80 dark:bg-[#FF5F57]/70" />
          <span className="size-2.5 rounded-full bg-[#FEBC2E]/80 dark:bg-[#FEBC2E]/70" />
          <span className="size-2.5 rounded-full bg-[#28C840]/80 dark:bg-[#28C840]/70" />
        </div>
        <p className="min-w-0 flex-1 truncate text-center text-xs text-muted-foreground">
          {CONTENT.shellCaption}
        </p>
        <Lock className="size-3 shrink-0 text-muted-foreground/60" aria-hidden />
      </div>

      <div className="grid grid-cols-1 @min-[40rem]:grid-cols-[160px_1fr]">
        <div className="hidden border-r border-border-subtle p-3 @min-[40rem]:block">
          <div className="space-y-1.5" aria-hidden>
            {["Overview", "Projects", "Billing", "Settings"].map((item, i) => (
              <div
                key={item}
                className={
                  i === 0
                    ? "rounded-[var(--atro-control-radius,var(--radius))] bg-[color-mix(in_oklch,var(--brand)_12%,transparent)] px-2.5 py-1.5 text-xs font-medium text-foreground"
                    : "rounded-[var(--atro-control-radius,var(--radius))] px-2.5 py-1.5 text-xs text-muted-foreground"
                }
              >
                {item}
              </div>
            ))}
          </div>
        </div>

        <div className="min-w-0 p-4 @min-[40rem]:p-5">
          <p className="text-sm font-medium text-foreground">
            {CONTENT.shellTitle}
          </p>
          <dl className="mt-3 grid grid-cols-1 gap-2 @min-[40rem]:grid-cols-3">
            {CONTENT.metrics.map((metric) => (
              <div
                key={metric.label}
                className="rounded-[var(--atro-control-radius,var(--radius))] border border-border-subtle bg-card p-2.5"
              >
                <dd className="text-sm font-semibold tabular-nums text-foreground @min-[40rem]:text-base">
                  {metric.value}
                </dd>
                <dt className="mt-0.5 truncate text-[11px] text-muted-foreground">
                  {metric.label}
                </dt>
              </div>
            ))}
          </dl>
          <ul className="mt-3 divide-y divide-border-subtle rounded-[var(--atro-control-radius,var(--radius))] border border-border-subtle">
            {CONTENT.rows.map((row) => (
              <li
                key={row.name}
                className="flex items-center justify-between gap-3 px-3 py-2.5"
              >
                <div className="min-w-0">
                  <p className="truncate text-xs font-medium text-foreground">
                    {row.name}
                  </p>
                  <p className="text-[11px] tabular-nums text-muted-foreground">
                    {row.progress}
                  </p>
                </div>
                <span className="shrink-0 rounded-[var(--atro-control-radius,var(--radius))] border border-border-subtle bg-muted/60 px-2 py-0.5 text-[11px] text-muted-foreground">
                  {row.status}
                </span>
              </li>
            ))}
          </ul>
        </div>
      </div>
    </div>
  )
}

export function AuthSplit() {
  return (
    <section className="border-t border-border-subtle bg-background text-foreground">
      <div className="border-b border-border-subtle">
        <div className="mx-auto grid max-w-7xl grid-cols-1 border-x border-border-subtle @min-[64rem]:grid-cols-2">
          <div className="flex flex-col justify-center px-6 py-12 @min-[40rem]:px-10 @min-[40rem]:py-16">
            <p className="atro-chip w-fit">
              <span className="size-1.5 rounded-full bg-brand" aria-hidden />
              {CONTENT.stamp}
            </p>
            <h2 className="mt-4 text-3xl font-medium tracking-tight text-foreground @min-[40rem]:text-4xl">
              {CONTENT.headlineBefore}{" "}
              <span className="italic text-brand">
                {CONTENT.headlineAccent}
              </span>
              {CONTENT.headlineAfter}
            </h2>
            <p className="mt-3 max-w-md text-[15px] leading-relaxed text-muted-foreground">
              {CONTENT.lede}
            </p>

            <form
              className="mt-8 max-w-md space-y-4"
              onSubmit={(e) => e.preventDefault()}
            >
              <div>
                <label
                  htmlFor="auth-split-email"
                  className="mb-1.5 block text-sm font-medium text-foreground"
                >
                  {CONTENT.emailLabel}
                </label>
                <input
                  id="auth-split-email"
                  type="email"
                  required
                  autoComplete="email"
                  placeholder={CONTENT.emailPlaceholder}
                  className="h-[var(--atro-control-height,2.25rem)] w-full rounded-[var(--atro-control-radius,var(--radius))] border border-border-subtle bg-background px-3 text-base text-foreground transition-[border-color,box-shadow] outline-none placeholder:text-muted-foreground/60 focus-visible:border-ring focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring @min-[40rem]:text-sm"
                />
              </div>
              <div>
                <label
                  htmlFor="auth-split-password"
                  className="mb-1.5 block text-sm font-medium text-foreground"
                >
                  {CONTENT.passwordLabel}
                </label>
                <input
                  id="auth-split-password"
                  type="password"
                  required
                  autoComplete="current-password"
                  placeholder="••••••••"
                  className="h-[var(--atro-control-height,2.25rem)] w-full rounded-[var(--atro-control-radius,var(--radius))] border border-border-subtle bg-background px-3 text-base text-foreground transition-[border-color,box-shadow] outline-none placeholder:text-muted-foreground/60 focus-visible:border-ring focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring @min-[40rem]:text-sm"
                />
              </div>
              <button type="submit" className="atro-btn w-full justify-center">
                {CONTENT.submitLabel}
                <ArrowRight className="size-4" aria-hidden />
              </button>
            </form>

            <p className="mt-6 max-w-md text-sm text-muted-foreground">
              {CONTENT.switchNote}{" "}
              <Link
                href={CONTENT.switchCta.href}
                className="font-medium text-brand"
              >
                {CONTENT.switchCta.label}
              </Link>
            </p>
          </div>

          <div className="border-t border-border-subtle bg-muted/30 px-6 py-12 @min-[40rem]:px-10 @min-[40rem]:py-16 @min-[64rem]:border-t-0 @min-[64rem]:border-l">
            <div className="mx-auto max-w-md @min-[64rem]:max-w-none">
              <DashboardShell />
            </div>
          </div>
        </div>
      </div>
    </section>
  )
}
```
