Preview
Design guidance
Tabs provide keyboard mode selection. The draw canvas has an accessible name and a separate clear button; typed mode uses one Input; upload mode exposes a labeled file input and contained image with remove and replace actions. Keep the component inside a labeled form when the signature is required.
Installation
Basic usage
Structures
Signature is the public component. It owns the active mode, the retained values, and upload validation. DrawPanel, TypePanel, and UploadPanel are private helpers inside signature.tsx; compose the component through its props.
Root and mode selection
An optional caller-owned Field.Root groups the signature with its label and validation message. Pass the label through header. Inside Signature, Tabs.Root connects the three mode buttons to their panels. The header comes before the shared signature pad.
Draw panel
DrawPanel captures strokes on a canvas. Its clear action uses IconButton. The root retains the path and its original dimensions so restored ink keeps its shape when the pad resizes.
Type panel
TypePanel contains one Input with variant="underline". It renders typed text in Playwrite Canada, while the placeholder uses the body font. Hover, focus, and invalid states style only the underline. typeLabel supplies the input's accessible name; the visible field label stays caller-owned.
Upload panel
UploadPanel uses its own Field.Root for file validation. A native file input owns file selection. The empty state exposes Choose image. A selected image replaces that action with a contained preview, Replace image, and a remove action named for the file. File errors appear in Field.Error.
Retained values
Switching modes keeps each value. SignatureValue stores draw, optional drawDimensions, type, and upload. Use value with onValueChange for controlled state, or defaultValue for initial values. mode with onModeChange controls selection independently. Required-field validation and its message belong to the caller.
Examples
Draw and clear
Draw inside the dashed outline. Sample strokes and a “Draw here” hint appear while the pad is empty; they are never part of the saved signature. Clear restores that empty state and stays disabled until a stroke is drawn.
Subtle pad
Use variant="subtle" for a filled pad with the shared Input hover, focus and error appearance. Outline remains the default with a dashed border. Signature imports Input.Surface for this boundary; its typed entry keeps the underline Input.
Type signature
Type a name on the signature line. Hover, focus, and invalid states change only its underline. The typed value stays available when another mode is selected.
Upload a signature image
Choose a safe raster image up to 5 MB. The image stays contained in the pad, and Replace image opens the chooser without discarding the current value if the chooser is canceled. SVG, HTML, wrong MIME types, and oversized files are rejected; removing the image revokes only an object URL created by this component.
Saved typed signature
Restore a typed name with defaultValue.type. Typed signatures use Playwrite Canada (Playwrite CA), without guides. The value stays plain text.
Saved image
Restore an uploaded signature with its URL and file metadata. The image keeps its aspect ratio within the pad, with room for replace and remove actions.

Required signature
The caller owns validation and its message. Set aria-invalid on Signature to mark the typed input; compose Field.Error for feedback.
Disabled signature
Show a saved signature while locking mode selection and editing.
Retain the complete value, including drawDimensions, when restoring drawn ink. The pad preserves its shape when resized.
API reference
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "outline" | "subtle" | outline | Pad appearance. Inherits Field.Root when omitted; explicit value wins. Typed entry remains underline. |
defaultMode | "draw" | "type" | "upload" | draw | Initial uncontrolled mode. |
disabled | boolean | false | Locks all modes and actions. |
typeLabel | string | Type your signature | Accessible name for the canonical typed Input; it is not rendered as a duplicate field label. |
accept | string | image/png,image/jpeg,image/webp,image/gif,image/avif | File chooser filter; validation still rejects unsupported or empty images. |
header | ReactNode | Caller-owned header content beside mode tabs. Compose Field.Label for a visible question. | |
mode | 'draw' | 'type' | 'upload' | Active mode. Omit for uncontrolled mode. | |
value | SignatureValue | Controlled draw path and dimensions, typed text, and upload value. | |
defaultValue | Partial<SignatureValue> | Initial values for uncontrolled use. | |
onValueChange | (value, details) => void | Receives changed values and mode/reason details. | |
onModeChange | (mode) => void | Receives mode changes. | |
maxUploadSize | number | 5242880 | Maximum accepted image size in bytes. |
onUploadError | (message) => void | Receives validation failures. |
Native div attributes and a div ref are supported on the root. Labels and business validation belong to the caller.