Bootstrap 5 Login Page with Form Validation (Free Code)
This guide builds a complete Bootstrap 5 login page you can copy: a centered sign-in card with email and password fields, form validation, a show-password button, “Remember me”, a forgot-password link, and a sign-up link, in plain HTML with no build step. It then adds a split-screen version with an image, a dark mode toggle, and the attributes that make password managers and screen readers work. Everything uses Bootstrap 5.3.9 from a CDN.
The complete login page
Save this as login.html and open it in a browser:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Sign in</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.9/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.13.2/font/bootstrap-icons.min.css" rel="stylesheet">
</head>
<body class="bg-body-tertiary">
<main class="d-flex align-items-center justify-content-center min-vh-100 p-3">
<div class="card shadow-sm border-0 w-100" style="max-width: 420px;">
<div class="card-body p-4 p-sm-5">
<h1 class="h3 mb-1 fw-semibold text-center">Sign in</h1>
<p class="text-body-secondary text-center mb-4">Welcome back. Enter your details to continue.</p>
<form id="loginForm" class="needs-validation" action="/login" method="post" novalidate>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email"
autocomplete="username" required>
<div class="invalid-feedback">Enter a valid email address.</div>
</div>
<div class="mb-3">
<div class="d-flex justify-content-between">
<label for="password" class="form-label">Password</label>
<a href="/forgot-password" class="small">Forgot password?</a>
</div>
<div class="input-group has-validation">
<input type="password" class="form-control" id="password" name="password"
autocomplete="current-password" minlength="8" required>
<button class="btn btn-outline-secondary" type="button" id="togglePassword" aria-label="Show password">
<i class="bi bi-eye" aria-hidden="true"></i>
</button>
<div class="invalid-feedback">Enter your password (at least 8 characters).</div>
</div>
</div>
<div class="form-check mb-4">
<input class="form-check-input" type="checkbox" id="remember" name="remember">
<label class="form-check-label" for="remember">Remember me</label>
</div>
<button class="btn btn-primary w-100 py-2" type="submit">Sign in</button>
</form>
<p class="text-center text-body-secondary small mt-4 mb-0">
New here? <a href="/signup">Create an account</a>
</p>
</div>
</div>
</main>
<script>
// Validate on submit: show Bootstrap's messages instead of the browser's.
const form = document.getElementById('loginForm');
form.addEventListener('submit', event => {
if (!form.checkValidity()) {
event.preventDefault();
event.stopPropagation();
}
form.classList.add('was-validated');
});
// Show and hide the password.
const password = document.getElementById('password');
const toggle = document.getElementById('togglePassword');
toggle.addEventListener('click', () => {
const show = password.type === 'password';
password.type = show ? 'text' : 'password';
toggle.setAttribute('aria-label', show ? 'Hide password' : 'Show password');
toggle.querySelector('i').className = show ? 'bi bi-eye-slash' : 'bi bi-eye';
});
</script>
</body>
</html>
The page needs no Bootstrap JavaScript, only the CSS and the icon font. The sections below explain each part.
How the centering works
<main class="d-flex align-items-center justify-content-center min-vh-100"> makes the page at least as tall as the screen and centers the card both ways. The card is w-100 with a max-width of 420px, so it fills a phone screen and stays compact on a desktop. p-3 on <main> keeps a margin around the card on small screens. The centering guide covers other ways to do this.
How the validation works
Bootstrap styles validation, and the browser does the checking:
novalidateon the form turns off the browser’s own error bubbles.required,type="email", andminlength="8"set the rules.- On submit, the script calls
checkValidity(). If a field fails, it stops the submit. - Adding
was-validatedto the form shows the.invalid-feedbackmessages and colors the fields red or green.
has-validation on the input group keeps the password field’s corners correct when the message appears. The message must sit inside the input group, right after the button. The forms guide has more validation options, including messages from the server.
Show a “wrong password” error from the server
Client-side checks only catch empty or badly formatted fields. When the server rejects the login, render the page with is-invalid on the field and the server’s message:
<input type="password" class="form-control is-invalid" id="password" name="password"
autocomplete="current-password" required aria-describedby="passwordError">
<div id="passwordError" class="invalid-feedback">Incorrect email or password.</div>
Use the same message for a wrong email and a wrong password, so the form doesn’t reveal which accounts exist. For a general error, an alert above the form also works:
<div class="alert alert-danger" role="alert">Incorrect email or password.</div>
Password manager and accessibility details
autocomplete="username"on the email field andautocomplete="current-password"on the password field let password managers and browsers fill the form. On a sign-up form, useautocomplete="new-password".- Every field has a visible
<label>linked withfor. Placeholders aren’t labels. - The show-password button has an
aria-labelthat changes between “Show password” and “Hide password”, and the icon isaria-hidden. type="email"brings up the email keyboard on phones.
Split-screen login page
For a login page with an image or brand panel beside the form, use the grid. The image hides on phones:
<main class="container-fluid">
<div class="row min-vh-100">
<div class="col-lg-6 d-none d-lg-block p-0">
<img src="login-photo.jpg" alt="" class="w-100 h-100 object-fit-cover">
</div>
<div class="col-lg-6 d-flex align-items-center justify-content-center p-4">
<div class="w-100" style="max-width: 400px;">
<h1 class="h3 mb-4 fw-semibold">Sign in to Acme</h1>
<!-- the same form as above -->
</div>
</div>
</div>
</main>
object-fit-cover fills the left half without stretching the image, and alt="" marks it as decorative. Swap the image for a colored panel with bg-primary text-white and a short product message if you prefer.
Floating labels version
For a more compact form, use floating labels. The placeholder is required for the effect, but isn’t shown:
<div class="form-floating mb-3">
<input type="email" class="form-control" id="floatingEmail" placeholder="name@example.com" autocomplete="username" required>
<label for="floatingEmail">Email address</label>
<div class="invalid-feedback">Enter a valid email address.</div>
</div>
<div class="form-floating mb-3">
<input type="password" class="form-control" id="floatingPassword" placeholder="Password" autocomplete="current-password" required>
<label for="floatingPassword">Password</label>
</div>
Add a dark mode toggle
Bootstrap 5.3 colors switch with data-bs-theme. Because the page uses bg-body-tertiary and text-body-secondary, it works in dark mode without changes. A button that toggles it:
<button class="btn btn-link position-absolute top-0 end-0 m-3" type="button" id="themeToggle" aria-label="Toggle dark mode">
<i class="bi bi-moon-stars" aria-hidden="true"></i>
</button>
<script>
document.getElementById('themeToggle').addEventListener('click', () => {
const html = document.documentElement;
html.dataset.bsTheme = html.dataset.bsTheme === 'dark' ? 'light' : 'dark';
});
</script>
The dark mode guide shows how to remember the choice and follow the system setting.
Social sign-in buttons
Add buttons above the form, with a divider:
<div class="d-grid gap-2 mb-3">
<a href="/auth/google" class="btn btn-outline-secondary"><i class="bi bi-google me-2" aria-hidden="true"></i>Continue with Google</a>
<a href="/auth/github" class="btn btn-outline-secondary"><i class="bi bi-github me-2" aria-hidden="true"></i>Continue with GitHub</a>
</div>
<div class="d-flex align-items-center my-3">
<hr class="flex-grow-1"><span class="px-2 small text-body-secondary">or</span><hr class="flex-grow-1">
</div>
The links point to your server’s sign-in routes for each provider.
Make it match your brand
The page uses only standard classes, so a theme changes it completely. Open the builder, set your primary color, font, and corner radius, then export bootstrap.css and replace the CDN link. The free templates use the same approach for full websites.
Two things the page doesn’t do on its own: it sends the form to /login, which your server must handle over HTTPS, and it doesn’t stop bots. Add rate limiting on the server and, for sign-up forms, the checks in our guide to stopping spam sign-ups.
FAQ
How do I make a login page with Bootstrap 5?
Center a card with d-flex align-items-center justify-content-center min-vh-100, add a form with form-control inputs for email and password, a form-check for “Remember me”, and a btn btn-primary w-100 submit button. Add novalidate and a short script that adds was-validated on submit to show Bootstrap’s validation messages.
How do I add validation to a Bootstrap login form?
Add required, type="email", and minlength to the inputs, novalidate to the form, and an invalid-feedback element after each input. On submit, call form.checkValidity(), stop the submit if it fails, and add the was-validated class to the form.
How do I center a login form in Bootstrap?
Wrap it in an element with d-flex align-items-center justify-content-center min-vh-100, and give the form or card a max-width such as 420px with w-100, so it fills small screens and stays narrow on large ones.
How do I add a show password button in Bootstrap?
Put the password input and a <button class="btn btn-outline-secondary"> in an .input-group, then use a few lines of JavaScript to switch the input’s type between password and text when the button is clicked. Update the button’s aria-label and icon at the same time.
Does a Bootstrap login page need JavaScript?
The layout and styles don’t. JavaScript is only needed for the validation messages on submit and the show-password button. Without it, the browser’s own validation still checks the required fields.
Is a Bootstrap login form secure?
Bootstrap only handles the look of the form. Security depends on your server: send the form over HTTPS with method="post", hash passwords, limit login attempts, and show the same error for a wrong email or password.
Try it in the builder
Make these changes visually with a live preview, then export clean Bootstrap Sass or CSS.
Open the Builder