Input
Input Examples
Copy-ready examples for a floating-label input with variants, icon slot, and support text.
Basic usage
A simple controlled input using `value` + `onChange`.
• If you pass `value`, treat the component as controlled (always update it in `onChange`).
• If you don't pass `value`, the browser manages the value (uncontrolled).
Live preview
Controlled input
"use client";
import { useState } from "react";
import Input from "@/_components/forms/input/Input";
export default function Example() {
const [value, setValue] = useState(""); // controlled state
return (
<Input
name="email"
label="Email"
type="email"
value={value}
onChange={(e) => setValue((e.target as HTMLInputElement).value)}
placeholder="you@example.com"
/>
);
}Variants
Switch between outlined (default) and filled.
Live preview
Outlined vs filled
"use client";
import { useState } from "react";
import Input from "@/_components/forms/input/Input";
export default function Example() {
const [value, setValue] = useState("Hello");
return (
<div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
<Input
name="outlined"
label="Title"
value={value}
onChange={(e) => setValue((e.target as HTMLInputElement).value)}
/>
<Input
name="filled"
label="Title"
variant="filled"
value={value}
onChange={(e) => setValue((e.target as HTMLInputElement).value)}
/>
</div>
);
}With leading icon
Use `iconContent` to render a leading icon/content inside the field.
• Any ReactNode works for `iconContent` (an icon, badge, loader, etc.).
Live preview
Search input with icon
"use client";
import { useState, useId } from "react";
import Input from "@/_components/forms/input/Input";
import Icon from "@/_components/icon/Icon";
export default function Example() {
const id = useId();
const [q, setQ] = useState("");
return (
<Input
name={`${id}-search`}
label="Search"
type="search"
iconContent={<Icon name="Search" size="small" />}
value={q}
onChange={(e) => setQ((e.target as HTMLInputElement).value)}
placeholder="Search..."
/>
);
}Support text states
Show helper text under the input, with optional styling state.
• `extraTextState` only affects the support text styling (it doesn't validate the input by itself).
• Use it alongside your form validation logic (e.g., show error only after blur/submit).
Live preview
Shown as support text
Looks good
Minimum 8 characters
Support / success / error
import Input from "@/_components/forms/input/Input";
export default function Example() {
return (
<div style={{ display: "grid", gap: 14 }}>
<Input
name="support"
label="Project"
value="cssnt_components"
extraText="Shown as support text"
extraTextState="support"
/>
<Input
name="success"
label="Username"
value="luischvz"
editState="readonly"
extraText="Looks good"
extraTextState="success"
/>
<Input
name="error"
label="Password"
type="password"
required
extraText="Minimum 8 characters"
extraTextState="error"
/>
</div>
);
}Edit state
Disable editing with `editState`.
• `editState="readonly"` sets the native `readOnly` attribute.
• `editState="disabled"` sets the native `disabled` attribute.
Live preview
free / readonly / disabled
import Input from "@/_components/forms/input/Input";
export default function Example() {
return (
<div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
<Input name="free" label="Name" placeholder="John" />
<Input
name="readonly"
label="Readonly"
editState="readonly"
value="Locked"
/>
<Input name="disabled" label="Disabled" editState="disabled" />
</div>
);
}Common input types
Use native HTML input types (email, tel, date, number, etc.).
Live preview
Tip: keep it controlled to normalize empty string.
Types overview
"use client";
import { useId, useState } from "react";
import Input from "@/_components/forms/input/Input";
export default function Example() {
const id = useId();
const [amount, setAmount] = useState<number | "">("");
return (
<div style={{ display: "grid", gap: 14 }}>
<div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
<Input name={`${id}-text`} label="Text" placeholder="Type here" />
<Input name={`${id}-tel`} label="Phone" type="tel" placeholder="+52 55 ..." />
</div>
<div style={{ display: "flex", gap: 12, flexWrap: "wrap" }}>
<Input name={`${id}-url`} label="Website" type="url" placeholder="https://example.com" />
<Input name={`${id}-date`} label="Date" type="date" />
</div>
<Input
name={`${id}-number`}
label="Amount"
type="number"
value={amount}
onChange={(e) => {
const next = (e.target as HTMLInputElement).value;
setAmount(next === """ ? """ : Number(next));
}}
placeholder="0"
extraText="Tip: keep it controlled to normalize empty string."
extraTextState="support"
/>
</div>
);
}On this page
Input Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| name | string | No | — | Used for `id`, `name`, and `htmlFor`. Should be unique per field. |
| label | string | No | — | Floating label content. |
| type | "text" | "password" | "email" | "number" | "search" | "tel" | "url" | "date" | "time" | "datetime-local" | "month" | "week" | No | "text" | Native input type. |
| placeholder | string | No | " " | Placeholder. Defaults to a single space so the floating label logic works reliably. |
| value | string | number | No | — | Input value. If you pass this, treat the input as controlled. |
| onChange | (...args: unknown[]) => unknown | No | — | Change handler forwarded to `<input onChange>`. |
| variant | "filled" | "outlined" | No | "outlined" | Visual variant. |
| iconContent | string | ReactNode | No | — | Optional leading icon/content rendered before the input. |
| required | boolean | No | false | Sets `required` on the input and adds a required label class (asterisk via CSS). |
| editState | "disabled" | "readonly" | "free" | No | "free" | Sets `disabled` or `readOnly` on the input and adds root state classes. |
| extraText | string | No | — | Optional support text rendered under the input. |
| extraTextState | "error" | "success" | "support" | No | — | Support text style class applied to the `<p>` node. |
Components