# Register Page

> A React register page for a CRM sign-up: an account with password rules, company details from the CUI, an email code and a welcome step.

- Type: React block (Blocks)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.1.0
- Install: `npx wingo-ui@latest add register-page`
- Docs: https://wingo-ui.com/components/register-page

## Interaction

Sign up in up to three steps shown as bars above the form; completed bars are buttons that go back, and each change is announced ("Step 2 of 3: Company"). Account: "Sign up with Google" (Apple, Microsoft) spins on the pressed button and skips the email check; name, work email (a gmail, yahoo, outlook, hotmail or icloud address shows a warning to use the company address), a password with a strength meter and a rules checklist that opens on focus, the required terms checkbox with its links, the optional product news checkbox and Continue. Fields validate when you leave them and as you fix them; submitting empty shows every error and focuses the first. An email that already has an account (emma.carter@acme.com in the demo) shows a red alert with Sign in and Forgot password?. Company: type a CUI with or without RO and press Enter or Search; an invalid control digit is an error and never calls the lookup, the button spins while the ANAF lookup runs, a match fades in the name, the address and a VAT registered badge (Edit switches to manual fields), no match asks for manual entry and a failed lookup offers Try again and Fill in by hand. Pick a role (a bottom sheet on phones) and a team size, then Continue, or Skip. Verify: type or paste the 6 digit code and the sixth digit submits (123456 passes), a wrong code shakes the cells and shows an alert, Resend the code waits with a live countdown, Change the email goes back with the email selected. With verify link the step offers Open Gmail or Outlook for those domains and I confirmed my address. Done: a bouncy check, three next step rows and Open Acme CRM. Going back keeps what you typed. Steps slide in from the side they belong to and focus moves to the step title; on phones the form fills the screen with 48px fields and the hero is gone; with reduced motion steps cross-fade.

## Installation

```bash
npx wingo-ui@latest add register-page
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/register-page` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/blocks/register-page.tsx`
- `components/blocks/register-page-data.ts`

npm dependencies: `npm i motion lucide-react react-hook-form zod`

Wingo UI dependencies (installed automatically):

- [Marketing Section](https://wingo-ui.com/components/marketing-section) ([Markdown](https://wingo-ui.com/components/marketing-section.md))
- [Alert](https://wingo-ui.com/components/alert) ([Markdown](https://wingo-ui.com/components/alert.md))
- [Badge](https://wingo-ui.com/components/badge) ([Markdown](https://wingo-ui.com/components/badge.md))
- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Checkbox](https://wingo-ui.com/components/checkbox) ([Markdown](https://wingo-ui.com/components/checkbox.md))
- [Input](https://wingo-ui.com/components/input) ([Markdown](https://wingo-ui.com/components/input.md))
- [OTP Input](https://wingo-ui.com/components/otp-input) ([Markdown](https://wingo-ui.com/components/otp-input.md))
- [Password Input](https://wingo-ui.com/components/password-input) ([Markdown](https://wingo-ui.com/components/password-input.md))
- [Segmented Control](https://wingo-ui.com/components/segmented-control) ([Markdown](https://wingo-ui.com/components/segmented-control.md))
- [Select](https://wingo-ui.com/components/select) ([Markdown](https://wingo-ui.com/components/select.md))
- [Separator](https://wingo-ui.com/components/separator) ([Markdown](https://wingo-ui.com/components/separator.md))
- [Steps](https://wingo-ui.com/components/steps) ([Markdown](https://wingo-ui.com/components/steps.md))
- [Toast](https://wingo-ui.com/components/toast) ([Markdown](https://wingo-ui.com/components/toast.md))
- [useControllableState](https://wingo-ui.com/components/use-controllable-state) ([Markdown](https://wingo-ui.com/components/use-controllable-state.md))
- [useCooldown](https://wingo-ui.com/components/use-cooldown) ([Markdown](https://wingo-ui.com/components/use-cooldown.md))
- [Form validation](https://wingo-ui.com/components/form-validation) ([Markdown](https://wingo-ui.com/components/form-validation.md))
- [Invoicing](https://wingo-ui.com/components/invoicing) ([Markdown](https://wingo-ui.com/components/invoicing.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Navigation](https://wingo-ui.com/components/navigation) ([Markdown](https://wingo-ui.com/components/navigation.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))

## Usage

```tsx
// app/(auth)/sign-up/page.tsx
"use client";

import { useRouter } from "next/navigation";
import { RegisterPage } from "@/components/blocks/register-page";

export default function Page() {
  const router = useRouter();
  return (
    <RegisterPage
      heroImage="/images/office.jpg"
      onRegister={async (values) => {
        const response = await fetch("/api/register", { method: "POST", body: JSON.stringify(values) });
        if (response.status === 409) throw Object.assign(new Error("There is already an account with this email"), { code: "email-taken" });
        if (!response.ok) throw new Error("We could not create the account");
      }}
      onLookupCui={async (cui) => {
        const response = await fetch(`/api/anaf?cui=${cui}`);
        return response.ok ? response.json() : null;
      }}
      onVerify={async (code) => {
        const response = await fetch("/api/verify", { method: "POST", body: JSON.stringify({ code }) });
        if (!response.ok) throw new Error("That code is not right");
      }}
      onDone={() => router.push("/")}
    />
  );
}
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `defaultStep?` | `RegisterStep` | `"account"` | The step shown first (uncontrolled); step / onStepChange control it. A step the flow does not have shows account. |
| `step?` | `RegisterStep` |  | The current step, controlled: "account" \| "company" \| "verify" \| "done". |
| `onStepChange?` | `(step: RegisterStep) => void` |  | Runs on every step change (mirror it to the URL or the router). |
| `companyStep?` | `boolean` | `true` | The optional company step: CUI lookup, role and team size, with Skip. |
| `verify?` | `"code" \| "link" \| "none"` | `"code"` | code: a 6 digit code step; link: a check your email step with a confirm button; none: straight to done. |
| `providers?` | `AuthProvider[]` |  | Social sign-up buttons, default REGISTER_PROVIDERS (Google, Apple, Microsoft); [] hides them and the divider. |
| `showProviders?` | `boolean` | `true` | Shortcut for providers={[]} when false. |
| `plan?` | `ReactNode` | `"Pro · 14 days free, no card"` | The badge above the account title (labels.plan by default); a node replaces the badge, null or an empty string hides it. |
| `passwordMinLength?` | `number` | `8` | Minimum password length: the rule, the checklist and the strength meter. |
| `passwordMinScore?` | `number` | `2` | The lowest scorePassword result (0 to 4) the password must reach; 0 accepts any password of the minimum length. |
| `freeMailWarning?` | `boolean` | `true` | A warning (not an error) under the email for gmail.com, yahoo.com, yahoo.ro, outlook.com, hotmail.com and icloud.com. |
| `defaultValues?` | `Partial<RegisterValues>` |  | Prefills every step (name, email, password, terms, marketing, company). |
| `defaultCompany?` | `CompanyLookup \| null` |  | A lookup result the company step opens with (name, address, vatPayer). |
| `onRegister?` | `(values: RegisterValues) => (void \| Promise<unknown>)` |  | After the account step: create the account and send the code. Default: simulated, rejects "There is already an account with this email" for emma.carter@acme.com; a rejection with that message or code "email-taken" shows the sign-in and password links. |
| `onLookupCui?` | `(cui: string) => Promise<CompanyLookup \| null>` |  | Looks up a valid CUI (normalized with formatCui). Default: the REGISTER_PAGE_SAMPLE.companies table (Tech Solutions SRL for RO12345674 and the other sample clients) after 900 ms; null asks for manual entry, a rejection offers a retry. |
| `onCompany?` | `(company: RegisterCompany) => (void \| Promise<unknown>)` |  | Saves the company step (name, cui, role, teamSize, address, vatPayer). Default: simulated. |
| `onVerify?` | `(code: string) => (void \| Promise<unknown>)` |  | Checks the 6 digit code. Default: 123456 passes, anything else rejects "That code is not right". |
| `onResend?` | `() => (void \| Promise<unknown>)` |  | Sends the code or the link again; the button then waits resendSeconds. Default: simulated. |
| `onProvider?` | `(id: string) => (void \| Promise<unknown>)` |  | Starts a social sign-up; the button spins until it settles, then the flow skips to the company step (or done). Default: simulated for 1.2 s. |
| `onDone?` | `() => void` |  | The Open Acme CRM button. Default: the toast "Welcome to Acme CRM" (the page mounts its own Toaster then). |
| `resendSeconds?` | `number` | `30` | Wait before the code or the link can be sent again (use-cooldown). |
| `layout?` | `"split" \| "centered" \| "card"` | `"split"` | split: the form beside a hero from 1024px; centered: no hero, the form in a card from 640px; card: no hero, the form in a raised card at every width, centered on the page background (dots unless background is set) with the logo above and the links below. |
| `heroSide?` | `"start" \| "end"` | `"end"` | Which side the hero sits on in the split layout. |
| `background?` | `"plain" \| "soft" \| "glow" \| "aurora" \| "grid" \| "dots"` | `"glow"` | The page background: plain is the bare page color; the others tint the page and add a glow, an aurora, a grid or dots over the first screen. The layout picks one unless you set it (split: glow; centered: glow; card: dots). |
| `backgroundColor?` | `string` | `"oklch(0.62 0.14 275)"` | The tint and glow color of the page background. |
| `heroImage?` | `string` | `""` | A photo behind the hero with a dark scrim and white text; empty keeps the neutral card hero. The demo passes /demo/laptop.jpg. |
| `hero?` | `ReactNode` |  | Replaces the hero content (badge, title, feature checklist and customer names). |
| `logo?` | `ReactNode` |  | Top-left of the form column; default the Acme CRM mark and name. |
| `size?` | `"md" \| "lg"` | `"lg"` | Fields and buttons: lg is 48px, md 40px (44px on touch); the code cells stay 48px. |
| `loginHref?` | `string` | `"/sign-in"` | The "Sign in" links (footer and the email taken alert); "" hides them. |
| `forgotHref?` | `string` | `"/sign-in#forgot"` | The "Forgot password?" link of the email taken alert; "" hides it. |
| `termsHref?` | `string` | `"/terms"` | The terms link in the checkbox and the footer; "" leaves plain text. |
| `privacyHref?` | `string` | `"/privacy"` | The privacy link in the checkbox and the footer; "" leaves plain text. |
| `tone?` | `Tone` | `"neutral"` | The primary buttons, the steps, the checkboxes, the team size thumb and the plan badge. |
| `radius?` | `"sm" \| "md" \| "lg"` | `"md"` | The hero, the card, the tiles and the controls' corners. |
| `animateIn?` | `boolean` | `true` | The logo, form, footer and hero rise in once when the page mounts on the client (server html is already final). |
| `labels?` | `Partial<RegisterPageLabels>` |  | Every visible string, English by default (DEFAULT_REGISTER_PAGE_LABELS); spread it and override what you translate; steps takes the steps indicator strings, validation the form-validation messages. |
| `locale?` | `string` | `"en-US"` | Sets the page language (lang). |
| `linkComponent?` | `(props: LinkComponentProps) => ReactNode` |  | The router link for the sign-in, legal and next step links (next/link); default <a>. |
| `classNames?` | `Partial<Record<RegisterPageSlot, string>>` |  | Classes for formColumn, header, body, footer, hero, steps, step, title and form. |

## Parts

### DEFAULT_REGISTER_PAGE_LABELS (constant)

Every English string of the account, company, verify and done steps, the hero and the footer. Spread it and override what you translate.

### REGISTER_PAGE_LABELS_EN (constant)

An alias of DEFAULT_REGISTER_PAGE_LABELS, kept so older imports keep working.

### REGISTER_PROVIDERS (constant)

The default social sign-up buttons: Google, Apple and Microsoft with their marks. Filter it or add your own { id, label, icon }.

### REGISTER_PAGE_SAMPLE (constant)

The demo content: the person who signs up, the hero copy and customers, roles and team sizes, the welcome checklist, the simulated CUI register and the code that passes.

## Examples

- **Company, found by CUI**: The company step after an ANAF lookup of RO12345674: name, address and the VAT badge, then role and team size.
- **Verification code**: Six digit code sent to the email (123456 passes), a resend that waits 30 s and Change the email.
- **Welcome**: The last step: the bouncy check, the first steps as rows and the way into the app.
- **No company step, email link**: companyStep={false} and verify="link": two steps, the second a check your email view.
- **Centered card, brand**: layout="centered" with tone="brand" on the primary actions, the steps and the checkboxes.
- **Card layout**: layout="card": every step in a raised card on a dotted page background, the logo above it and the sign-in link below.
- **Neutral hero**: locale en-GB and the neutral hero (no photo).

## See also

- [Login Page](https://wingo-ui.com/components/login-page) ([Markdown](https://wingo-ui.com/components/login-page.md))
- [Password Input](https://wingo-ui.com/components/password-input) ([Markdown](https://wingo-ui.com/components/password-input.md))
- [Steps](https://wingo-ui.com/components/steps) ([Markdown](https://wingo-ui.com/components/steps.md))
- [Form validation](https://wingo-ui.com/components/form-validation) ([Markdown](https://wingo-ui.com/components/form-validation.md))

---

Canonical: https://wingo-ui.com/components/register-page
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
