# Unidy Web SDK — Registration

A complete multi-step sign-up built from <u-registration-root>: you declare the step order once, and steps that don't apply — password for social sign-ups, email verification after a resume, legacy-account matching when disabled — skip themselves. Interrupted and invited users are handled too.

## Setup

Every example below assumes the SDK is loaded and configured once per page:

```html
<script type="module" src="https://cdn.jsdelivr.net/npm/@unidy.io/sdk@1.8.1/dist/sdk/sdk.esm.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@unidy.io/sdk@1.8.1/dist/sdk/sdk.css">

<u-config
  base-url="https://hammarby.staging.unidy.app"
  api-key="your-api-key"
  locale="en"
  check-signed-in="true"></u-config>
```

## Multi-step registration wizard

The steps attribute defines the order: email, verification, internal matching, profile, password, passkey and newsletter opt-ins. u-registration-resume appears automatically when the email already has an unfinished registration, verification codes auto-send and auto-submit, and the passkey step is optional. Try it with a fresh email address.

- **Declarative steps** — steps='[…]' — the SDK handles transitions and skipping
- **Email verification** — auto-send 4-digit code with auto-submit
- **Resume support** — unfinished registrations continue via email link
- **Legacy account matching** — u-registration-internal-matching links existing records
- **Passkey enrollment** — optional u-registration-passkey step
- **Newsletter opt-ins** — collected during sign-up with topic preferences

```html
<!-- steps defines the order; steps whose requirements don't apply are skipped
     automatically (e.g. password for social sign-ups, internal-matching when
     the feature is disabled in the tenant). -->
<u-registration-root
  id="registration-root"
  steps='["email","verification","internal-matching","profile","password","passkey","newsletters"]'>
  <u-error-message for="registration" class-name="mb-3 text-sm text-danger"></u-error-message>

  <!-- 1: email -->
  <u-registration-step name="email">
    <div class="flex flex-col gap-3">
      <p class="text-center text-sm text-text-light">Create your account to get started</p>
      <u-raw-field
        field="email"
        type="email"
        required
        placeholder="Enter your email"
        class-name="px-4 py-2 border border-border rounded-lg w-full"></u-raw-field>
      <u-error-message for="email" class-name="text-sm text-danger"></u-error-message>

      <!-- Appears automatically when a registration already exists for this email -->
      <u-registration-resume
        class-name="px-4 py-2 border border-primary-light text-primary bg-primary-soft rounded-lg text-center w-full cursor-pointer disabled:opacity-50">
        Resume my registration
        <span slot="success" class="block text-sm text-success-text"
          >Resume link sent — check your email.</span
        >
        <span slot="resend">Resend resume link</span>
      </u-registration-resume>

      <u-submit-button
        class-name="px-4 py-2 text-white bg-accent hover:bg-accent-hover rounded-lg disabled:opacity-50 w-full cursor-pointer">
        Continue
      </u-submit-button>
    </div>
  </u-registration-step>

  <!-- 2: email verification (skipped if already verified, e.g. after resume) -->
  <u-registration-step name="verification" requires-email-verification>
    <div class="flex flex-col gap-3">
      <p class="text-center text-sm text-text-light">Enter the 4-digit code sent to your email</p>
      <u-registration-email-verification
        auto-send
        class-name="flex justify-center gap-2"
        input-class-name="w-12 h-12 text-center text-xl font-bold border-2 border-border rounded-lg focus:border-primary focus:outline-none"
      ></u-registration-email-verification>
      <u-error-message for="verificationCode" class-name="text-center text-sm text-danger"
      ></u-error-message>
      <u-registration-resend
        class-name="px-4 py-2 border border-border rounded-lg text-center w-full cursor-pointer hover:bg-background-light disabled:opacity-50">
        Resend code
      </u-registration-resend>
    </div>
  </u-registration-step>

  <!-- 3: link a legacy account (skipped unless enabled in the tenant) -->
  <u-registration-step name="internal-matching">
    <u-registration-internal-matching
      class-name="flex flex-col gap-3"
      input-class-name="px-4 py-2 border border-border rounded-lg w-full"
      primary-button-class-name="px-4 py-2 text-white bg-accent hover:bg-accent-hover rounded-lg cursor-pointer"
      secondary-button-class-name="px-4 py-2 border border-border rounded-lg cursor-pointer hover:bg-background-light"
      error-class-name="text-sm text-danger"></u-registration-internal-matching>
  </u-registration-step>

  <!-- 4: profile -->
  <u-registration-step name="profile">
    <div class="flex flex-col gap-3">
      <div class="grid grid-cols-2 gap-3">
        <u-raw-field
          field="first_name"
          type="text"
          required
          placeholder="First name"
          class-name="px-4 py-2 border border-border rounded-lg w-full"></u-raw-field>
        <u-raw-field
          field="last_name"
          type="text"
          placeholder="Last name"
          class-name="px-4 py-2 border border-border rounded-lg w-full"></u-raw-field>
      </div>
      <u-error-message for="first_name" class-name="text-sm text-danger"></u-error-message>
      <div class="flex gap-2">
        <u-back-button
          class-name="px-4 py-2 border border-border rounded-lg cursor-pointer hover:bg-background-light"
          >Back</u-back-button
        >
        <u-submit-button
          class-name="flex-1 px-4 py-2 text-white bg-accent hover:bg-accent-hover rounded-lg disabled:opacity-50 cursor-pointer"
          >Continue</u-submit-button
        >
      </div>
    </div>
  </u-registration-step>

  <!-- 5: password (skipped for social/passwordless sign-ups) -->
  <u-registration-step name="password" requires-password>
    <div class="flex flex-col gap-3">
      <u-raw-field
        field="password"
        type="password"
        required
        placeholder="Create a password"
        pattern="^(?=.*[A-Z])(?=.*[0-9]).{8,}$"
        pattern-error-message="Min 8 characters, one uppercase letter and one number"
        class-name="px-4 py-2 border border-border rounded-lg w-full"></u-raw-field>
      <p class="text-xs text-text-muted">Min 8 characters, one uppercase letter and one number</p>
      <u-error-message for="password" class-name="text-sm text-danger"></u-error-message>
      <div class="flex gap-2">
        <u-back-button
          class-name="px-4 py-2 border border-border rounded-lg cursor-pointer hover:bg-background-light"
          >Back</u-back-button
        >
        <u-submit-button
          class-name="flex-1 px-4 py-2 text-white bg-accent hover:bg-accent-hover rounded-lg disabled:opacity-50 cursor-pointer"
          >Continue</u-submit-button
        >
      </div>
    </div>
  </u-registration-step>

  <!-- 6: passkey enrollment (optional — Continue skips it) -->
  <u-registration-step name="passkey">
    <div class="flex flex-col gap-3">
      <p class="text-center text-sm text-text-light">Add a passkey for password-free sign-ins</p>
      <u-registration-passkey
        class-name="px-4 py-2 text-white bg-dark hover:bg-dark-lighter rounded-lg disabled:opacity-50 w-full cursor-pointer">
        Add passkey
      </u-registration-passkey>
      <div class="flex gap-2">
        <u-back-button
          class-name="px-4 py-2 border border-border rounded-lg cursor-pointer hover:bg-background-light"
          >Back</u-back-button
        >
        <u-submit-button
          class-name="flex-1 px-4 py-2 text-white bg-accent hover:bg-accent-hover rounded-lg disabled:opacity-50 cursor-pointer"
          >Continue</u-submit-button
        >
      </div>
    </div>
  </u-registration-step>

  <!-- 7: newsletter opt-ins collected during sign-up -->
  <u-registration-step name="newsletters">
    <div class="flex flex-col gap-3">
      <p class="text-sm font-medium text-text-light">Stay in the loop</p>
      <label class="flex cursor-pointer items-center gap-3 text-sm">
        <u-registration-newsletter name="main" checked></u-registration-newsletter>
        Main Newsletter
      </label>
      <div class="ml-6 flex flex-col gap-2">
        <label class="flex cursor-pointer items-center gap-3 text-sm">
          <u-registration-newsletter-preference name="main" preference="news-updates"
          ></u-registration-newsletter-preference>
          News Updates
        </label>
        <label class="flex cursor-pointer items-center gap-3 text-sm">
          <u-registration-newsletter-preference name="main" preference="tips-tricks"
          ></u-registration-newsletter-preference>
          Tips &amp; Tricks
        </label>
      </div>
      <label class="flex cursor-pointer items-center gap-3 text-sm">
        <u-registration-newsletter name="weather"></u-registration-newsletter>
        Weather Forecast
      </label>
      <div class="flex gap-2">
        <u-back-button
          class-name="px-4 py-2 border border-border rounded-lg cursor-pointer hover:bg-background-light"
          >Back</u-back-button
        >
        <u-submit-button
          class-name="flex-1 px-4 py-2 text-white bg-accent hover:bg-accent-hover rounded-lg disabled:opacity-50 cursor-pointer"
          >Complete registration</u-submit-button
        >
      </div>
    </div>
  </u-registration-step>
</u-registration-root>

<u-signed-in>
  <p class="mt-3 text-sm text-success-text">
    You are signed in — registration is for new accounts.
  </p>
</u-signed-in>
```

Live demo: /registration#registration-flow

## Unconfirmed & invited accounts

Two account states commonly break naive login forms: users who registered but never confirmed their email, and users invited by an admin or import who never accepted. The sign-in flow routes each to its own step, where u-resend-confirmation-email and u-resend-invitation-email offer the fix — both rate-limit themselves after sending.

- **unconfirmed step** — resend the confirmation email
- **invited step** — resend the invitation email (expired-link recovery)
- **Built-in rate limiting** — buttons disable with a countdown after sending

```html
<!-- The sign-in flow routes special account states to dedicated steps:
     "unconfirmed" — registered but never confirmed the email
     "invited"     — invited by an admin/import but hasn't accepted yet -->
<u-signin-root class="flex flex-col gap-2">
  <u-signin-step name="email">
    <u-email-field
      placeholder="Enter your email"
      class-name="px-4 py-2 mb-2 border border-border rounded-lg w-full"></u-email-field>
    <u-error-message for="email" class-name="mb-2 text-sm text-danger"></u-error-message>
    <u-submit-button
      for="email"
      text="Continue"
      class-name="px-4 py-2 text-white bg-accent hover:bg-accent-hover rounded-lg disabled:opacity-50 w-full cursor-pointer"
    ></u-submit-button>
  </u-signin-step>

  <u-signin-step name="unconfirmed">
    <u-back-button class-name="mb-3 text-sm text-primary cursor-pointer">← Back</u-back-button>
    <div class="rounded-lg bg-warning-bg p-4 text-sm text-warning-text">
      <p class="mb-3">
        This account exists but the email address was never confirmed. Resend the confirmation email
        to finish setting it up.
      </p>
      <u-resend-confirmation-email
        class-name="px-4 py-2 rounded-lg border border-warning-border text-sm font-medium cursor-pointer hover:bg-warning-border/40 disabled:opacity-50">
        Resend confirmation email
      </u-resend-confirmation-email>
    </div>
  </u-signin-step>

  <u-signin-step name="invited">
    <u-back-button class-name="mb-3 text-sm text-primary cursor-pointer">← Back</u-back-button>
    <div class="rounded-lg bg-primary-soft p-4 text-sm">
      <p class="mb-3 text-text">
        This account was created by invitation and hasn't been accepted yet. Resend the invitation
        email so the user can set their password — the button rate-limits itself with a countdown
        after each send.
      </p>
      <u-resend-invitation-email
        class-name="px-4 py-2 rounded-lg bg-accent text-white text-sm font-medium cursor-pointer hover:bg-accent-hover disabled:opacity-50">
        Resend invitation email
      </u-resend-invitation-email>
    </div>
  </u-signin-step>
</u-signin-root>
```

Live demo: /registration#edge-cases

## Registration events

u-registration-root emits stepChange on every transition, registrationComplete when the account is created, and errorEvent on failures — wire them up with plain addEventListener for progress tracking, analytics, or post-signup redirects.

- **stepChange / registrationComplete / errorEvent** — plain DOM events
- **Live event log** — walk through the wizard and watch

```html
<div class="rounded-lg border border-border bg-background-light p-4">
  <p class="mb-2 text-sm font-medium">Event log</p>
  <ul id="registration-event-log" class="flex flex-col gap-1 font-mono text-xs text-text-light">
    <li>Waiting for registration events… (use the wizard above)</li>
  </ul>
</div>

<script is:inline>
  (() => {
    const log = (message) => {
      const entry = document.createElement("li");
      entry.textContent = `${new Date().toLocaleTimeString()} ${message}`;
      document.getElementById("registration-event-log")?.prepend(entry);
    };

    const registration = document.getElementById("registration-root");

    // Fired on every step transition — drive progress UIs or analytics from it
    registration?.addEventListener("stepChange", (event) => {
      log(`stepChange → ${event.detail?.stepName ?? JSON.stringify(event.detail)}`);
    });

    // Fired once the account is created
    registration?.addEventListener("registrationComplete", (event) => {
      log(`registrationComplete (${JSON.stringify(event.detail ?? {})})`);
    });

    registration?.addEventListener("errorEvent", (event) => {
      log(`errorEvent — ${event.detail?.error ?? "unknown error"}`);
    });
  })();
</script>
```

Live demo: /registration#registration-events

## Further reading

- Full component reference: https://github.com/UnidyID/unidy-sdk/blob/HEAD/packages/sdk/readme.md
- Official quick-start examples: https://github.com/UnidyID/unidy-sdk/blob/HEAD/packages/sdk/quick-start-examples.md
- Package: https://www.npmjs.com/package/@unidy.io/sdk
- All guides: /llms.txt
