Skip to content

shadcn.io is an independent project built on shadcn/ui

Free

Tabs with Forms for React

Login and Register split the row in half. Login has Email and Password. Register adds Full Name and Bio, and its button says Create Account.

Login and Register share the row in two equal halves. Login is selected and shows Email, Password and a Login button. Register adds Full Name and Bio, and its button says Create Account.

Use two tabs when sign-in and sign-up should share one box. A Dialog works if each form should open on its own. Base UI in React does not submit either form.

Preview

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.
Use email insteadNo credit card

Anatomy

Tabs anatomy: root, list, indicator, trigger, content

Usage

<Tabs className="w-full max-w-md" defaultValue="login">
  <TabsList className="grid w-full grid-cols-2">
    <TabsTrigger value="login">Login</TabsTrigger>
    <TabsTrigger value="register">Register</TabsTrigger>
  </TabsList>
  <TabsContent value="login">
    <div className="rounded-lg border bg-card p-6 text-card-foreground shadow-sm">
      <div className="mb-4">
        <h3 className="font-semibold text-lg">Login</h3>
        <p className="text-muted-foreground text-sm">
          Enter your credentials to access your account.
        </p>
      </div>
      <form onSubmit={handleSubmit}>
        <FieldGroup className="gap-4">
          <Field>
            <FieldLabel htmlFor="login-email">Email</FieldLabel>
            <Input
              id="login-email"
              placeholder="name@example.com"
              type="email"
            />
          </Field>
          <Field>
            <FieldLabel htmlFor="login-password">Password</FieldLabel>
            <Input id="login-password" type="password" />
          </Field>
          <Button className="w-full" type="submit">
            Sign In
          </Button>
        </FieldGroup>
{/* The rest of the file is under Installation. */}

FAQ

How do Login and Register share the width?

TabsList has grid w-full grid-cols-2, so each trigger takes half. Login is the default, and the group is w-full max-w-md.

Which fields are in each form?

Login has Email and Password. Register has Full Name, Email, Password and Bio. Bio is a Textarea, and the rest are inputs with type set to text, email or password.

How are the labels tied to the inputs?

Each pair sits in a Field with a FieldLabel. Each FieldLabel points at its input's id, such as login-email and register-bio.

What happens on submit?

Both forms call one handler that prevents the default submit, so the page does not reload. Sign In and Create Account are full-width submit buttons. Read the form data and send it in that handler.

Do typed values survive a switch to the other tab?

No. Inactive panels unmount, so the inputs reset when you come back. Set keepMounted on each TabsContent to keep what was typed.

Why does the register panel say Create Account?

The trigger says Register, and the panel heading says Create Account with a line asking for the details. Login's heading matches its trigger.

API Reference

Tabs

Sets defaultValue="login", value="login" and value="register". The rest is on the Tabs page.

Field

Sets htmlFor="login-email", htmlFor="login-password", htmlFor="register-name", htmlFor="register-email", htmlFor="register-password" and htmlFor="register-bio". The rest is on the Field page.

Input

Sets id="login-email", placeholder="name@example.com", type="email", id="login-password", type="password" and id="register-name". The rest is on the Input page.

Button

Sets type="submit". The rest is on the Button page.

Textarea

Sets id="register-bio" and placeholder="Tell us about yourself...". The rest is on the Textarea page.