Inputs
Use these input components to create things like search bars with authentication forms, credit card inputs, and other form inputs with combined input and label elements. Alpine.js handles the frontend interactivity, while HTMX queries the backend.
Phone number with mask
Requires Alpine.js
<!--
This example requires the mask Alpine.js plugin.
-->
<div class="mx-auto max-w-xs">
<label for="phone" class="block pl-0.5 text-sm font-medium text-slate-900">Phone number</label>
<div class="mt-1.5 rounded-md shadow-sm">
<input type="text"
name="phone"
id="phone"
class="block w-full rounded-md border-0 py-1.5 px-2 text-slate-900 ring-1 ring-inset ring-slate-300 placeholder:text-slate-400 focus:ring-inset focus:ring-blue-600 sm:text-sm"
x-mask="(999) 999-9999"
placeholder="(555) 555-1234">
</div>
</div>
# no python is needed for this component
<div x-cloak x-data="{ error: false }" class="mx-auto max-w-xs">
<label for="email" class="block pl-0.5 text-sm font-medium text-slate-900">Email</label>
<div class="mt-1.5 rounded-md shadow-sm">
<input hx-post="{% url 'htmx:validate_email' %}"
hx-target="#emailError"
required
type="email"
name="email"
id="email"
class="block w-full rounded-md py-1.5 px-2 text-slate-900 ring-1 ring-inset placeholder:text-slate-400 focus:ring-inset focus:ring-blue-600 sm:text-sm"
:class="error ? 'ring-red-600' : 'ring-slate-300'"
placeholder="name@example.com">
</div>
<div @htmx:after-swap="error = $refs.emailError.innerHTML != ''"
id="emailError"
x-ref="emailError"
class="pl-0.5 h-5 text-sm text-red-600"></div>
</div>
### urls.py
from django.urls import path
from . import views
app_name = "htmx"
urlpatterns = [
path("htmx/validate-email/", views.validate_email, name="validate_email"),
]
### views.py
from django.core.exceptions import ValidationError
from django.core.validators import validate_email as email_validator
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
@csrf_exempt
@require_POST
def validate_email(request):
email = request.POST["email"]
try:
email_validator(email)
return HttpResponse("")
except ValidationError:
return HttpResponse("Invalid email")
Password with show/hide toggle
Requires Alpine.js
<div x-cloak x-data="{ showPassword: false }" class="mx-auto max-w-xs">
<label for="password"
class="block pl-0.5 text-sm font-medium text-slate-900">Password</label>
<div class="relative mt-1.5 rounded-md shadow-sm">
<input :type="showPassword ? 'text' : 'password'"
name="password"
id="password"
class="block w-full rounded-md border-0 py-1.5 px-2 text-slate-900 ring-1 ring-inset ring-slate-300 placeholder:text-slate-400 focus:ring-inset focus:ring-blue-600 sm:text-sm"
placeholder="••••••••">
<button type="button"
@click="showPassword = !showPassword"
class="absolute right-2.5 top-1/2 -translate-y-1/2 text-slate-600"
aria-label="Show password">
<svg x-show="!showPassword"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
aria-hidden="true"
class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
<svg x-show="showPassword"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
aria-hidden="true"
class="size-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" />
</svg>
</button>
</div>
</div>
# no python is needed for this component
Password with requirements
Requires Alpine.js
Minimum 8 characters
At least one capital letter
At least one special character (#!$%&)
<div x-data="{ password: '' }" class="mx-auto max-w-xs">
<label for="password"
class="block pl-0.5 text-sm font-medium text-slate-900">Password</label>
<div class="mt-1.5 rounded-md shadow-sm">
<input type="password"
name="password"
id="password"
x-model="password"
class="block w-full rounded-md border-0 py-1.5 px-2 text-slate-900 ring-1 ring-inset ring-slate-300 placeholder:text-slate-400 focus:ring-inset focus:ring-blue-600 sm:text-sm"
placeholder="••••••••">
</div>
<div class="mt-2">
<div class="flex flex-row items-center gap-x-1 text-sm/6"
:class="password.length >= 8 ? 'text-green-600' : 'text-slate-500'">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
Minimum 8 characters
</div>
<div class="flex flex-row items-center gap-x-1 text-sm/6"
:class="password !== password.toLowerCase() ? 'text-green-600' : 'text-slate-500'">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
At least one capital letter
</div>
<div class="flex flex-row items-center gap-x-1 text-sm/6"
:class="['!', '#', '$', '%', '&'].some(el => password.includes(el)) ? 'text-green-600' : 'text-slate-500'">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
At least one special character (#!$%&)
</div>
</div>
</div>
# no python is needed for this component