diff --git a/js/react/lib/components/input/index.ts b/js/react/lib/components/input/index.ts
new file mode 100644
index 0000000..f144060
--- /dev/null
+++ b/js/react/lib/components/input/index.ts
@@ -0,0 +1,2 @@
+export * from "./input";
+export * from "./input.types";
diff --git a/js/react/lib/components/input/input.tsx b/js/react/lib/components/input/input.tsx
new file mode 100644
index 0000000..2a701d4
--- /dev/null
+++ b/js/react/lib/components/input/input.tsx
@@ -0,0 +1,33 @@
+import clsx from "clsx";
+import { InputFieldProps } from "./input.types";
+
+export function Input({
+ errorMessage,
+ hasError = !!errorMessage,
+ icon,
+ disabled,
+ className,
+ ...props
+}: InputFieldProps) {
+ return (
+
+
+ {icon && {icon}}
+
+
+ {hasError && errorMessage && (
+
{errorMessage}
+ )}
+
+ );
+}
diff --git a/js/react/lib/components/input/input.types.ts b/js/react/lib/components/input/input.types.ts
new file mode 100644
index 0000000..a747e26
--- /dev/null
+++ b/js/react/lib/components/input/input.types.ts
@@ -0,0 +1,7 @@
+import { InputHTMLAttributes, ReactNode } from "react";
+
+export interface InputFieldProps extends InputHTMLAttributes {
+ hasError?: boolean;
+ errorMessage?: string;
+ icon?: ReactNode;
+}
diff --git a/js/react/lib/index.ts b/js/react/lib/index.ts
index 501163c..6f9ff49 100644
--- a/js/react/lib/index.ts
+++ b/js/react/lib/index.ts
@@ -5,6 +5,7 @@ export * from "./components/contact-form";
export * from "./components/chip";
export * from "./components/tag";
export * from "./components/flap";
+export * from "./components/input";
export * from "./components/level";
export * from "./components/avatar";
export * from "./components/collaborators";
diff --git a/js/react/showcase/App.tsx b/js/react/showcase/App.tsx
index 6872002..973d45e 100644
--- a/js/react/showcase/App.tsx
+++ b/js/react/showcase/App.tsx
@@ -5,6 +5,8 @@ import {
Github,
Tag,
Telegram,
+ Input,
+ Location,
Flap,
Chip,
Level,
@@ -524,6 +526,31 @@ export function App() {
+
+
+ } placeholder="Input" />
+
:active),
+ &:has(> :focus) {
+ @apply border-primary-500;
+ }
+
+ @variant has-disabled {
+ @apply cursor-not-allowed border-neutral-400 bg-neutral-100 text-neutral-600;
+ @apply dark:bg-neutral-900 dark:text-neutral-400;
+ }
+ }
+
+ .rustlanges-input--error {
+ @apply border-error-600;
+ }
+
+ .rustlanges-input__container {
+ @apply flex flex-col gap-1;
+ }
+
+ .rustlanges-input__error {
+ @apply text-error-800 dark:text-error-300 mt-1 text-sm;
+ }
+
+ .rustlanges-input__inner {
+ @apply w-full bg-transparent outline-none;
+ @apply placeholder:text-neutral-600 dark:placeholder:text-neutral-400;
+ @apply disabled:pointer-events-none disabled:placeholder:text-neutral-400 dark:disabled:placeholder:text-neutral-600;
+ }
+
+ .rustlanges-input__icon {
+ @apply text-neutral-600;
+
+ &:has(+ :disabled) {
+ @apply text-neutral-400;
+ }
+ }
+}