Docs

Styling for Clerk Elements

Note

Clerk Elements is for advanced use-cases that require a high-level of customization. If you are using our all-in-one UI components, learn more about customization through our Appearance Prop.

You can style Clerk Elements components with the following props:

  • className – Can be used on any Clerk Elements component that renders markup.
  • asChild – Can be used to change the rendered element entirely.

This guide will demonstrate multiple different styling approaches using the following basic sign-in flow as a starting point:

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

import * as Clerk from '@clerk/elements/common'
import * as SignIn from '@clerk/elements/sign-in'

export default function SignInPage() {
  return (
    <SignIn.Root>
      <SignIn.Step name="start">
        <Clerk.Connection name="google">Sign in with Google</Clerk.Connection>
        <Clerk.Field name="identifier">
          <Clerk.Label>Email</Clerk.Label>
          <Clerk.Input />
          <Clerk.FieldError />
        </Clerk.Field>
        <SignIn.Action submit>Continue</SignIn.Action>
      </SignIn.Step>
      <SignIn.Step name="verifications">
        <SignIn.Strategy name="email_code">
          <Clerk.Field name="code">
            <Clerk.Label>Code</Clerk.Label>
            <Clerk.Input />
            <Clerk.FieldError />
          </Clerk.Field>
          <SignIn.Action submit>Verify</SignIn.Action>
        </SignIn.Strategy>
      </SignIn.Step>
    </SignIn.Root>
  )
}

Tailwind CSS

If you are already using Tailwind CSS, no additional setup is required. Classes from Tailwind can be applied to most Clerk Elements components. Use your editor's IntelliSense to see if className is a valid prop on a component you want to style.

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

import * as Clerk from '@clerk/elements/common'
import * as SignIn from '@clerk/elements/sign-in'

export default function SignInPage() {
  return (
    <SignIn.Root>
      <SignIn.Step
        name="start"
        className="bg-white w-96 rounded-2xl py-10 px-8 shadow-sm border space-y-6"
      >
        <div className="grid grid-cols-2 gap-x-4">
          <Clerk.Connection
            name="google"
            className="flex items-center gap-x-3 justify-center font-medium border shadow-sm py-1.5 px-2.5 rounded-md"
          >
            <Clerk.Icon className="size-4" />
            Google
          </Clerk.Connection>
          <Clerk.Connection
            name="github"
            className="flex items-center gap-x-3 justify-center font-medium border shadow-sm py-1.5 px-2.5 rounded-md"
          >
            <Clerk.Icon className="size-4" />
            GitHub
          </Clerk.Connection>
        </div>
        <Clerk.Field name="identifier" className="space-y-2">
          <Clerk.Label className="text-sm font-medium">Email</Clerk.Label>
          <Clerk.Input className="w-full border rounded-md py-1.5 px-2.5" />
          <Clerk.FieldError className="block text-red-500 text-sm" />
        </Clerk.Field>
        <SignIn.Action
          submit
          className="bg-black text-white rounded-md py-1.5 px-2.5"
        >
          Continue
        </SignIn.Action>
      </SignIn.Step>
    </SignIn.Root>
  )
}

With existing components via asChild

Many of the Clerk Elements components accept an asChild prop that allows you to swap out the rendered element. This is useful if you have an existing design system or component library that you wish to use with Clerk Elements.

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

import * as Clerk from '@clerk/elements/common'
import * as SignIn from '@clerk/elements/sign-in'

import { Button } from '@components/button'
import { Input } from '@components/input'

export default function SignInPage() {
  return (
    <SignIn.Root>
      <SignIn.Step name="start">
        <Clerk.Connection name="google" asChild>
          <Button>Sign in with Google</Button>
        </Clerk.Connection>
        <Clerk.Field name="identifier">
          <Clerk.Label>Email</Clerk.Label>
          <Clerk.Input asChild>
            <Input />
          </Clerk.Input>
          <Clerk.FieldError />
        </Clerk.Field>
        <SignIn.Action submit asChild>
          <Button>Continue</Button>
        </SignIn.Action>
      </SignIn.Step>
      <SignIn.Step name="verifications">
        <SignIn.Strategy name="email_code">
          <Clerk.Field name="code">
            <Clerk.Label>Code</Clerk.Label>
            <Clerk.Input asChild>
              <Input />
            </Clerk.Input>
            <Clerk.FieldError />
          </Clerk.Field>
          <SignIn.Action submit asChild>
            <Button>Continue</Button>
          </SignIn.Action>
        </SignIn.Strategy>
      </SignIn.Step>
    </SignIn.Root>
  )
}

Notice how the Clerk Elements components are wrapping the rendered <Input> and <Button> when asChild is used. This ensures the underlying event handlers and necessary props are passed along automatically.

Configure your components for asChild

To use the asChild prop, your component must spread its incoming props and return forwardRef(). Here's an example of how you might implement a custom <Input /> component:

import { forwardRef } from 'react'

const CustomInput = forwardRef(function CustomInput(props, forwardedRef) {
  return <input ref={forwardedRef} {...props} className="custom-class" />
})

CSS Modules

Classes from an imported CSS module can be applied to most Clerk Elements components with className.

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

import * as Clerk from '@clerk/elements/common'
import * as SignIn from '@clerk/elements/sign-in'
import styles from './sign-in.module.css'

export default function SignInPage() {
  return (
    <SignIn.Root>
      <SignIn.Step name="start" className={styles.startStep}>
        <Clerk.Connection name="google" className={styles.provider}>Sign in with Google</Clerk.Connection>
        <Clerk.Field name="identifier">
          <Clerk.Label className={styles.label}>Email</Clerk.Label>
          <Clerk.Input className={styles.input} />
          <Clerk.FieldError className={styles.error} />
        </Clerk.Field>
        <SignIn.Action submit className={styles.submit}>Continue</SignIn.Action>
      </SignIn.Step>
      <SignIn.Step name="verifications" className={styles.verificationsStep}>
        <SignIn.Strategy name="email_code">
          <Clerk.Field name="code">
            <Clerk.Label className={styles.label}>Code</Clerk.Label>
            <Clerk.Input className={styles.input} />
            <Clerk.FieldError className={styles.error} />
          </Clerk.Field>
          <SignIn.Action submit className={styles.submit}>Verify</SignIn.Action>
      </SignIn.Step>
    </SignIn.Root>
  )
}

Inline styles

You can also use inline styles with Clerk Elements. This is useful when you need to apply styles conditionally or avoid creating a separate CSS file.

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

import * as Clerk from '@clerk/elements/common'
import * as SignIn from '@clerk/elements/sign-in'

export default function SignInPage() {
  return (
    <SignIn.Root>
      <SignIn.Step name="start" style={{
        backgroundColor: 'white',
        padding: '1rem',
        border: '1px solid #dddddd',
        borderRadius: '4px',
      }}>
        <Clerk.Field name="identifier">
          <Clerk.Label style={{
            color: '#dddddd',
            fontSize: '0.875rem',
          }}>Email</Clerk.Label>
          <Clerk.Input style={{
            border: '1px solid #dddddd',
            borderRadius: '4px',
            padding: '1rem',
          }} />
        </Clerk.Field>
        <SignIn.Action submit style={{
          backgroundColor: '#111111',
          color: 'white',
          padding: '1rem',
          borderRadius: '4px',
        }}>Continue</SignIn.Action>
      </SignIn.Step>
    </SignIn.Root>
  )
}

State-based styling

In some cases you might want to style components based on their state. Clerk Elements exposes data attributes for this purpose, as well as components that expose the state programmatically to support more complex logic.

Data attributes

<Field> and <Input> can be styled based on their validity state by targeting the data-valid or data-invalid attributes:

style.css
.input {
  --border-color: gray;
  border: 1px solid var(--border-color);

  &[data-invalid] {
    --border-color: red;
  }
}
page.tsx
<Clerk.Input className="input" />

Function as children

If you need programmatic access to state for more complex styling, several components provide a function as children. This is useful when dealing with animations, or for conditionally rendering elements based on state.

For example, to access a field's state, use <FieldState>.

page.tsx
<Clerk.Field name="email">
  <Clerk.FieldState>
    {(state) => state === 'invalid' && <ErrorIcon />}
  </Clerk.FieldState>
  <Clerk.Label>Email</Clerk.Label>
  <Clerk.Input />
  <Clerk.FieldError />
</Clerk.Field>

Feedback

What did you think of this content?