Docs

Build a sign-up flow with Clerk Elements

You will learn the following:

  • Build out different steps of an authentication flow
  • Accept user input through form fields
  • Accept additional data during sign-up
  • Verify a user's identity during a sign-up attempt

Note

  • Clerk Elements is for advanced use-cases that require a high-level of customization. The easiest way to implement Clerk is with our all-in-one UI components.

  • Clerk Elements currently only works with Next.js and Clerk Core 2. As it gets closer to a stable release, support for additional frameworks will be added.

Add a sign-up route

Create a new route in your Next.js application. The route needs to be an optional catch-all route so the sign-up flow can handled nested paths, as shown in the following example:

/app/sign-up/[[...sign-up]]/page.tsx
'use client'

import * as Clerk from '@clerk/elements/common'
import * as SignUp from '@clerk/elements/sign-up'

export default function SignUpPage() {
  return (
    <SignUp.Root>
      [Sign Up Root]
    </SignUp.Root>
  )
}

You will use these two imports to build out the rest of the flow. <SignUp.Root> manages the sign-up state and handles connecting the components to Clerk's APIs.

Tip

If you're getting TypeScript errors on the @clerk/elements imports you probably have forgotten to set your moduleResolution in tsconfig.json to bundler.

Add the start step

The Clerk authentication flows are made up of steps. Steps handle rendering the UI for each part of the flow. To allow users to create a sign-up attempt, the start step needs to be rendered. The following example does so with the <SignUp.Step> component:

/app/sign-up/[[...sign-up]]/page.tsx
'use client'

import * as Clerk from '@clerk/elements/common'
import * as SignUp from '@clerk/elements/sign-up'

export default function SignUpPage() {
  return (
    <SignUp.Root>
      <SignUp.Step name="start">
        <h1>Create an account</h1>
      </SignUp.Step>
    </SignUp.Root>
  )
}

Add form fields

Make it functional by adding input fields. The following example uses the <Clerk.Field> component to render the emailAddress and username fields, as well as the <Connection> component to allow users to sign up with a social connection, like Google:

/app/sign-up/[[...sign-up]]/page.tsx
'use client'

import * as Clerk from '@clerk/elements/common'
import * as SignUp from '@clerk/elements/sign-up'

export default function SignUpPage() {
  return (
    <SignUp.Root>
      <SignUp.Step name="start">
        <h1>Create an account</h1>

        <Clerk.Connection name="google">
          Sign up with Google
        </Clerk.Connection>
        
        <Clerk.Field name="username">
          <Clerk.Label>Username</Clerk.Label>
          <Clerk.Input />
          <Clerk.FieldError />
        </Clerk.Field>

        <Clerk.Field name="emailAddress">
          <Clerk.Label>Email</Clerk.Label>
          <Clerk.Input />
          <Clerk.FieldError />
        </Clerk.Field>
        
        <Clerk.Field name="password">
          <Clerk.Label>Password</Clerk.Label>
          <Clerk.Input />
          <Clerk.FieldError />
        </Clerk.Field>

        <SignUp.Action submit>Sign up</SignUp.Action>
      </SignUp.Step>
    </SignUp.Root>
  )
}

<Clerk.Field> takes care of wiring up the input with the label element, and <Clerk.FieldError> will render any field-specific errors that get returned from Clerk's API. The <SignUp.Action> component provides common actions that are used throughout the flows. In this case, using the submit action to render a submit button for the start form.

Add verification

As users progress through a sign-up attempt, they may be asked to verify a number of authentication factors in the verifications step. You can render a form for the user to complete verification, but each verification strategy requires different fields. You must render the form fields conditionally for each authentication strategy your instance supports using the <SignIn.Strategy> component.

The following example demonstrates how to conditionally render a form for the phone_code and email_code strategies:

/app/sign-up/[[...sign-up]]/page.tsx
'use client'

import * as Clerk from '@clerk/elements/common'
import * as SignUp from '@clerk/elements/sign-up'

export default function SignUpPage() {
  return (
    <SignUp.Root>
      <SignUp.Step name="start">
        <h1>Create an account</h1>

        <Clerk.Connection name="google">
          Sign up with Google
        </Clerk.Connection>

        <Clerk.Field name="username">
          <Clerk.Label>Username</Clerk.Label>
          <Clerk.Input />
          <Clerk.FieldError />
        </Clerk.Field>

        <Clerk.Field name="emailAddress">
          <Clerk.Label>Email</Clerk.Label>
          <Clerk.Input />
          <Clerk.FieldError />
        </Clerk.Field>
        
        <Clerk.Field name="password">
          <Clerk.Label>Password</Clerk.Label>
          <Clerk.Input />
          <Clerk.FieldError />
        </Clerk.Field>

        <SignUp.Action submit>Sign up</SignUp.Action>
      </SignUp.Step>

      <SignUp.Step name="verifications">
        <SignUp.Strategy name="phone_code">
          <h1>Check your phone for an SMS</h1>

          <Clerk.Field name="code">
            <Clerk.Label>Phone Code</Clerk.Label>
            <Clerk.Input />
            <Clerk.FieldError />
          </Clerk.Field>

          <SignUp.Action submit>Verify</SignUp.Action>
        </SignUp.Strategy>

        <SignUp.Strategy name="email_code">
          <h1>Check your email</h1>

          <Clerk.Field name="code">
            <Clerk.Label>Email Code</Clerk.Label>
            <Clerk.Input />
            <Clerk.FieldError />
          </Clerk.Field>

          <SignUp.Action submit>Verify</SignUp.Action>
        </SignUp.Strategy>
      </SignUp.Step>
    </SignUp.Root>
  )
}

Verification is the final step in the sign-up flow. When a user has verified all required factors, the sign-up attempt will be complete, their account will be created, and they will be signed in.

Accept additional fields

Should a user attempt to sign up via Google while a username is a required field, the continue step will be necessary to accept the username. The <SignUp.Step> component will display any additional required fields.

/app/sign-up/[[...sign-up]]/page.tsx
'use client'

import * as Clerk from '@clerk/elements/common'
import * as SignUp from '@clerk/elements/sign-up'

export default function SignUpPage() {
  return (
    <SignUp.Root>
      <SignUp.Step name="start">
        <h1>Create an account</h1>

        <Clerk.Connection name="google">
          Sign up with Google
        </Clerk.Connection>

        <Clerk.Field name="username">
          <Clerk.Label>Username</Clerk.Label>
          <Clerk.Input />
          <Clerk.FieldError />
        </Clerk.Field>

        <Clerk.Field name="emailAddress">
          <Clerk.Label>Email</Clerk.Label>
          <Clerk.Input />
          <Clerk.FieldError />
        </Clerk.Field>
        
        <Clerk.Field name="password">
          <Clerk.Label>Password</Clerk.Label>
          <Clerk.Input />
          <Clerk.FieldError />
        </Clerk.Field>

        <SignUp.Action submit>Sign up</SignUp.Action>
      </SignUp.Step>

      <SignUp.Step name="continue">
        <h1>Fill in missing fields</h1>

        <Clerk.Field name="username">
          <Clerk.Label>Username</Clerk.Label>
          <Clerk.Input />
          <Clerk.FieldError />
        </Clerk.Field>

        <SignUp.Action submit>Continue</SignUp.Action>
      </SignUp.Step>
      
      <SignUp.Step name="verifications">
        <SignUp.Strategy name="phone_code">
          <h1>Check your phone for an SMS</h1>

          <Clerk.Field name="code">
            <Clerk.Label>Phone Code</Clerk.Label>
            <Clerk.Input />
            <Clerk.FieldError />
          </Clerk.Field>

          <SignUp.Action submit>Verify</SignUp.Action>
        </SignUp.Strategy>
        
        <SignUp.Strategy name="email_code">
          <h1>Check your email</h1>

          <Clerk.Field name="code">
            <Clerk.Label>Email Code</Clerk.Label>
            <Clerk.Input />
            <Clerk.FieldError />
          </Clerk.Field>

          <SignUp.Action submit>Verify</SignUp.Action>
        </SignUp.Strategy>
      </SignUp.Step>
    </SignUp.Root>
  )
}

If your instance has additional required fields, you can add them the same way you added the username field to the continue step.

Note

Under the hood, Clerk Elements will conditionally render the fields that are necessary to complete the sign up attempt, so there's no need to check the state of the sign up attempt yourself.

Customize and add styling

Learn how to style your Clerk Elements components with the styling guide.

For more extensive customization of the UI, check out additional Clerk Elements components such as <Loading> and <FieldState>.

Feedback

What did you think of this content?