Modern Registration Form Component
A sleek, user-friendly registration form designed for modern web applications. It features a translucent, blurred background card effect, real-time client-side validation, password visibility toggles, and a distinct loading state upon submission. After successful submission, the form is replaced by a confirmation message.
LTR
RTL
<section class="relative overflow-hidden min-h-screen bg-white dark:bg-gray-900 flex items-center justify-center py-12">
<!-- Background elements -->
<div class="absolute inset-0 overflow-hidden">
<div class="absolute -top-1/2 -right-1/2 bg-blue-400/20 dark:bg-blue-600/20 blur-3xl w-96 h-96 rounded-full"></div>
<div class="absolute -bottom-1/2 -left-1/2 bg-purple-400/20 dark:bg-purple-600/20 blur-3xl w-96 h-96 rounded-full"></div>
</div>
<div class="relative z-10 mx-auto px-4 w-full max-w-lg">
<!-- Logo/Brand -->
<div class="text-center mb-8">
<h1 class="text-3xl font-bold text-gray-900 leading-tight sm:text-4xl dark:text-white">Create Account</h1>
<p class="mt-2 text-gray-600 dark:text-gray-400">Join us and build the future today</p>
</div>
<!-- Single Page Form Container -->
<div class="bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-2xl shadow-xl p-8 border border-gray-200/50 dark:border-gray-700/50"
x-data="{
submitting: false,
showSuccess: false,
showPassword: false,
showConfirmPassword: false,
formData: {
firstName: '',
lastName: '',
email: '',
phone: '',
password: '',
confirmPassword: '',
terms: false
},
errors: {},
validateField(fieldName, value) {
this.errors[fieldName] = '';
if (fieldName === 'firstName') {
if (!value.trim()) return 'First name is required';
}
if (fieldName === 'lastName') {
if (!value.trim()) return 'Last name is required';
}
if (fieldName === 'email') {
if (!value.trim()) {
return 'Email is required';
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
return 'Please enter a valid email address';
}
}
if (fieldName === 'password') {
if (!value) {
return 'Password is required';
} else if (value.length < 8) {
return 'Password must be at least 8 characters';
}
}
if (fieldName === 'confirmPassword') {
if (value !== this.formData.password) {
return 'Passwords do not match';
}
}
if (fieldName === 'terms') {
if (!value) {
return 'You must accept the terms and conditions';
}
}
return '';
},
validateForm() {
this.errors = {};
let isValid = true;
// Validate all fields
Object.keys(this.formData).forEach(field => {
if (field !== 'phone') {
const error = this.validateField(field, this.formData[field]);
if (error) {
this.errors[field] = error;
isValid = false;
}
}
});
return isValid;
},
async submitForm() {
if (!this.validateForm()) {
// Scroll to first error
const firstError = document.querySelector('.error-input');
if (firstError) {
firstError.scrollIntoView({ behavior: 'smooth', block: 'center' });
firstError.focus();
}
return;
}
this.submitting = true;
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1500));
// Show success message
this.showSuccess = true;
this.submitting = false;
},
resetForm() {
this.formData = {
firstName: '',
lastName: '',
email: '',
phone: '',
password: '',
confirmPassword: '',
terms: false
};
this.errors = {};
this.showSuccess = false;
}
}">
<!-- Registration Form -->
<form @submit.prevent="submitForm" x-show="!showSuccess" x-transition>
<div class="space-y-6">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">Create Your Account</h2>
<!-- Personal Information -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="firstName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">First Name</label>
<input type="text" id="firstName" x-model="formData.firstName"
@blur="errors.firstName = validateField('firstName', formData.firstName)"
:class="errors.firstName ? 'error-input' : ''"
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
placeholder="John">
<div x-show="errors.firstName" x-text="errors.firstName" class="mt-1 text-sm text-red-600 dark:text-red-400"></div>
</div>
<div>
<label for="lastName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Last Name</label>
<input type="text" id="lastName" x-model="formData.lastName"
@blur="errors.lastName = validateField('lastName', formData.lastName)"
:class="errors.lastName ? 'error-input' : ''"
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
placeholder="Doe">
<div x-show="errors.lastName" x-text="errors.lastName" class="mt-1 text-sm text-red-600 dark:text-red-400"></div>
</div>
</div>
<div>
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email Address</label>
<input type="email" id="email" x-model="formData.email"
@blur="errors.email = validateField('email', formData.email)"
:class="errors.email ? 'error-input' : ''"
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
placeholder="you@example.com">
<div x-show="errors.email" x-text="errors.email" class="mt-1 text-sm text-red-600 dark:text-red-400"></div>
</div>
<div>
<label for="phone" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Phone Number (Optional)</label>
<input type="tel" id="phone" x-model="formData.phone"
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200"
placeholder="+1 (555) 123-4567">
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Password</label>
<div class="relative">
<input :type="showPassword ? 'text' : 'password'" id="password" x-model="formData.password"
@blur="errors.password = validateField('password', formData.password)"
:class="errors.password ? 'error-input' : ''"
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200 pr-10"
placeholder="••••••••">
<button type="button" @click="showPassword = !showPassword" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300">
<svg x-show="!showPassword" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
</svg>
<svg x-show="showPassword" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>
</svg>
</button>
</div>
<div x-show="errors.password" x-text="errors.password" class="mt-1 text-sm text-red-600 dark:text-red-400"></div>
</div>
<div>
<label for="confirmPassword" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Confirm Password</label>
<div class="relative">
<input :type="showConfirmPassword ? 'text' : 'password'" id="confirmPassword" x-model="formData.confirmPassword"
@blur="errors.confirmPassword = validateField('confirmPassword', formData.confirmPassword)"
:class="errors.confirmPassword ? 'error-input' : ''"
class="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white focus:outline-none transition-all duration-200 pr-10"
placeholder="••••••••">
<button type="button" @click="showConfirmPassword = !showConfirmPassword" class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300">
<svg x-show="!showConfirmPassword" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
</svg>
<svg x-show="showConfirmPassword" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>
</svg>
</button>
</div>
<div x-show="errors.confirmPassword" x-text="errors.confirmPassword" class="mt-1 text-sm text-red-600 dark:text-red-400"></div>
</div>
<div class="mb-6">
<div class="flex items-start">
<div class="flex items-center h-5">
<input id="terms" x-model="formData.terms" type="checkbox" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
</div>
<label for="terms" class="ml-2 text-sm text-gray-700 dark:text-gray-300">
I agree to the <a href="#" class="text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300">Terms and Conditions</a> and <a href="#" class="text-blue-600 hover:text-blue-500 dark:text-blue-400 dark:hover:text-blue-300">Privacy Policy</a>
</label>
</div>
<div x-show="errors.terms" x-text="errors.terms" class="mt-1 text-sm text-red-600 dark:text-red-400"></div>
</div>
<div class="flex justify-center">
<button type="submit" :disabled="submitting" class="w-full px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 disabled:opacity-70 disabled:cursor-not-allowed transition-colors duration-200 font-medium flex items-center justify-center">
<span x-show="!submitting">Create Account</span>
<span x-show="submitting" class="flex items-center">
<svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Creating Account...
</span>
</button>
</div>
</div>
</form>
<!-- Success Message -->
<div x-show="showSuccess" x-transition:enter="transition ease-out duration-300"
x-transition:enter-start="opacity-0 transform scale-95"
x-transition:enter-end="opacity-100 transform scale-100"
class="text-center py-8">
<div class="mb-4 flex justify-center">
<div class="w-16 h-16 rounded-full bg-green-100 dark:bg-green-900 flex items-center justify-center success-checkmark">
<svg class="w-8 h-8 text-green-600 dark:text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
</svg>
</div>
</div>
<h3 class="text-2xl font-bold text-gray-900 dark:text-white mb-2">Account Created Successfully!</h3>
<p class="text-gray-600 dark:text-gray-400 mb-6">Welcome to our platform! We've sent a confirmation email to your address.</p>
<div class="bg-blue-50 dark:bg-blue-900/20 rounded-lg p-4 mb-6 text-left">
<h4 class="font-medium text-blue-800 dark:text-blue-300 mb-2">Your Account Details:</h4>
<p class="text-sm text-blue-700 dark:text-blue-400" x-text="`Email: ${formData.email}`"></p>
<p class="text-sm text-blue-700 dark:text-blue-400" x-text="`Name: ${formData.firstName} ${formData.lastName}`"></p>
</div>
<div class="flex space-x-4">
<button @click="resetForm" class="flex-1 px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors duration-200 font-medium">
Create Another Account
</button>
<a href="#" class="flex-1 px-4 py-2 bg-blue-600 text-white text-center rounded-lg hover:bg-blue-700 transition-colors duration-200 font-medium">Go to Dashboard</a>
</div>
</div>
</div>
</div>
</section>