implement
Input

Input

A text field.

Nothing typed yet.
import { Div, signal, Span } from "@implementjs/core";
import { Input } from "@/lib/components/ui/input";
import { Label } from "@/lib/components/ui/label";

export default function InputDemo() {
	const value = signal("");

	return Div(
		{ class: "flex w-full max-w-sm flex-col gap-4" },
		Div(
			{ class: "flex flex-col gap-2" },
			Label({ for: "project" }, "Project name"),
			Input({ id: "project", placeholder: "acme-web", value }),
			Span(
				{ class: "text-sm text-muted-foreground" },
				value.bind((current) => (current === "" ? "Nothing typed yet." : `Typed: ${current}`)),
			),
		),
		Input({ placeholder: "Disabled", disabled: true }),
		Input({ placeholder: "Invalid", "aria-invalid": true }),
	);
}

Installation

npx jsrepo add @implementjs/ui/input

Nothing else comes with it — this one stands alone on @implementjs/core and @implementjs/primitives.

Usage

import { Input } from "@/lib/components/ui/input";

Input({ id: "email", type: "email", placeholder: "you@example.com" });

Every prop goes through to the input, so type, value, placeholder, required, and the rest work as they always do — this only dresses it.

Binding a value

Pass a signal as value to read what is typed:

const email = signal("");

Input({ id: "email", type: "email", value: email });

Labels and errors

Pair it with a label by for and id, or let field arrange the label, the hint, and the error together.

aria-invalid is styled as well as announced — the border and focus ring turn destructive — which is how FieldError marks the control it belongs to.

File inputs

type="file" is styled too: the file: variants give the button the same text and weight as the rest of the field, instead of the browser default.

API Reference

Input

A text field. Every prop goes through, so type, value, and placeholder work as they always do. aria-invalid is styled as well as announced, which is how FieldError marks its control. Renders a Input; extra props are forwarded onto it.