diff --git a/apps/www/content/docs/components/input-otp.mdx b/apps/www/content/docs/components/input-otp.mdx
index 0e31d1b9b1d..3bd96c36726 100644
--- a/apps/www/content/docs/components/input-otp.mdx
+++ b/apps/www/content/docs/components/input-otp.mdx
@@ -114,24 +114,19 @@ import {
```
```tsx
- (
- <>
-
- {slots.slice(0, 3).map((slot, index) => (
-
- ))}{" "}
-
-
-
- {slots.slice(3).map((slot, index) => (
-
- ))}
-
- >
- )}
-/>
+
+
+
+
+
+
+
+
+
+
+
+
+
```
## Examples
@@ -150,14 +145,12 @@ import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
(
-
- {slots.map((slot, index) => (
-
- ))}{" "}
-
- )}
-/>
+>
+
+
+ {/* ... */}
+
+
```
### Separator
@@ -166,29 +159,27 @@ You can use the `` component to add a separator between the
-```tsx showLineNumbers {4,17}
+```tsx showLineNumbers {4,15}
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
-} from "@/registry/new-york/ui/input-otp"
+} from "@/components/ui/input-otp"
...
- (
-
- {slots.map((slot, index) => (
-
-
- {index !== slots.length - 1 && }
-
- ))}{" "}
-
- )}
-/>
+
+
+
+
+
+
+
+
+
+
+
```
### Controlled
@@ -200,3 +191,80 @@ You can use the `value` and `onChange` props to control the input value.
### Form
+
+## Changelog
+
+### 2024-03-19 Composition
+
+We've made some updates and replaced the render props pattern with composition. Here's how to update your code if you prefer the composition pattern.
+
+
+ **Note:** You are not required to update your code if you are using the
+ `render` prop. It is still supported.
+
+
+
+
+Update to the latest version of `input-otp`.
+
+```bash
+npm install input-otp@latest
+```
+
+Update `input-otp.tsx`
+
+```diff showLineNumbers title="input-otp.tsx" {2,8-11}
+- import { OTPInput, SlotProps } from "input-otp"
++ import { OTPInput, OTPInputContext } from "input-otp"
+
+ const InputOTPSlot = React.forwardRef<
+ React.ElementRef<"div">,
+- SlotProps & React.ComponentPropsWithoutRef<"div">
+- >(({ char, hasFakeCaret, isActive, className, ...props }, ref) => {
++ React.ComponentPropsWithoutRef<"div"> & { index: number }
++ >(({ index, className, ...props }, ref) => {
++ const inputOTPContext = React.useContext(OTPInputContext)
++ const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]
+```
+
+Then replace the `render` prop in your code.
+
+```diff showLineNumbers {2-12}
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+
+
+### 2024-03-19 Disabled
+
+To add a disabled state to the input, update `` as follows:
+
+```tsx showLineNumbers title="input-otp.tsx" {4,7-11}
+const InputOTP = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, containerClassName, ...props }, ref) => (
+
+))
+InputOTP.displayName = "InputOTP"
+```
diff --git a/apps/www/package.json b/apps/www/package.json
index 38c212ce85a..6d65f3c0e92 100644
--- a/apps/www/package.json
+++ b/apps/www/package.json
@@ -58,7 +58,7 @@
"embla-carousel-autoplay": "8.0.0-rc15",
"embla-carousel-react": "8.0.0-rc15",
"geist": "^1.1.0",
- "input-otp": "^1.0.1",
+ "input-otp": "^1.2.2",
"jotai": "^2.1.0",
"lodash.template": "^4.5.0",
"lucide-react": "0.288.0",
diff --git a/apps/www/public/registry/styles/default/input-otp.json b/apps/www/public/registry/styles/default/input-otp.json
index 6be1abeabc9..648ea71421c 100644
--- a/apps/www/public/registry/styles/default/input-otp.json
+++ b/apps/www/public/registry/styles/default/input-otp.json
@@ -6,7 +6,7 @@
"files": [
{
"name": "input-otp.tsx",
- "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { OTPInput, SlotProps } from \"input-otp\"\nimport { Dot } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst InputOTP = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n))\nInputOTP.displayName = \"InputOTP\"\n\nconst InputOTPGroup = React.forwardRef<\n React.ElementRef<\"div\">,\n React.ComponentPropsWithoutRef<\"div\">\n>(({ className, ...props }, ref) => (\n \n))\nInputOTPGroup.displayName = \"InputOTPGroup\"\n\nconst InputOTPSlot = React.forwardRef<\n React.ElementRef<\"div\">,\n SlotProps & React.ComponentPropsWithoutRef<\"div\">\n>(({ char, hasFakeCaret, isActive, className, ...props }, ref) => {\n return (\n \n {char}\n {hasFakeCaret && (\n
\n )}\n
\n )\n})\nInputOTPSlot.displayName = \"InputOTPSlot\"\n\nconst InputOTPSeparator = React.forwardRef<\n React.ElementRef<\"div\">,\n React.ComponentPropsWithoutRef<\"div\">\n>(({ ...props }, ref) => (\n \n \n
\n))\nInputOTPSeparator.displayName = \"InputOTPSeparator\"\n\nexport { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }\n"
+ "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { OTPInput, OTPInputContext } from \"input-otp\"\nimport { Dot } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst InputOTP = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, containerClassName, ...props }, ref) => (\n \n))\nInputOTP.displayName = \"InputOTP\"\n\nconst InputOTPGroup = React.forwardRef<\n React.ElementRef<\"div\">,\n React.ComponentPropsWithoutRef<\"div\">\n>(({ className, ...props }, ref) => (\n \n))\nInputOTPGroup.displayName = \"InputOTPGroup\"\n\nconst InputOTPSlot = React.forwardRef<\n React.ElementRef<\"div\">,\n React.ComponentPropsWithoutRef<\"div\"> & { index: number }\n>(({ index, className, ...props }, ref) => {\n const inputOTPContext = React.useContext(OTPInputContext)\n const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]\n\n return (\n \n {char}\n {hasFakeCaret && (\n
\n )}\n
\n )\n})\nInputOTPSlot.displayName = \"InputOTPSlot\"\n\nconst InputOTPSeparator = React.forwardRef<\n React.ElementRef<\"div\">,\n React.ComponentPropsWithoutRef<\"div\">\n>(({ ...props }, ref) => (\n \n \n
\n))\nInputOTPSeparator.displayName = \"InputOTPSeparator\"\n\nexport { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }\n"
}
],
"type": "components:ui"
diff --git a/apps/www/public/registry/styles/new-york/input-otp.json b/apps/www/public/registry/styles/new-york/input-otp.json
index 8bdb700821b..15b25c76b74 100644
--- a/apps/www/public/registry/styles/new-york/input-otp.json
+++ b/apps/www/public/registry/styles/new-york/input-otp.json
@@ -6,7 +6,7 @@
"files": [
{
"name": "input-otp.tsx",
- "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { DashIcon } from \"@radix-ui/react-icons\"\nimport { OTPInput, SlotProps } from \"input-otp\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst InputOTP = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n))\nInputOTP.displayName = \"InputOTP\"\n\nconst InputOTPGroup = React.forwardRef<\n React.ElementRef<\"div\">,\n React.ComponentPropsWithoutRef<\"div\">\n>(({ className, ...props }, ref) => (\n \n))\nInputOTPGroup.displayName = \"InputOTPGroup\"\n\nconst InputOTPSlot = React.forwardRef<\n React.ElementRef<\"div\">,\n SlotProps & React.ComponentPropsWithoutRef<\"div\">\n>(({ char, hasFakeCaret, isActive, className, ...props }, ref) => {\n return (\n \n {char}\n {hasFakeCaret && (\n
\n )}\n
\n )\n})\nInputOTPSlot.displayName = \"InputOTPSlot\"\n\nconst InputOTPSeparator = React.forwardRef<\n React.ElementRef<\"div\">,\n React.ComponentPropsWithoutRef<\"div\">\n>(({ ...props }, ref) => (\n \n \n
\n))\nInputOTPSeparator.displayName = \"InputOTPSeparator\"\n\nexport { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }\n"
+ "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { DashIcon } from \"@radix-ui/react-icons\"\nimport { OTPInput, OTPInputContext } from \"input-otp\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst InputOTP = React.forwardRef<\n React.ElementRef,\n React.ComponentPropsWithoutRef\n>(({ className, containerClassName, ...props }, ref) => (\n \n))\nInputOTP.displayName = \"InputOTP\"\n\nconst InputOTPGroup = React.forwardRef<\n React.ElementRef<\"div\">,\n React.ComponentPropsWithoutRef<\"div\">\n>(({ className, ...props }, ref) => (\n \n))\nInputOTPGroup.displayName = \"InputOTPGroup\"\n\nconst InputOTPSlot = React.forwardRef<\n React.ElementRef<\"div\">,\n React.ComponentPropsWithoutRef<\"div\"> & { index: number }\n>(({ index, className, ...props }, ref) => {\n const inputOTPContext = React.useContext(OTPInputContext)\n const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]\n\n return (\n \n {char}\n {hasFakeCaret && (\n
\n )}\n
\n )\n})\nInputOTPSlot.displayName = \"InputOTPSlot\"\n\nconst InputOTPSeparator = React.forwardRef<\n React.ElementRef<\"div\">,\n React.ComponentPropsWithoutRef<\"div\">\n>(({ ...props }, ref) => (\n \n \n
\n))\nInputOTPSeparator.displayName = \"InputOTPSeparator\"\n\nexport { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }\n"
}
],
"type": "components:ui"
diff --git a/apps/www/registry/default/example/input-otp-controlled.tsx b/apps/www/registry/default/example/input-otp-controlled.tsx
index 264b212cc63..c5f95cfbfca 100644
--- a/apps/www/registry/default/example/input-otp-controlled.tsx
+++ b/apps/www/registry/default/example/input-otp-controlled.tsx
@@ -17,14 +17,16 @@ export default function InputOTPControlled() {
maxLength={6}
value={value}
onChange={(value) => setValue(value)}
- render={({ slots }) => (
-
- {slots.map((slot, index) => (
-
- ))}{" "}
-
- )}
- />
+ >
+
+
+
+
+
+
+
+
+
{value === "" ? (
<>Enter your one-time password.>
diff --git a/apps/www/registry/default/example/input-otp-demo.tsx b/apps/www/registry/default/example/input-otp-demo.tsx
index f2b8d7fb542..ec19b7db0fa 100644
--- a/apps/www/registry/default/example/input-otp-demo.tsx
+++ b/apps/www/registry/default/example/input-otp-demo.tsx
@@ -7,23 +7,18 @@ import {
export default function InputOTPDemo() {
return (
-
(
- <>
-
- {slots.slice(0, 3).map((slot, index) => (
-
- ))}{" "}
-
-
-
- {slots.slice(3).map((slot, index) => (
-
- ))}
-
- >
- )}
- />
+
+
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/apps/www/registry/default/example/input-otp-form.tsx b/apps/www/registry/default/example/input-otp-form.tsx
index 66b18702143..cc6922f52df 100644
--- a/apps/www/registry/default/example/input-otp-form.tsx
+++ b/apps/www/registry/default/example/input-otp-form.tsx
@@ -56,17 +56,16 @@ export default function InputOTPForm() {
One-Time Password
- (
-
- {slots.map((slot, index) => (
-
- ))}{" "}
-
- )}
- {...field}
- />
+
+
+
+
+
+
+
+
+
+
Please enter the one-time password sent to your phone.
diff --git a/apps/www/registry/default/example/input-otp-pattern.tsx b/apps/www/registry/default/example/input-otp-pattern.tsx
index a9454b0a0ab..87d711b0ecd 100644
--- a/apps/www/registry/default/example/input-otp-pattern.tsx
+++ b/apps/www/registry/default/example/input-otp-pattern.tsx
@@ -8,16 +8,15 @@ import {
export default function InputOTPPattern() {
return (
- (
-
- {slots.map((slot, index) => (
-
- ))}{" "}
-
- )}
- />
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/apps/www/registry/default/example/input-otp-separator.tsx b/apps/www/registry/default/example/input-otp-separator.tsx
index fd8f12c1153..e6e5a2a6fa2 100644
--- a/apps/www/registry/default/example/input-otp-separator.tsx
+++ b/apps/www/registry/default/example/input-otp-separator.tsx
@@ -9,18 +9,21 @@ import {
export default function InputOTPWithSeparator() {
return (
- (
-
- {slots.map((slot, index) => (
-
-
- {index !== slots.length - 1 && }
-
- ))}{" "}
-
- )}
- />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/apps/www/registry/default/ui/input-otp.tsx b/apps/www/registry/default/ui/input-otp.tsx
index a42ca9a1c92..f66fcfa0ddb 100644
--- a/apps/www/registry/default/ui/input-otp.tsx
+++ b/apps/www/registry/default/ui/input-otp.tsx
@@ -1,7 +1,7 @@
"use client"
import * as React from "react"
-import { OTPInput, SlotProps } from "input-otp"
+import { OTPInput, OTPInputContext } from "input-otp"
import { Dot } from "lucide-react"
import { cn } from "@/lib/utils"
@@ -9,10 +9,14 @@ import { cn } from "@/lib/utils"
const InputOTP = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
+>(({ className, containerClassName, ...props }, ref) => (
))
@@ -28,14 +32,17 @@ InputOTPGroup.displayName = "InputOTPGroup"
const InputOTPSlot = React.forwardRef<
React.ElementRef<"div">,
- SlotProps & React.ComponentPropsWithoutRef<"div">
->(({ char, hasFakeCaret, isActive, className, ...props }, ref) => {
+ React.ComponentPropsWithoutRef<"div"> & { index: number }
+>(({ index, className, ...props }, ref) => {
+ const inputOTPContext = React.useContext(OTPInputContext)
+ const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]
+
return (
)}
diff --git a/apps/www/registry/new-york/example/input-otp-controlled.tsx b/apps/www/registry/new-york/example/input-otp-controlled.tsx
index 0293a90a095..9b4eb7330f8 100644
--- a/apps/www/registry/new-york/example/input-otp-controlled.tsx
+++ b/apps/www/registry/new-york/example/input-otp-controlled.tsx
@@ -17,14 +17,16 @@ export default function InputOTPControlled() {
maxLength={6}
value={value}
onChange={(value) => setValue(value)}
- render={({ slots }) => (
-
- {slots.map((slot, index) => (
-
- ))}{" "}
-
- )}
- />
+ >
+
+
+
+
+
+
+
+
+
{value === "" ? (
<>Enter your one-time password.>
diff --git a/apps/www/registry/new-york/example/input-otp-demo.tsx b/apps/www/registry/new-york/example/input-otp-demo.tsx
index bfa24c031a3..bc63ca1c162 100644
--- a/apps/www/registry/new-york/example/input-otp-demo.tsx
+++ b/apps/www/registry/new-york/example/input-otp-demo.tsx
@@ -7,23 +7,18 @@ import {
export default function InputOTPDemo() {
return (
-
(
- <>
-
- {slots.slice(0, 3).map((slot, index) => (
-
- ))}{" "}
-
-
-
- {slots.slice(3).map((slot, index) => (
-
- ))}
-
- >
- )}
- />
+
+
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/apps/www/registry/new-york/example/input-otp-form.tsx b/apps/www/registry/new-york/example/input-otp-form.tsx
index 432216a7a56..2ab26e4739f 100644
--- a/apps/www/registry/new-york/example/input-otp-form.tsx
+++ b/apps/www/registry/new-york/example/input-otp-form.tsx
@@ -56,17 +56,16 @@ export default function InputOTPForm() {
One-Time Password
- (
-
- {slots.map((slot, index) => (
-
- ))}{" "}
-
- )}
- {...field}
- />
+
+
+
+
+
+
+
+
+
+
Please enter the one-time password sent to your phone.
diff --git a/apps/www/registry/new-york/example/input-otp-pattern.tsx b/apps/www/registry/new-york/example/input-otp-pattern.tsx
index 48eb4f5630a..9dc46cab92d 100644
--- a/apps/www/registry/new-york/example/input-otp-pattern.tsx
+++ b/apps/www/registry/new-york/example/input-otp-pattern.tsx
@@ -8,16 +8,15 @@ import {
export default function InputOTPPattern() {
return (
- (
-
- {slots.map((slot, index) => (
-
- ))}{" "}
-
- )}
- />
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/apps/www/registry/new-york/example/input-otp-separator.tsx b/apps/www/registry/new-york/example/input-otp-separator.tsx
index c789f155413..dd652966353 100644
--- a/apps/www/registry/new-york/example/input-otp-separator.tsx
+++ b/apps/www/registry/new-york/example/input-otp-separator.tsx
@@ -9,18 +9,21 @@ import {
export default function InputOTPWithSeparator() {
return (
- (
-
- {slots.map((slot, index) => (
-
-
- {index !== slots.length - 1 && }
-
- ))}{" "}
-
- )}
- />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
)
}
diff --git a/apps/www/registry/new-york/ui/input-otp.tsx b/apps/www/registry/new-york/ui/input-otp.tsx
index 6229e3cb792..84d07209056 100644
--- a/apps/www/registry/new-york/ui/input-otp.tsx
+++ b/apps/www/registry/new-york/ui/input-otp.tsx
@@ -2,17 +2,21 @@
import * as React from "react"
import { DashIcon } from "@radix-ui/react-icons"
-import { OTPInput, SlotProps } from "input-otp"
+import { OTPInput, OTPInputContext } from "input-otp"
import { cn } from "@/lib/utils"
const InputOTP = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
->(({ className, ...props }, ref) => (
+>(({ className, containerClassName, ...props }, ref) => (
))
@@ -28,8 +32,11 @@ InputOTPGroup.displayName = "InputOTPGroup"
const InputOTPSlot = React.forwardRef<
React.ElementRef<"div">,
- SlotProps & React.ComponentPropsWithoutRef<"div">
->(({ char, hasFakeCaret, isActive, className, ...props }, ref) => {
+ React.ComponentPropsWithoutRef<"div"> & { index: number }
+>(({ index, className, ...props }, ref) => {
+ const inputOTPContext = React.useContext(OTPInputContext)
+ const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]
+
return (
)}
diff --git a/apps/www/styles/mdx.css b/apps/www/styles/mdx.css
index 8f7ba69222c..e84b16a56db 100644
--- a/apps/www/styles/mdx.css
+++ b/apps/www/styles/mdx.css
@@ -69,3 +69,7 @@
.mdx > .steps:first-child > h3:first-child {
@apply mt-0;
}
+
+.steps > h3 {
+ @apply mt-8 mb-4 text-base font-semibold;
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5f112a67368..a443cde9866 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -231,8 +231,8 @@ importers:
specifier: ^1.1.0
version: 1.1.0(next@14.0.4)
input-otp:
- specifier: ^1.0.1
- version: 1.0.1(react-dom@18.2.0)(react@18.2.0)
+ specifier: ^1.2.2
+ version: 1.2.2(react-dom@18.2.0)(react@18.2.0)
jotai:
specifier: ^2.1.0
version: 2.1.0(react@18.2.0)
@@ -7615,8 +7615,8 @@ packages:
resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
dev: false
- /input-otp@1.0.1(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-AFMGRsOwXcH7koO+8nnVcJFYEe92tNmRlb2TUKbj9Bpdyc44GaS3LfJam3MdoXQv1jejpMS0+fxJFSCsEDHd9A==}
+ /input-otp@1.2.2(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-9x6UurPuc9Tb+ywWFcFrG4ryvScSmfLyj8D35dl/HNpSr9jZNtWiXufU65kaDHD/KYUop7hDFH+caZCUKdYNsg==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0