# Input Otp Use Cases 4 - Transaction confirmation

> Transaction Confirmation.

A shadcn/ui block from the [@shadcnblocks](https://shadcnblocks.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/shadcnblocks/input-otp-input-otp-use-cases-4).

- Page, with a live preview: https://shadcn.exchange/r/shadcnblocks/input-otp-input-otp-use-cases-4
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/shadcnblocks/input-otp-input-otp-use-cases-4-light-54feabb596240aa5.png
- Registry item: https://shadcnblocks.com/r/input-otp-input-otp-use-cases-4.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://shadcnblocks.com/r/input-otp-input-otp-use-cases-4.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@hookform/resolvers`, `lucide-react`, `react-hook-form`, `sonner`, `zod`
- Registry items: `alert`, `button`, `field`, `input-otp`

## Files

### `example/input-otp/input-otp-use-cases-4.tsx`

```tsx
"use client";

import { zodResolver } from "@hookform/resolvers/zod";
import { AlertTriangle } from "lucide-react";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";

import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button";
import { Controller } from "react-hook-form";

import { Field, FieldError } from "@/components/ui/field";
import {
  InputOTP,
  InputOTPGroup,
  InputOTPSeparator,
  InputOTPSlot,
} from "@/components/ui/input-otp";

export const title = "Transaction Confirmation";

const formSchema = z.object({
  code: z.string().min(6, {
    message: "Confirmation code must be 6 characters.",
  }),
});

const Example = () => {
  const [isConfirming, setIsConfirming] = useState(false);

  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      code: "",
    },
  });

  function onSubmit(_values: z.infer<typeof formSchema>) {
    setIsConfirming(true);
    // Simulate transaction confirmation
    setTimeout(() => {
      setIsConfirming(false);
      toast.success("Transaction confirmed successfully!");
    }, 1500);
  }

  return (
    <div className="w-full max-w-sm space-y-6">
      <div className="space-y-2 text-center">
        <h2 className="text-2xl font-semibold">Confirm Transaction</h2>
        <p className="text-sm text-muted-foreground">
          You're about to send $1,250.00 to John Doe
        </p>
      </div>
      <Alert>
        <AlertTriangle className="h-4 w-4" />
        <AlertTitle>Security Check</AlertTitle>
        <AlertDescription>
          For your security, please enter the confirmation code sent to your
          registered email address.
        </AlertDescription>
      </Alert>
      <form className="space-y-6" onSubmit={form.handleSubmit(onSubmit)}>
        <Controller
          control={form.control}
          name="code"
          render={({ field, fieldState }) => (
            <Field data-invalid={fieldState.invalid}>
              <div className="flex justify-center">
                <InputOTP maxLength={6} {...field}>
                  <InputOTPGroup>
                    <InputOTPSlot className="bg-background" index={0} />
                    <InputOTPSlot className="bg-background" index={1} />
                    <InputOTPSlot className="bg-background" index={2} />
                  </InputOTPGroup>
                  <InputOTPSeparator />
                  <InputOTPGroup>
                    <InputOTPSlot className="bg-background" index={3} />
                    <InputOTPSlot className="bg-background" index={4} />
                    <InputOTPSlot className="bg-background" index={5} />
                  </InputOTPGroup>
                </InputOTP>
              </div>
              {fieldState.invalid && <FieldError errors={[fieldState.error]} />}
            </Field>
          )}
        />
        <div className="flex gap-2">
          <Button
            className="flex-1"
            onClick={() => toast.info("Transaction cancelled")}
            type="button"
            variant="outline"
          >
            Cancel
          </Button>
          <Button className="flex-1" disabled={isConfirming} type="submit">
            {isConfirming ? "Confirming..." : "Confirm Transaction"}
          </Button>
        </div>
      </form>
    </div>
  );
};

export default Example;
```
