A reusable drawpad component for Expo React Native
npm install expo-drawpad


A smooth, animated drawing pad component for React Native and Expo applications. Perfect for signatures, sketches, and interactive drawing experiences.
- โจ Smooth Drawing: Responsive touch gestures with customizable stroke width and color
- ๐ฌ Path Animation: Replay drawings with smooth animations
- ๐จ Multiple Brush Types: Solid, dotted, dashed, and highlighter brush styles
- ๐ Gradient Support: Apply beautiful gradients to your strokes
- โฉ๏ธ Undo/Redo: Built-in undo functionality for better user experience
- ๐งน Clear Canvas: Easy erase functionality to start fresh
- ๐ฑ Cross-Platform: Works on iOS, Android, and Web
- ๐ฏ TypeScript: Full TypeScript support with type definitions
- ๐ชถ Lightweight: Minimal dependencies and optimized performance
- ๐ Path Management: Get, set, and add paths programmatically
- ๐ SVG Export: Export drawings as SVG strings
- ๐๏ธ Customizable Animation: Control animation duration and easing
- ๐ช Event Callbacks: Handle draw start and end events
``bash`
npm install expo-drawpad
Make sure you have these peer dependencies installed:
`bash`
npm install react-native-gesture-handler react-native-reanimated react-native-svg react-native-worklets
For Expo projects, you can install them with:
`bash`
npx expo install react-native-gesture-handler react-native-reanimated react-native-svg react-native-worklets
`tsx
import React, { useRef } from "react";
import { View, Button } from "react-native";
import DrawPad, { DrawPadHandle } from "expo-drawpad";
export default function App() {
const drawPadRef = useRef
return (
);
}
`
`tsx
import React, { useRef } from "react";
import { View, Button } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import DrawPad, { DrawPadHandle } from "expo-drawpad";
export default function App() {
const drawPadRef = useRef
const pathLength = useSharedValue(0);
const playing = useSharedValue(false);
const signed = useSharedValue(false);
return (
stroke="#000000"
strokeWidth={3}
pathLength={pathLength}
playing={playing}
signed={signed}
/>
);
}
`
| Prop | Type | Required | Default | Description |
| ------------------- | ---------------------- | -------- | ------------------------------- | --------------------------------------------------- |
| stroke | string | No | "grey" | Color of the drawing stroke (hex, rgb, etc.) |pathLength
| | SharedValue | No | - | Reanimated shared value for total path length |playing
| | SharedValue | No | - | Reanimated shared value for animation state |strokeWidth
| | number | No | 3.5 | Width of the drawing stroke |signed
| | SharedValue | No | - | Optional shared value to track if anything is drawn |pathProps
| | PathProps | No | - | Additional SVG path properties |gradient
| | gradientProps | No | - | Gradient configuration for stroke |brushType
| | BrushType | No | "solid" | Brush style: solid, dotted, dashed, or highlighter |onDrawStart
| | () => void | No | - | Callback when drawing starts |onDrawEnd
| | () => void | No | - | Callback when drawing ends |animationDuration
| | number | No | pathLength.value * 2 | Custom animation duration in milliseconds |easingFunction
| | EasingFunction | No | Easing.bezier(0.4, 0, 0.5, 1) | Custom easing function for animations |
`tsx
export interface gradientProps {
colors: string[];
locations?: number[];
start?: { x: number; y: number };
end?: { x: number; y: number };
}
export type BrushType = "solid" | "dotted" | "dashed" | "highlighter";
`
| Method | Description |
| --------------------------- | -------------------------------------- |
| erase() | Clears the entire drawing canvas |undo()
| | Removes the last drawn path |play()
| | Starts the drawing animation playback |stop()
| | Stops the current animation playback |getPaths()
| | Returns array of all drawn paths |setPaths(paths: string[])
| | Sets the paths programmatically |addPath(path: string)
| | Adds a single path to the canvas |getSVG()
| | Returns Promise
`tsx
import React, { useRef, useState } from "react";
import { View, Button, StyleSheet } from "react-native";
import DrawPad, { DrawPadHandle, BrushType } from "expo-drawpad";
export function BrushTypesExample() {
const drawPadRef = useRef
const [brushType, setBrushType] = useState
return (
brushType={brushType}
stroke="#2563eb"
strokeWidth={4}
/>
onPress={() => setBrushType("highlighter")}
/>
);
}
`
`tsx
import React, { useRef } from "react";
import { View, Button } from "react-native";
import DrawPad, { DrawPadHandle } from "expo-drawpad";
export function GradientExample() {
const drawPadRef = useRef
const gradient = {
colors: ["#ff6b35", "#f7931e", "#ffd23f"],
locations: [0, 0.5, 1],
start: { x: 0, y: 0 },
end: { x: 1, y: 1 },
};
return (
);
}
`
`tsx
import React, { useRef, useState } from "react";
import { View, Text, Button } from "react-native";
import DrawPad, { DrawPadHandle } from "expo-drawpad";
export function CallbackExample() {
const drawPadRef = useRef
const [isDrawing, setIsDrawing] = useState(false);
const [pathCount, setPathCount] = useState(0);
const handleDrawStart = () => {
setIsDrawing(true);
};
const handleDrawEnd = () => {
setIsDrawing(false);
setPathCount((prev) => prev + 1);
};
return (
onDrawStart={handleDrawStart}
onDrawEnd={handleDrawEnd}
stroke="#10b981"
/>
onPress={() => {
drawPadRef.current?.erase();
setPathCount(0);
}}
/>
);
}
`
`tsx
import React, { useRef } from "react";
import { View, Button, Alert } from "react-native";
import DrawPad, { DrawPadHandle } from "expo-drawpad";
export function SVGExportExample() {
const drawPadRef = useRef
const handleExportSVG = async () => {
try {
const svgString = await drawPadRef.current?.getSVG();
if (svgString) {
// You can save this SVG string to a file or send it to a server
Alert.alert("SVG Exported", "Check console for SVG string");
console.log(svgString);
}
} catch (error) {
Alert.alert("Error", "Failed to export SVG");
}
};
return (
);
}
`
`tsx
import React, { useRef } from "react";
import { View, Text, TouchableOpacity, StyleSheet } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import DrawPad, { DrawPadHandle } from "expo-drawpad";
export function SignaturePad() {
const drawPadRef = useRef
const pathLength = useSharedValue(0);
const playing = useSharedValue(false);
const signed = useSharedValue(false);
return (
stroke="#2563eb"
strokeWidth={2}
pathLength={pathLength}
playing={playing}
signed={signed}
/>
onPress={() => drawPadRef.current?.erase()}
>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 20 },
title: { fontSize: 18, marginBottom: 20, textAlign: "center" },
canvas: {
flex: 1,
backgroundColor: "white",
borderRadius: 10,
borderWidth: 1,
borderColor: "#ddd",
},
buttons: { marginTop: 20 },
button: {
padding: 15,
backgroundColor: "#f0f0f0",
borderRadius: 5,
alignItems: "center",
},
});
`
`tsx
import React, { useRef } from "react";
import { View, Button } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import DrawPad, { DrawPadHandle } from "expo-drawpad";
export function AnimatedDrawing() {
const drawPadRef = useRef
const pathLength = useSharedValue(0);
const playing = useSharedValue(false);
const handleReplay = () => {
drawPadRef.current?.play();
};
return (
stroke="#ff6b35"
strokeWidth={4}
pathLength={pathLength}
playing={playing}
/>
);
}
`
- React Native 0.81.4+
- React Native Reanimated 4.1.1+
- React Native Gesture Handler 2.28.0+
- React Native SVG 15.12.1+
- React Native Worklets 0.5.1+
- Expo SDK 51+
- iOS 13.0+
- Android API Level 23+
#### Gestures not working
Make sure you have properly configured react-native-gesture-handler in your project. For React Native CLI projects, you may need to complete the platform-specific installation steps.
#### Animation not smooth
Ensure react-native-reanimated is properly installed and configured. For React Native CLI projects, you may need to rebuild your app after installation.
#### SVG not rendering
Verify that react-native-svg is correctly installed and linked. For React Native CLI projects, you may need to run cd ios && pod install.
- Use strokeWidth between 1-10 for optimal performancesigned
- Limit drawing area size for better performance on older devices
- Consider using shared value to track drawing state efficiently
Contributions are welcome! Please feel free to submit a Pull Request.
- โจ NEW: Added multiple brush types (solid, dotted, dashed, highlighter)
- โจ NEW: Added gradient support for strokes
- โจ NEW: Added SVG export functionality (getSVG() method)getPaths()
- โจ NEW: Added path management methods (, setPaths(), addPath())onDrawStart
- โจ NEW: Added draw event callbacks (, onDrawEnd`)
- โจ NEW: Added customizable animation duration and easing
- โจ NEW: Added additional path properties support
- ๐ง IMPROVED: Smoother drawing with quadratic curves instead of linear paths
- ๐ง IMPROVED: Better worklets integration for performance
- ๐ง IMPROVED: Updated peer dependencies to latest versions
- ๐ง IMPROVED: Enhanced TypeScript definitions
ISC
Solarin Johnson
---
Made with โค๏ธ for the React Native community