---
title: Input OTP
description: Accessible one-time password component with copy paste functionality.
---

```templ
package examples

import "github.com/axadrn/shadcn-templ/v2/components/inputotp"

templ InputOTPDemo() {
	@inputotp.InputOTP(inputotp.Props{Value: "123456"}) {
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 0})
			@inputotp.Slot(inputotp.SlotProps{Index: 1})
			@inputotp.Slot(inputotp.SlotProps{Index: 2})
			@inputotp.Slot(inputotp.SlotProps{Index: 3})
			@inputotp.Slot(inputotp.SlotProps{Index: 4})
			@inputotp.Slot(inputotp.SlotProps{Index: 5})
		}
	}
}
```

## About

The `InputOTP` component is a native templ and vanilla JavaScript implementation with no external dependencies.

## Installation

<CodeTabs>

<TabsList>
  <TabsTrigger value="cli">Command</TabsTrigger>
  <TabsTrigger value="manual">Manual</TabsTrigger>
</TabsList>
<TabsContent value="cli">

```bash
shadcn-templ add input-otp
```

</TabsContent>

<TabsContent value="manual">

<Steps className="mb-0 pt-2">

<Step>Copy and paste the following code into your project.</Step>

<ComponentSource name="input-otp" title="components/inputotp/inputotp.templ" />

<ComponentSource name="input-otp" title="components/inputotp/inputotp.js" />

Component scripts are loaded through the shared script bundle, see [JavaScript](/docs/installation#javascript).

<Step>Update the import paths to match your project setup.</Step>

</Steps>

</TabsContent>

</CodeTabs>

## Usage

```go showLineNumbers
import "github.com/axadrn/shadcn-templ/v2/components/inputotp"
```

```templ showLineNumbers
@inputotp.InputOTP() {
	@inputotp.Group() {
		@inputotp.Slot(inputotp.SlotProps{Index: 0})
		@inputotp.Slot(inputotp.SlotProps{Index: 1})
		@inputotp.Slot(inputotp.SlotProps{Index: 2})
	}
	@inputotp.Separator()
	@inputotp.Group() {
		@inputotp.Slot(inputotp.SlotProps{Index: 3})
		@inputotp.Slot(inputotp.SlotProps{Index: 4})
		@inputotp.Slot(inputotp.SlotProps{Index: 5})
	}
}
```

## Composition

Use the following composition to build an `InputOTP`:

```text
inputotp.InputOTP
├── inputotp.Group
│   ├── inputotp.Slot
│   ├── inputotp.Slot
│   └── inputotp.Slot
├── inputotp.Separator
├── inputotp.Group
│   ├── inputotp.Slot
│   ├── inputotp.Slot
│   └── inputotp.Slot
├── inputotp.Separator
└── inputotp.Group
    ├── inputotp.Slot
    └── inputotp.Slot
```

## Pattern

Use the `Pattern` prop to define a custom pattern for the OTP input.

```templ showLineNumbers {2}
@inputotp.InputOTP(inputotp.Props{
	Pattern: inputotp.RegexpOnlyDigitsAndChars,
}) {
	// ...
}
```

```templ
package examples

import (
	"github.com/axadrn/shadcn-templ/v2/components/field"
	"github.com/axadrn/shadcn-templ/v2/components/inputotp"
)

templ InputOTPPattern() {
	@field.Field(field.Props{Class: "w-fit"}) {
		@field.Label(field.LabelProps{For: "digits-only"}) {
			Digits Only
		}
		@inputotp.InputOTP(inputotp.Props{ID: "digits-only", Pattern: inputotp.RegexpOnlyDigits}) {
			@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 0})
			@inputotp.Slot(inputotp.SlotProps{Index: 1})
			@inputotp.Slot(inputotp.SlotProps{Index: 2})
			@inputotp.Slot(inputotp.SlotProps{Index: 3})
			@inputotp.Slot(inputotp.SlotProps{Index: 4})
			@inputotp.Slot(inputotp.SlotProps{Index: 5})
			}
		}
	}
}
```

## Separator

Use the `inputotp.Separator` component to add a separator between input groups.

```templ
package examples

import "github.com/axadrn/shadcn-templ/v2/components/inputotp"

templ InputOTPSeparatorExample() {
	@inputotp.InputOTP() {
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 0})
			@inputotp.Slot(inputotp.SlotProps{Index: 1})
		}
		@inputotp.Separator()
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 2})
			@inputotp.Slot(inputotp.SlotProps{Index: 3})
		}
		@inputotp.Separator()
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 4})
			@inputotp.Slot(inputotp.SlotProps{Index: 5})
		}
	}
}
```

## Disabled

Use the `Disabled` prop to disable the input.

```templ
package examples

import "github.com/axadrn/shadcn-templ/v2/components/inputotp"

templ InputOTPDisabled() {
	@inputotp.InputOTP(inputotp.Props{ID: "disabled", Disabled: true, Value: "123456"}) {
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 0})
			@inputotp.Slot(inputotp.SlotProps{Index: 1})
			@inputotp.Slot(inputotp.SlotProps{Index: 2})
		}
		@inputotp.Separator()
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 3})
			@inputotp.Slot(inputotp.SlotProps{Index: 4})
			@inputotp.Slot(inputotp.SlotProps{Index: 5})
		}
	}
}
```

## Controlled

Use the `Value` prop and the hidden input's `change` event to control the input value.

```templ
package examples

import "github.com/axadrn/shadcn-templ/v2/components/inputotp"

// The vanilla pendant of the demo's React state, the real input's input
// event carries the combined value.
templ InputOTPControlled() {
	<div class="space-y-2" data-otp-controlled-demo>
		@inputotp.InputOTP() {
			@inputotp.Group() {
				@inputotp.Slot(inputotp.SlotProps{Index: 0})
				@inputotp.Slot(inputotp.SlotProps{Index: 1})
				@inputotp.Slot(inputotp.SlotProps{Index: 2})
				@inputotp.Slot(inputotp.SlotProps{Index: 3})
				@inputotp.Slot(inputotp.SlotProps{Index: 4})
				@inputotp.Slot(inputotp.SlotProps{Index: 5})
			}
		}
		<div class="text-center text-sm" data-otp-status>Enter your one-time password.</div>
	</div>
	<script nonce={ templ.GetNonce(ctx) }>
		document.addEventListener('input', (e) => {
			const demo = e.target.closest?.('[data-otp-controlled-demo]');
			if (!demo || !e.target.hasAttribute('data-tui-inputotp-input')) return;
			const value = e.target.value;
			demo.querySelector('[data-otp-status]').textContent =
				value === '' ? 'Enter your one-time password.' : 'You entered: ' + value;
		});
	</script>
}
```

## Invalid

Use `aria-invalid` on the slots to show an error state.

```templ
package examples

import "github.com/axadrn/shadcn-templ/v2/components/inputotp"

templ InputOTPInvalid() {
	@inputotp.InputOTP(inputotp.Props{Value: "000000"}) {
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 0, Attributes: templ.Attributes{"aria-invalid": "true"}})
			@inputotp.Slot(inputotp.SlotProps{Index: 1, Attributes: templ.Attributes{"aria-invalid": "true"}})
		}
		@inputotp.Separator()
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 2, Attributes: templ.Attributes{"aria-invalid": "true"}})
			@inputotp.Slot(inputotp.SlotProps{Index: 3, Attributes: templ.Attributes{"aria-invalid": "true"}})
		}
		@inputotp.Separator()
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 4, Attributes: templ.Attributes{"aria-invalid": "true"}})
			@inputotp.Slot(inputotp.SlotProps{Index: 5, Attributes: templ.Attributes{"aria-invalid": "true"}})
		}
	}
}
```

## Four Digits

A common pattern for PIN codes. This uses the `Pattern: inputotp.RegexpOnlyDigits` prop.

```templ
package examples

import "github.com/axadrn/shadcn-templ/v2/components/inputotp"

templ InputOTPFourDigits() {
	@inputotp.InputOTP(inputotp.Props{Pattern: inputotp.RegexpOnlyDigits}) {
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 0})
			@inputotp.Slot(inputotp.SlotProps{Index: 1})
			@inputotp.Slot(inputotp.SlotProps{Index: 2})
			@inputotp.Slot(inputotp.SlotProps{Index: 3})
		}
	}
}
```

## Alphanumeric

Use `inputotp.RegexpOnlyDigitsAndChars` to accept both letters and numbers.

```templ
package examples

import "github.com/axadrn/shadcn-templ/v2/components/inputotp"

templ InputOTPAlphanumeric() {
	@inputotp.InputOTP(inputotp.Props{Pattern: inputotp.RegexpOnlyDigitsAndChars}) {
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 0})
			@inputotp.Slot(inputotp.SlotProps{Index: 1})
			@inputotp.Slot(inputotp.SlotProps{Index: 2})
		}
		@inputotp.Separator()
		@inputotp.Group() {
			@inputotp.Slot(inputotp.SlotProps{Index: 3})
			@inputotp.Slot(inputotp.SlotProps{Index: 4})
			@inputotp.Slot(inputotp.SlotProps{Index: 5})
		}
	}
}
```

## Form

```templ
package examples

import (
	"github.com/axadrn/shadcn-templ/v2/components/button"
	"github.com/axadrn/shadcn-templ/v2/components/card"
	"github.com/axadrn/shadcn-templ/v2/components/field"
	"github.com/axadrn/shadcn-templ/v2/components/icon"
	"github.com/axadrn/shadcn-templ/v2/components/inputotp"
)

templ InputOTPForm() {
	@card.Card(card.Props{Class: "mx-auto max-w-md"}) {
		@card.Header() {
			@card.Title() {
				Verify your login
			}
			@card.Description() {
				Enter the verification code we sent to your email address: <span class="font-medium">m@example.com</span>.
			}
		}
		@card.Content() {
			@field.Field() {
				<div class="flex items-center justify-between">
					@field.Label(field.LabelProps{For: "otp-verification"}) {
						Verification code
					}
					@button.Button(button.Props{Variant: button.VariantOutline, Size: button.SizeXs}) {
						@icon.RefreshCw()
						Resend Code
					}
				</div>
				@inputotp.InputOTP(inputotp.Props{ID: "otp-verification", Required: true}) {
					@inputotp.Group(inputotp.GroupProps{Class: "*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl"}) {
						@inputotp.Slot(inputotp.SlotProps{Index: 0})
						@inputotp.Slot(inputotp.SlotProps{Index: 1})
						@inputotp.Slot(inputotp.SlotProps{Index: 2})
					}
					@inputotp.Separator(inputotp.SeparatorProps{Class: "mx-2"})
					@inputotp.Group(inputotp.GroupProps{Class: "*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl"}) {
						@inputotp.Slot(inputotp.SlotProps{Index: 3})
						@inputotp.Slot(inputotp.SlotProps{Index: 4})
						@inputotp.Slot(inputotp.SlotProps{Index: 5})
					}
				}
				@field.Description() {
					<a href="#">I no longer have access to this email address.</a>
				}
			}
		}
		@card.Footer() {
			@field.Field() {
				@button.Button(button.Props{Type: button.TypeSubmit, Class: "w-full"}) {
					Verify
				}
				<div class="text-sm text-muted-foreground">
					Having trouble signing in? <a href="#" class="underline underline-offset-4 transition-colors hover:text-primary">Contact support</a>
				</div>
			}
		}
	}
}
```

## API Reference

### InputOTP

The `InputOTP` component is the root that manages the slots and submits the combined value.

| Prop       | Type     | Default |
| ---------- | -------- | ------- |
| `Value`    | `string` | -       |
| `Name`     | `string` | -       |
| `Form`     | `string` | -       |
| `Pattern`  | `string` | -       |
| `Disabled` | `bool`   | `false` |
| `Class`    | `string` | -       |

### Group

The `inputotp.Group` component groups adjacent slots.

| Prop    | Type     | Default |
| ------- | -------- | ------- |
| `Class` | `string` | -       |

### Slot

The `inputotp.Slot` component is a single character cell.

| Prop          | Type     | Default |
| ------------- | -------- | ------- |
| `Index`       | `int`    | -       |
| `Placeholder` | `string` | -       |
| `Disabled`    | `bool`   | `false` |
| `Class`       | `string` | -       |

### Separator

The `inputotp.Separator` component divides slot groups.

| Prop    | Type     | Default |
| ------- | -------- | ------- |
| `Class` | `string` | -       |
