Signature Pad
The signature pad component allows users to draw handwritten signatures using touch or pointer devices. The signature can be saved as an image or cleared.
Features
- Draw signatures using touch or pointer devices.
- Save the signature as an image.
- Clear the signature.
Installation
To use the signature pad machine in your project, run the following command in your command line:
npm install @zag-js/signature-pad @zag-js/react # or yarn add @zag-js/signature-pad @zag-js/react
npm install @zag-js/signature-pad @zag-js/solid # or yarn add @zag-js/signature-pad @zag-js/solid
npm install @zag-js/signature-pad @zag-js/vue # or yarn add @zag-js/signature-pad @zag-js/vue
npm install @zag-js/signature-pad @zag-js/vue # or yarn add @zag-js/signature-pad @zag-js/vue
This command will install the framework agnostic signature pad logic and the reactive utilities for your framework of choice.
Anatomy
To set up the signature pad correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-part
attribute to help identify them in the DOM.
Usage
First, import the signature pad package into your project
import * as signaturePad from "@zag-js/signature-pad"
The signature pad package exports two key functions:
machine
— The state machine logic for the signature pad widget.connect
— The function that translates the machine's state to JSX attributes and event handlers.
Next, import the required hooks and functions for your framework and use the signature pad machine in your project 🔥
import { normalizeProps, useMachine } from "@zag-js/react" import * as signaturePad from "@zag-js/signature-pad" import { useId, useState } from "react" export function SignaturePad() { const [state, send] = useMachine( signaturePad.machine({ id: useId(), }), ) const api = signaturePad.connect(state, send, normalizeProps) return ( <div {...api.rootProps}> <label {...api.labelProps}>Signature Pad</label> <div {...api.controlProps}> <svg {...api.segmentProps}> {api.paths.map((path, i) => ( <path key={i} {...api.getSegmentPathProps({ path })} /> ))} {api.currentPath && ( <path {...api.getSegmentPathProps({ path: api.currentPath })} /> )} </svg> <button {...api.clearTriggerProps}>X</button> <div {...api.guideProps} /> </div> </div> ) }
import * as signaturePad from "@zag-js/signature-pad" import { useMachine, normalizeProps } from "@zag-js/solid" import { createMemo, createUniqueId } from "solid-js" export function SignaturePad() { const [state, send] = useMachine( signaturePad.machine({ id: createUniqueId(), }), ) const api = createMemo(() => signaturePad.connect(state, send, normalizeProps), ) return ( <div {...api().rootProps}> <label {...api().labelProps}>Signature Pad</label> <div {...api().controlProps}> <svg {...api().segmentProps}> {api().paths.map((path, i) => ( <path key={i} {...api().getSegmentPathProps({ path })} /> ))} {api().currentPath && ( <path {...api().getSegmentPathProps({ path: api().currentPath })} /> )} </svg> <button {...api().clearTriggerProps}>X</button> <div {...api().guideProps} /> </div> </div> ) }
import * as signaturePad from "@zag-js/signature-pad" import { useMachine, normalizeProps } from "@zag-js/vue" import { computed, defineComponent } from "vue" export const SignaturePad = defineComponent({ name: "Signature-Pad", setup() { const [state, send] = useMachine(signaturePad.machine({ id: "1" })) const apiRef = computed(() => signaturePad.connect(state.value, send, normalizeProps), ) return () => { const api = apiRef.value return ( <div {...api.rootProps}> <label {...api.labelProps}>Signature Pad</label> <div {...api.controlProps}> <svg {...api.segmentProps}> {api.paths.map((path, i) => ( <path key={i} {...api.getSegmentPathProps({ path })} /> ))} {api.currentPath && ( <path {...api.getSegmentPathProps({ path: api.currentPath })} /> )} </svg> <button {...api.clearTriggerProps}>X</button> <div {...api.guideProps} /> </div> </div> ) } }, })
<script setup> import * as signaturePad from "@zag-js/signature-pad" import { useMachine, normalizeProps } from "@zag-js/vue" import { computed } from "vue" const [state, send] = useMachine(signaturePad.machine({ id: "1" })) const api = computed(() => signaturePad.connect(state.value, send, normalizeProps), ) </script> <template> <div v-bind="api.rootProps"> <label v-bind="api.labelProps">Signature Pad</label> <div v-bind="api.controlProps"> <svg v-bind="api.segmentProps"> <path v-for="(path, i) of api.paths" :key="i" v-bind="api.getSegmentPathProps({ path })" /> <path v-if="api.currentPath" v-bind="api.getSegmentPathProps({ path: api.currentPath })" /> </svg> <button v-bind="api.clearTriggerProps">X</button> <div v-bind="api.guideProps" /> </div> </div> </template>
Listening to drawing events
The signature pad component emits the following events:
onDraw
: Emitted when the user is drawing the signature.onDrawEnd
: Emitted when the user stops drawing the signature.
const [state, send] = useMachine( signature.machine({ onDraw(details) { // details => { path: string[] } console.log("Drawing signature", details) }, onDrawEnd(details) { // details => { path: string[], toDataURL: () => string } console.log("Signature drawn", details) }, }), )
Clearing the signature
To clear the signature, use the api.clear()
, or render the clear trigger
button.
<button onClick={() => api.clear()}>Clear</button>
Rendering an image preview
Use the api.getDataUrl()
method to get the signature as a data URL and render
it as an image.
You can also leverage the
onDrawEnd
event to get the signature data URL.
const [state, send] = useMachine( signature.machine({ onDrawEnd(details) { details.getDataUrl("image/png").then((url) => { // set the image URL in local state setImageURL(url) }) }, }), )
Next, render the image preview using the URL.
<img src={imageURL} alt="Signature" />
Changing the stroke color
To change the stroke color, set the drawing.fill
option to a valid CSS color.
Note: You can't use a css variable as the stroke color.
const [state, send] = useMachine( signature.machine({ drawing: { fill: "red", }, }), )
Changing the stroke width
To change the stroke width, set the drawing.size
option to a number.
const [state, send] = useMachine( signature.machine({ drawing: { size: 5, }, }), )
Simulating pressure sensitivity
Pressure sensitivity is disabled by default. To enable it, set the
drawing.simulatePressure
option to true
.
const [state, send] = useMachine( signature.machine({ drawing: { simulatePressure: true, }, }), )
Usage in forms
To use the signature pad in a form, set the name
context property.
const [state, send] = useMachine( signature.machine({ name: "signature", }), )
Then, render the hidden input element using api.getHiddenInputProps
.
<input {...api.getHiddenInputProps({ value: imageURL })} />
Disabling the signature pad
Set the disabled
context property to true
to disable the signature pad.
const [state, send] = useMachine( signature.machine({ disabled: true, }), )
Making the signature pad read-only
Set the readOnly
context property to true
to make the signature pad
read-only.
const [state, send] = useMachine( signature.machine({ readOnly: true, }), )
Methods and Properties
The signature pad api
exposes the following methods and properties:
Machine Context
The signature pad machine exposes the following context properties:
ids
Partial<{ root: string; control: string; hiddenInput: string; }>
The ids of the signature pad elements. Useful for composition.onDraw
(details: DrawDetails) => void
Callback when the signature pad is drawing.onDrawEnd
(details: DrawEndDetails) => void
Callback when the signature pad is done drawing.drawing
DrawingOptions
The drawing options.disabled
boolean
Whether the signature pad is disabled.readOnly
boolean
Whether the signature pad is read-only.name
string
The name of the signature pad. Useful for form submission.dir
"ltr" | "rtl"
The document's text/writing direction.id
string
The unique identifier of the machine.getRootNode
() => ShadowRoot | Node | Document
A root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.
Machine API
The signature pad api
exposes the following methods:
empty
boolean
Whether the signature pad is empty.drawing
boolean
Whether the user is currently drawing.currentPath
string
The current path being drawn.paths
string[]
The paths of the signature pad.getDataUrl
(type: DataUrlType, quality?: number) => Promise<string>
Returns the data URL of the signature pad.clear
() => void
Clears the signature pad.
Data Attributes
Edit this page on GitHub