bigbullui logobigbullui
Componentform

Register Form

Registration form with name, email, animated password strength and terms consent.

Preview

Live Preview

Installation

Tokens guide →

Add to your project via CLI (zero dependencies, code you own):

npx bigbullui add register-form

Or install the full npm package:

npm install bigbullui

Usage

import { RegisterForm } from "@/components/ui/register-form";

<RegisterForm />

Source code

register-form.tsx

Zero dependencies (only React + utils). Copy and paste directly into your project:

"use client";

import * as React from "react";
import { cn } from "./lib/utils";

export interface RegisterFormProps extends Omit<React.HTMLAttributes<HTMLFormElement>, "onSubmit"> {
  onSubmit?: (data: { name: string; email: string; password: string }) => void;
  termsHref?: string;
}

/** Kayıt formu: ad/e-posta/şifre + şartlar onayı + canlı doğrulama. */
export function RegisterForm({ onSubmit, termsHref = "#", className, ...props }: RegisterFormProps) {
  const [name, setName] = React.useState("");
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");
  const [agreed, setAgreed] = React.useState(false);
  const [touched, setTouched] = React.useState<Record<string, boolean>>({});

  const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  const strength = password.length === 0 ? 0 : password.length < 6 ? 1 : password.length < 10 ? 2 : /[A-Z]/.test(password) && /[0-9]/.test(password) ? 3 : 2;
  const strengthLabels = ["", "ZAYIF", "ORTA", "GÜÇLÜ"];
  const strengthColors = ["", "bg-destructive", "bg-amber-500", "bg-emerald-500"];

  const valid = name.trim().length >= 2 && emailValid && password.length >= 6 && agreed;

  return (
    <form
      onSubmit={(e) => { e.preventDefault(); if (valid) onSubmit?.({ name, email, password }); }}
      className={cn("w-full max-w-sm space-y-4", className)}
      noValidate
      {...props}
    >
      <style>{`@keyframes regIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }`}</style>

      <div className="space-y-1.5 animate-[regIn_0.3s_ease-out_both] motion-reduce:animate-none">
        <label htmlFor="reg-name" className="font-mono text-[10px] uppercase tracking-[0.15em] text-muted-foreground">Ad Soyad</label>
        <input
          id="reg-name"
          value={name}
          onChange={(e) => setName(e.target.value)}
          onBlur={() => setTouched((t) => ({ ...t, name: true }))}
          className={cn(
            "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors motion-reduce:transition-none",
            touched.name && name.trim().length < 2 && "border-destructive"
          )}
          placeholder="Ada Lovelace"
        />
      </div>

      <div className="space-y-1.5 animate-[regIn_0.3s_ease-out_0.06s_both] motion-reduce:animate-none">
        <label htmlFor="reg-email" className="font-mono text-[10px] uppercase tracking-[0.15em] text-muted-foreground">E-posta</label>
        <input
          id="reg-email"
          type="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          onBlur={() => setTouched((t) => ({ ...t, email: true }))}
          className={cn(
            "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors motion-reduce:transition-none",
            touched.email && !emailValid && "border-destructive"
          )}
          placeholder="ornek@mail.com"
        />
      </div>

      <div className="space-y-1.5 animate-[regIn_0.3s_ease-out_0.12s_both] motion-reduce:animate-none">
        <label htmlFor="reg-password" className="font-mono text-[10px] uppercase tracking-[0.15em] text-muted-foreground">Şifre</label>
        <input
          id="reg-password"
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors motion-reduce:transition-none"
          placeholder="••••••••"
        />
        {password.length > 0 && (
          <div className="flex items-center gap-2">
            <div className="flex flex-1 gap-1">
              {[1, 2, 3].map((seg) => (
                <div
                  key={seg}
                  className={cn(
                    "h-1 flex-1 rounded-full transition-colors duration-300 motion-reduce:transition-none",
                    strength >= seg ? strengthColors[strength] : "bg-border"
                  )}
                />
              ))}
            </div>
            <span className="font-mono text-[9px] uppercase tracking-wider text-muted-foreground">{strengthLabels[strength]}</span>
          </div>
        )}
      </div>

      <label className="flex cursor-pointer items-start gap-2 text-xs animate-[regIn_0.3s_ease-out_0.18s_both] motion-reduce:animate-none select-none">
        <input type="checkbox" checked={agreed} onChange={(e) => setAgreed(e.target.checked)} className="mt-0.5 size-3.5 accent-accent" />
        <span className="text-muted-foreground">
          <a href={termsHref} className="text-accent hover:underline">Kullanım şartlarını</a> ve gizlilik politikasını okudum, kabul ediyorum.
        </span>
      </label>

      <button
        type="submit"
        disabled={!valid}
        className={cn(
          "w-full rounded-md bg-accent px-4 py-2.5 text-sm font-semibold uppercase tracking-widest text-accent-foreground",
          "transition-all duration-150 hover:bg-accent/90 active:scale-[0.98]",
          "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring motion-reduce:transition-none",
          "disabled:pointer-events-none disabled:opacity-40",
          "animate-[regIn_0.3s_ease-out_0.24s_both] motion-reduce:animate-none"
        )}
      >
        Hesap oluştur
      </button>
    </form>
  );
}

Props

PropTypeDescription
onSubmit(data: { name: string; email: string; password: string }) => void
termsHrefstring