- Published on
How to Capture Signature in React applications
- Authors
- Name
- Yinhuan Yuan
Introduction
When developing React applications with Material-UI (MUI), following best practices helps ensure your application is maintainable, performant, and aligned with industry standards. Below are some key best practices to follow when working with MUI in React:
I'll explain several approaches to capture signatures in web applications and provide an example implementation of one of them.
Here are the main approaches for signature capture:
- Canvas-based Solutions:
- Pros: Native browser support, lightweight, customizable
- Cons: Requires more custom code for features
- Best for: Simple signature requirements
- Library-based Solutions:
- SignaturePad.js: Lightweight, popular choice
- Fabric.js: Full featured canvas library
- react-signature-canvas: React-specific solution
- Pros: Ready-made features, good documentation
- Cons: Additional bundle size
- SVG-based Solutions:
- Pros: Scalable, smaller file size
- Cons: More complex to implement
- Best for: High-quality signature requirements
Let me create a simple but functional signature pad implementation using Canvas:
import React, { useRef, useState, useEffect } from 'react'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
const SignaturePad = () => {
const canvasRef = useRef(null)
const [isDrawing, setIsDrawing] = useState(false)
const [context, setContext] = useState(null)
useEffect(() => {
const canvas = canvasRef.current
const ctx = canvas.getContext('2d')
ctx.strokeStyle = '#000000'
ctx.lineWidth = 2
ctx.lineCap = 'round'
setContext(ctx)
// Handle device pixel ratio for crisp lines
const dpr = window.devicePixelRatio || 1
const rect = canvas.getBoundingClientRect()
canvas.width = rect.width * dpr
canvas.height = rect.height * dpr
ctx.scale(dpr, dpr)
canvas.style.width = `${rect.width}px`
canvas.style.height = `${rect.height}px`
}, [])
const startDrawing = (e) => {
const rect = canvasRef.current.getBoundingClientRect()
const x = e.clientX - rect.left
const y = e.clientY - rect.top
context.beginPath()
context.moveTo(x, y)
setIsDrawing(true)
}
const draw = (e) => {
if (!isDrawing) return
const rect = canvasRef.current.getBoundingClientRect()
const x = e.clientX - rect.left
const y = e.clientY - rect.top
context.lineTo(x, y)
context.stroke()
}
const stopDrawing = () => {
setIsDrawing(false)
context.closePath()
}
const clearSignature = () => {
const canvas = canvasRef.current
context.clearRect(0, 0, canvas.width, canvas.height)
}
const saveSignature = () => {
const canvas = canvasRef.current
const dataUrl = canvas.toDataURL('image/png')
// Create a temporary link to download the signature
const link = document.createElement('a')
link.download = 'signature.png'
link.href = dataUrl
link.click()
}
return (
<Card className="w-full max-w-2xl">
<CardHeader>
<CardTitle>Signature Pad</CardTitle>
</CardHeader>
<CardContent>
<div className="rounded-lg border bg-white p-4">
<canvas
ref={canvasRef}
className="h-48 w-full cursor-crosshair touch-none rounded border"
onMouseDown={startDrawing}
onMouseMove={draw}
onMouseUp={stopDrawing}
onMouseLeave={stopDrawing}
/>
</div>
</CardContent>
<CardFooter className="flex justify-end space-x-2">
<Button variant="outline" onClick={clearSignature}>
Clear
</Button>
<Button onClick={saveSignature}>Save</Button>
</CardFooter>
</Card>
)
}
export default SignaturePad
This implementation provides a basic but functional signature capture solution. Here are some additional solutions and enhancements you might consider:
- Touch-enabled Enhancement:
// Add these event listeners for touch support
onTouchStart={(e) => {
e.preventDefault();
const touch = e.touches[0];
startDrawing(touch);
}}
onTouchMove={(e) => {
e.preventDefault();
const touch = e.touches[0];
draw(touch);
}}
onTouchEnd={stopDrawing}
- Using SignaturePad.js library:
import SignaturePad from 'signature_pad'
// Initialize with
const signaturePad = new SignaturePad(canvas, {
minWidth: 0.5,
maxWidth: 2.5,
penColor: 'black',
})
- Advanced Features You Might Want to Add:
- Undo/Redo functionality
- Different pen colors and sizes
- Signature validation (minimum length/points)
- Base64 encoding for server submission
- Pressure sensitivity support
- Background lines or grid
- Mobile-specific optimizations
- Server Integration Example:
const submitSignature = async () => {
const dataUrl = canvasRef.current.toDataURL()
try {
const response = await fetch('/api/signatures', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ signature: dataUrl }),
})
// Handle response
} catch (error) {
// Handle error
}
}
- For Production Considerations:
- Add error boundaries
- Implement loading states
- Add proper validation
- Include accessibility features
- Add proper touch event handling
- Implement proper error messages
- Add signature verification
- Implement proper data storage
Would you like me to modify the implementation to include any of these additional features or focus on a specific aspect of signature capture?