Input
A text input component with support for labels, descriptions, and error states.
import { Input } from "~/components/input";
export default function InputBasicDemo() {
return <Input label="Username" placeholder="Enter username" />;
}Installation
CLI
Run the following command to add the component to your project:
npx @ark-preset/cli@latest add inputManual
Create the recipe file at src/components/recipes/input.ts:
import { tv, type VariantProps } from "tailwind-variants";
export const inputVariants = tv({
slots: {
root: "grid gap-1.5",
input:
"h-8 rounded-md border border-input bg-background px-2.5 py-1.5 text-base md:text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 disabled:cursor-not-allowed disabled:opacity-50",
description: "text-sm text-muted-foreground",
error: "text-sm text-destructive",
},
variants: {
error: {
true: {
input: "border-destructive focus-visible:ring-destructive",
},
},
},
defaultVariants: {
error: false,
},
});
export type InputVariants = VariantProps<typeof inputVariants>;Create the component directory and files.
src/components/input/input.base.tsx:
import { Field as ArkField } from "@ark-ui/solid/field";
import { splitProps, type Component } from "solid-js";
import { inputVariants, labelVariants, type InputVariants } from "../recipes/input";
const styles = inputVariants();
const InputRoot: Component<ArkField.RootProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkField.Root class={styles.root({ class: local.class })} {...others} />;
};
const InputLabel: Component<ArkField.LabelProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkField.Label class={labelVariants({ class: local.class })} {...others} />;
};
const InputField: Component<ArkField.InputProps & InputVariants> = (props) => {
const [local, others] = splitProps(props, ["class", "error"]);
return (
<ArkField.Input class={styles.input({ class: local.class, error: local.error })} {...others} />
);
};
const InputDescription: Component<ArkField.HelperTextProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkField.HelperText class={styles.description({ class: local.class })} {...others} />;
};
const InputErrorText: Component<ArkField.ErrorTextProps> = (props) => {
const [local, others] = splitProps(props, ["class"]);
return <ArkField.ErrorText class={styles.error({ class: local.class })} {...others} />;
};
export const Input = {
Root: InputRoot,
Label: InputLabel,
Field: InputField,
Description: InputDescription,
ErrorText: InputErrorText,
};src/components/input/index.tsx:
import { Field as ArkField } from "@ark-ui/solid/field";
import { splitProps, type Component } from "solid-js";
import { Input as InputBase } from "./input.base";
import { InputVariants } from "../recipes/input";
type InputProps = {
label?: string;
description?: string;
error?: string;
} & ArkField.InputProps &
Omit<InputVariants, "error">;
const Input: Component<InputProps> = (props) => {
const [local, others] = splitProps(props, ["class", "label", "description", "error", "children"]);
return (
<InputBase.Root class={local.class} invalid={!!local.error}>
{local.label && <InputBase.Label>{local.label}</InputBase.Label>}
<InputBase.Field error={!!local.error} {...others} />
{local.description && !local.error && (
<InputBase.Description>{local.description}</InputBase.Description>
)}
{local.error && <InputBase.ErrorText>{local.error}</InputBase.ErrorText>}
</InputBase.Root>
);
};
export { Input, InputBase };
export { inputVariants, type InputVariants } from "../recipes/input";Note: Make sure your project has the Tailwind CSS theme variables set up (
--background,--foreground,--ring,--border, etc.) or override the utility classes to match your design system.
Usage
Basic Usage
A basic text input field.
import { Input } from "~/components/input";
export default function InputBasicDemo() {
return <Input label="Username" placeholder="Enter username" />;
}With Description
Add a helper description below the input using the description prop.
We'll never share your email.
import { Input } from "~/components/input";
export default function InputDescriptionDemo() {
return (
<Input
label="Email"
description="We'll never share your email."
placeholder="email@example.com"
/>
);
}With Error State
Display an error message below the input using the error prop.
Invalid email address
import { Input } from "~/components/input";
export default function InputErrorDemo() {
return <Input label="Email" error="Invalid email address" type="email" />;
}API Reference
| Prop | Type | Default |
|---|---|---|
| label | string | — |
| description | string | — |
| error | string | — |
| class | string | — |
| (HTML input attrs) | — | — |