Sign-up block

A centered account-creation card: two-up name fields, work email, a password with the live strength meter, a plan SvSegmented switch, terms SvCheckBox and a benefits rail. Submit fires a promise toast. Pure UI-kit.

A live, editable Svelte 5 component example from the SvGrid gallery (Blocks). Read the SvGrid UI component docs for the full API.

About this example

A copy-paste sign-up page built from the SvGrid UI kit in Svelte 5: a centred SvCard with two-up name fields, a work email, a password with the live strength meter, a plan SvSegmented switch and a terms SvCheckBox, a submit SvButton that shows loading and stays disabled until the form is valid, and a benefits rail beside the card. Submit fires a promise toast.

Sign-up block - a centered account-creation card composed from the UI kit. Two-up name fields, email, a password with the live strength meter, a plan SvSegmented switch, terms SvCheckBox, and a submit SvButton. A compact benefits rail sits beside the card. Pure UI-kit, --sg-* themed.

Imports, features and API used

Imports: @svgrid/grid

Frequently asked questions

How is the submit button gated?

canSubmit is a $derived requiring a name, an email, a password of at least eight characters and the terms box; the button reads it as disabled and shows loading while the promise runs.

How is the plan chosen?

SvSegmented with block width lists the plans as mutually exclusive segments bound to plan, which goes into the submitted payload.

Can I reuse the benefits rail?

Yes. It is an SvList with type none and check icons inside a stack; edit the items or drop the rail for a single-column page.

Related documentation

Related articles

Source code (421-block-signup.svelte)

<script lang="ts">
  /**
   * Sign-up block - a centered account-creation card composed from the UI kit.
   * Two-up name fields, email, a password with the live strength meter, a plan
   * SvSegmented switch, terms SvCheckBox, and a submit SvButton. A compact
   * benefits rail sits beside the card. Pure UI-kit, --sg-* themed.
   */
  import {
    SvCard, SvTextInput, SvPasswordInput, SvSegmented, SvCheckBox, SvButton,
    SvDivider, SvList, SvBadge, SvToaster, toast,
  } from '@svgrid/grid'

  let first = $state('Ada')
  let last = $state('Lovelace')
  let email = $state('')
  let password = $state('')
  let plan = $state<string | number>('pro')
  let agree = $state(false)
  let busy = $state(false)

  const canSubmit = $derived(!!email && password.length >= 8 && agree)

  function submit(e: Event) {
    e.preventDefault()
    if (!canSubmit) { toast('Fill the form and accept the terms', { variant: 'warning' }); return }
    busy = true
    toast.promise(new Promise<void>((res) => setTimeout(res, 1200)), {
      loading: 'Creating your account...',
      success: () => { busy = false; return 'Account created - check your inbox' },
      error: () => { busy = false; return 'Could not create account' },
    })
  }
</script>

<div class="wrap">
  <div class="su-grid">
    <!-- Benefits rail -->
    <aside class="rail">
      <SvBadge variant="accent" pill>14-day free trial</SvBadge>
      <h2>Start building today.</h2>
      <p class="muted">No credit card required. Cancel anytime.</p>
      <SvDivider />
      <SvList type="none" spacing="md">
        <li>✓ Unlimited dashboards & reports</li>
        <li>✓ Live SQL and REST data sources</li>
        <li>✓ Role-based access for your team</li>
        <li>✓ Export to Excel, PDF and CSV</li>
      </SvList>
    </aside>

    <!-- Form card -->
    <SvCard title="Create your account" subtitle="Get your workspace up in under a minute.">
      <form onsubmit={submit} class="fields">
        <div class="two">
          <SvTextInput label="First name" bind:value={first} block />
          <SvTextInput label="Last name" bind:value={last} block />
        </div>
        <SvTextInput label="Work email" type="email" bind:value={email} block required>
          {#snippet leading()}<span class="ic">✉</span>{/snippet}
        </SvTextInput>
        <SvPasswordInput label="Password" bind:value={password} showStrength block required
          hint="At least 8 characters." />

        <div>
          <div class="lbl">Choose a plan</div>
          <SvSegmented
            bind:value={plan}
            block
            options={[
              { value: 'starter', label: 'Starter' },
              { value: 'pro', label: 'Pro' },
              { value: 'team', label: 'Team' },
            ]}
          />
        </div>

        <SvCheckBox checked={agree} onChange={(v) => (agree = v)}>
          I agree to the Terms of Service and Privacy Policy
        </SvCheckBox>

        <SvButton type="submit" variant="primary" block loading={busy} disabled={!canSubmit}>
          Create account
        </SvButton>
      </form>

      {#snippet footer()}
        <p class="foot">Already have an account? <button class="link" onclick={() => toast('Opening sign-in')}>Sign in</button></p>
      {/snippet}
    </SvCard>
  </div>
</div>

<SvToaster position="bottom-right" />

<style>
  .wrap { padding: 24px; }
  /* Full-width grid that centers its two fixed tracks via justify-content, so
     nothing shrinks the card and no percentage max-width can clamp it. */
  .su-grid { display: grid; grid-template-columns: 300px 420px; justify-content: center; gap: 28px; align-items: start; width: 100%; box-sizing: border-box; }
  .rail { padding: 8px 4px; display: flex; flex-direction: column; gap: 12px; }
  .rail h2 { font-size: 26px; margin: 8px 0 0; letter-spacing: -.02em; line-height: 1.15; }
  .muted { color: var(--sg-muted, #64748b); font-size: 13.5px; margin: 0; }
  .fields { display: flex; flex-direction: column; gap: 15px; }
  .two { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
  .lbl { font-size: 12.5px; font-weight: 500; margin-bottom: 6px; }
  .foot { margin: 0; text-align: center; font-size: 13px; color: var(--sg-muted, #64748b); }
  .link { background: none; border: 0; padding: 0; font: inherit; color: var(--sg-accent, #4f46e5); cursor: pointer; }
  .link:hover { text-decoration: underline; }
  .ic { display: inline-flex; font-size: 13px; }

  @media (max-width: 820px) {
    .su-grid { grid-template-columns: minmax(0, 420px); }
    .rail { display: none; }
  }
</style>

View this example on GitHub

More Blocks examples

  • Login block - A two-column authentication page: a branded gradient panel beside a sign-in SvCard with social SvButtons, SvTextInput + SvPasswordInput (reveal), remember-me SvCheckBox, an "or" SvDivider and forgot-password link. Drops into any route as-is.
  • Two-factor / OTP block - The verification step of a sign-in flow: SvOtpInput (6 digits, auto-advance + onComplete), a resend cooldown timer, and the waiting -> verifying -> verified states. Enter 000000 to see the error path.
  • Analytics dashboard block - A full overview page: a range SvSegmented + actions header, a KPI grid (SvStat + SvSparkline), a revenue bar panel, a recent-activity SvTimeline and a top-products list with SvProgress bars. No grid dependency.
  • App shell block - A complete application layout: a SvNavPane sidebar with sections + badges, a top bar with SvBreadcrumb, search and an account SvMenu, and a routed content area. Nav swaps the page; the toggle collapses the rail to an icon rail.
  • Pricing block - A three-tier plan grid with a monthly/annual SvSegmented toggle (annual applies the discount live), a highlighted "most popular" card, per-plan feature SvLists with check marks, and a CTA SvButton on each.