# Draw or type

> Tabs to draw a signature or type it, the keyboard route to the same outcome.

A shadcn/ui block from the [@reui](https://reui.io) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/reui/c-signature-pad-6).

- Page, with a live preview: https://shadcn.exchange/r/reui/c-signature-pad-6
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/reui/c-signature-pad-6-light-d817f469617e9674.png
- Registry item: https://reui.io/r/new-york-v4/c-signature-pad-6.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://reui.io/r/new-york-v4/c-signature-pad-6.json
```

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

## Dependencies

- Registry items: `@reui/badge`, `button`, `field`, `input`, `@reui/signature-pad`, `tabs`

## Files

### `c-signature-pad-6.tsx`

```tsx
"use client"

import { useId, useState } from "react"
import { Badge } from "@/components/reui/badge"
import {
  SignaturePad,
  SignaturePadArea,
  SignaturePadClear,
  SignaturePadControls,
  SignaturePadGuide,
  SignaturePadPlaceholder,
  type SignaturePadStroke,
} from "@/components/reui/signature-pad"

import { Button } from "@/components/ui/button"
import { Field, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import {
  Tabs,
  TabsContent,
  TabsList,
  TabsTrigger,
} from "@/components/ui/tabs"

export function Pattern() {
  const id = useId()
  const [mode, setMode] = useState<"draw" | "type">("draw")
  const [strokes, setStrokes] = useState<SignaturePadStroke[]>([])
  const [typed, setTyped] = useState("")
  const [adopted, setAdopted] = useState<"draw" | "type" | null>(null)

  /* Typing is the keyboard and screen reader route to the same outcome. */
  const ready = mode === "draw" ? strokes.length > 0 : typed.trim().length > 1

  return (
    <div className="mx-auto flex w-full max-w-md flex-col gap-4">
      <Tabs
        value={mode}
        onValueChange={(value) => setMode(value === "type" ? "type" : "draw")}
      >
        <TabsList>
          <TabsTrigger value="draw">Draw</TabsTrigger>
          <TabsTrigger value="type">Type</TabsTrigger>
        </TabsList>
        <TabsContent value="draw">
          <SignaturePad
            value={strokes}
            onValueChange={(next) => {
              setStrokes(next)
              setAdopted(null)
            }}
          >
            <SignaturePadArea className="h-40">
              <SignaturePadGuide />
              <SignaturePadPlaceholder />
              <SignaturePadControls position="top-end">
                <SignaturePadClear variant="ghost" />
              </SignaturePadControls>
            </SignaturePadArea>
          </SignaturePad>
        </TabsContent>
        <TabsContent value="type">
          <div className="flex flex-col gap-3">
            <Field>
              <FieldLabel htmlFor={`${id}-typed`}>Full name</FieldLabel>
              <Input
                id={`${id}-typed`}
                value={typed}
                onChange={(event) => {
                  setTyped(event.target.value)
                  setAdopted(null)
                }}
                autoComplete="name"
                placeholder="Maya Chen"
              />
            </Field>
            {/* A visual echo of the input, so it stays out of the reading order. */}
            <p
              aria-hidden="true"
              className="flex h-20 items-center justify-center border-b border-dashed px-4"
            >
              {typed.trim() ? (
                <span className="truncate font-serif text-3xl italic">
                  {typed.trim()}
                </span>
              ) : (
                <span className="text-muted-foreground text-sm">
                  Your typed signature appears here
                </span>
              )}
            </p>
          </div>
        </TabsContent>
      </Tabs>
      <div className="flex items-center gap-3">
        <Button disabled={!ready} onClick={() => setAdopted(mode)}>
          Adopt and sign
        </Button>
        <div role="status">
          {adopted && (
            <Badge variant="success-light">
              {adopted === "draw" ? "Drawn" : "Typed"} signature adopted
            </Badge>
          )}
        </div>
      </div>
    </div>
  )
}
```
