Files
Htmx/docs/Components/Input.md
T
2026-05-05 23:55:26 +05:00

7.0 KiB

Input

A styled single-line text field with an optional label and hint text below it. The workhorse of any form — use it for names, emails, passwords, search queries, or any other short text value.


Quick example

new Input(
    id:          "email",
    name:        "email",
    inputType:   "email",
    placeholder: "you@example.com",
    label:       "Email address")

All the options

public Input(
    string id,
    string name         = "",
    string inputType    = "text",
    string placeholder  = "",
    string label        = "",
    string description  = "",
    string extraClasses = "",
    string hxAttrs      = "")
Parameter What it does
id Element id. Also used by the <label for="..."> so clicking the label focuses the input.
name Form field name — required if you want the value submitted with the form.
inputType HTML type: text, email, password, number, search, tel, url, date, time.
placeholder Greyed-out hint inside the field before the user types.
label Visible text label above the field.
description Small hint text below the field (e.g. "At least 8 characters").
extraClasses Additional Tailwind classes on the <input> element.
hxAttrs Extra HTML attributes appended verbatim. Use for HTMX, min/max, autocomplete, etc.

Real-world examples

Login form fields

new Input(
    id:          "email",
    name:        "email",
    inputType:   "email",
    placeholder: "you@example.com",
    label:       "Email address")

new Input(
    id:          "password",
    name:        "password",
    inputType:   "password",
    placeholder: "••••••••",
    label:       "Password",
    description: "At least 8 characters")

Reading on the server:

public record Command(
    [property: FromForm] string Email,
    [property: FromForm] string Password
);

Live search with HTMX

This fires a GET request 300ms after the user stops typing and swaps the results in:

new Input(
    id:          "search",
    name:        "q",
    inputType:   "search",
    placeholder: "Search...",
    hxAttrs:     """hx-get="/search" hx-target="#results" hx-trigger="keyup changed delay:300ms"""")

Number input with min/max constraints

Extra HTML attributes like min and max can be passed through hxAttrs:

new Input(
    id:        "quantity",
    name:      "qty",
    inputType: "number",
    label:     "Quantity",
    hxAttrs:   """min="1" max="100" step="1"""")

How it works

Input renders a <div> wrapper containing an optional <label>, the <input>, and an optional description <p>. The label and description elements are omitted entirely from the HTML when not provided. The hxAttrs string is appended verbatim inside the <input> tag, so any valid HTML attribute can be passed through it. );


---

## Tips and tricks

- `inputType: "password"` does not add any server-side security — validate and hash passwords in your handler (see [AuthService](../../Htmx.ApiDemo/Data/AuthService.cs)).
- `hxAttrs` is verbatim — you can add HTML attributes like `min`, `max`, `step`, `autocomplete`, `required`, `readonly`, or `aria-*` here.
- For a pre-filled input (edit form), there is no `value` parameter in the constructor — add `value="..."` via `hxAttrs`: `hxAttrs: $"""value="{Html.Encode(existingValue)}" """`.
- For date and time inputs (`inputType: "date"` / `"time"`), the browser renders a native picker — consider `Calendar` or `TimePicker` for a custom-styled experience.
- Pair with `Alert` (destructive variant) or a description to show server-side validation errors beneath the field.
- Pair with `Alert` (destructive variant) or a description to show server-side validation errors beneath the field.

---

## Complete page example

**`Templates/ContactPage.htmx`**
```html
<div class="max-w-md mx-auto py-10">
  <h1 class="text-2xl font-bold mb-6">Contact us</h1>
  $$ErrorAlert$$
  <form method="post" action="/contact">
    $$AntiforgeryToken$$
    <div class="space-y-4 mb-6">
      $$NameField$$
      $$EmailField$$
      $$SubjectField$$
    </div>
    $$SubmitBtn$$
  </form>
</div>

Templates/ContactPage.htmx.cs

namespace Htmx.ApiDemo.Templates;

public sealed class ContactPage : ContactPageBase
{
    private readonly IHtmxComponent _error;
    private readonly IHtmxComponent _name;
    private readonly IHtmxComponent _email;
    private readonly IHtmxComponent _subject;
    private readonly IHtmxComponent _submit;
    private readonly byte[] _afToken;

    public ContactPage(IAntiforgery af, HttpContext ctx, string? errorMessage = null)
    {
        var tokens = af.GetAndStoreTokens(ctx);
        _afToken = $"""<input type="hidden" name="{tokens.FormFieldName}" value="{tokens.RequestToken}">""".ToUtf8Bytes();

        _error   = errorMessage is not null
            ? new Components.Alert(title: "Please fix the errors below", description: errorMessage, variant: "destructive")
            : HtmxEmpty.Instance;
        _name    = new Components.Input(id: "name",    name: "name",    label: "Full name",  placeholder: "Jane Doe");
        _email   = new Components.Input(id: "email",   name: "email",   label: "Email",      placeholder: "you@example.com", inputType: "email");
        _subject = new Components.Input(id: "subject", name: "subject", label: "Subject",    placeholder: "How can we help?");
        _submit  = new Components.Button("Send message", type: "submit");
    }

    protected override void RenderErrorAlert(HtmxRenderContext ctx)     => _error.Render(ctx.Next());
    protected override void RenderAntiforgeryToken(HtmxRenderContext ctx) => ctx.Writer.WriteUtf8(_afToken);
    protected override void RenderNameField(HtmxRenderContext ctx)      => _name.Render(ctx.Next());
    protected override void RenderEmailField(HtmxRenderContext ctx)     => _email.Render(ctx.Next());
    protected override void RenderSubjectField(HtmxRenderContext ctx)   => _subject.Render(ctx.Next());
    protected override void RenderSubmitBtn(HtmxRenderContext ctx)      => _submit.Render(ctx.Next());
}

POST handler

[Handler]
[MapPost("/contact")]
public static partial class PostContactHandler
{
    public record Command(
        [property: FromForm] string Name,
        [property: FromForm] string Email,
        [property: FromForm] string Subject);

    private static Task<IResult> HandleAsync(
        [AsParameters] Command cmd, HttpContext ctx, IAntiforgery af, CancellationToken ct)
    {
        if (string.IsNullOrWhiteSpace(cmd.Name) || string.IsNullOrWhiteSpace(cmd.Email))
        {
            return ctx.WriteHtmxPage(
                new ContactPage(af, ctx, "Name and email are required."), title: "Contact");
        }

        // Send email or persist enquiry…
        return Task.FromResult(Results.Redirect("/contact/thank-you"));
    }
}

AppJsonSerializerContext.cs

[JsonSerializable(typeof(PostContactHandler.Command), TypeInfoPropertyName = "ContactCommand")]