A native ink canvas for React Native. It draws with androidx.ink on Android, PencilKit on iOS and Canvas 2D on the web.
- Pen, marker and highlighter brushes, an eraser, undo and redo
- Background images and a stylus-only mode
- Export to PNG, JPEG or SVG, optionally trimmed to the drawing
- Save a drawing as JSON and load it back
- Web support, with the Ink API for lower latency in Chrome, Edge and other Chromium browsers
- Drop-in replacement for react-native-signature-canvas
npm install react-native-inkpad
cd ios && pod installRequirements:
- React Native 0.84+ (Expo SDK 56+) with the New Architecture
- iOS 15.1+
- Android 7.0+ (API 24)
- Web: react-native-web 0.21+ and a browser with Pointer Events
- Expo: a development build (Expo Go isn't supported)
import { useRef } from 'react';
import { Button, View } from 'react-native';
import { InkCanvas, type InkCanvasRef } from 'react-native-inkpad';
export function SignatureField() {
const canvas = useRef<InkCanvasRef>(null);
const save = async () => {
const image = await canvas.current?.toImage({ trim: { padding: 8 } });
console.log(image?.uri);
};
return (
<View>
<InkCanvas
ref={canvas}
style={{ height: 240 }}
brush={{ color: '#111111', size: 3 }}
background={{ color: '#FFFFFF' }}
/>
<Button title="Save" onPress={save} />
</View>
);
}InkCanvas takes the usual View props, plus:
| Prop | Type | Default | Description |
|---|---|---|---|
brush |
{ type, color, size } |
black pen, size 3 | Brush for new strokes. type is 'pen', 'marker' or 'highlighter'. Only the pen changes width with speed or stylus pressure. |
tool |
'draw' | 'erase' |
'draw' |
The eraser removes whole strokes. |
inputMode |
'any' | 'stylus' |
'any' |
With 'stylus', only a pen draws and fingers go to the parent. |
editable |
boolean |
true |
Set to false to turn drawing off. |
background |
{ color, image, fit } |
transparent | Canvas color and an image under the ink, both exported. image takes an image source, fit is 'contain' or 'cover'. On web, an image from another origin needs CORS headers. Request headers on the image source aren't sent. |
onStrokeStart |
() => void |
Called when a stroke starts. | |
onStrokeEnd |
() => void |
Called when a stroke ends. | |
onChange |
(state) => void |
Called after every change with { strokeCount, canUndo, canRedo }. |
|
onError |
(error) => void |
Called with an InkpadError when the background image fails to load. |
| Method | Description |
|---|---|
undo(), redo() |
Step back or forward through the history. |
clear() |
Remove every stroke. |
getStrokes() |
Get the strokes as a plain object you can store as JSON. |
setStrokes(doc) |
Replace the drawing with strokes from getStrokes(). |
toImage({ format, quality, scale, maxSize, trim, background, base64 }) |
Export a PNG or JPEG. maxSize caps the longest side in pixels. Resolves to { uri, width, height, base64? }. On web, uri is a blob: URL. |
toSVG({ trim, background }) |
Export an SVG string. It links the background image by URI. |
renderImage(doc, options) and renderSVG(doc, options) turn a document from getStrokes() into an image or an SVG string without a mounted InkCanvas. Use them for thumbnails and lists.
| Function | Returns | Options |
|---|---|---|
renderImage(doc, options) |
Promise<{ uri, width, height, base64? }>, the same result as toImage. |
The toImage options, except background is { color, image, fit } like the prop. |
renderSVG(doc, options) |
An SVG string, synchronously. | { trim, background }, with the same background. |
A document has no background, so the default is transparent. maxSize is the longest side in pixels. It lowers scale for an image that would be bigger and never raises it, and a value below 1 is ignored.
import { renderImage } from 'react-native-inkpad';
const thumb = await renderImage(doc, {
maxSize: 256,
trim: { padding: 8 },
background: { color: '#FFFFFF' },
});renderImage rejects and renderSVG throws an InkpadError. renderImage fails with E_EXPORT_FAILED if the native module is missing, so rebuild the app.
The react-native-inkpad/svg entry exports renderSVG, validateDocument, InkpadError and ErrorCode, and doesn't load React Native.
import { renderSVG } from 'react-native-inkpad/svg';
const svg = renderSVG(doc, { trim: { padding: 8 }, background: { color: 'hsl(0 0% 100%)' } });Its background is { color?, image?: { uri }, fit? }. color takes a hex, rgb(), hsl(), hwb() or named color, and one it can't parse throws E_EXPORT_FAILED. The SVG links the background image by URI.
npm 7+ installs the react and react-native peers on the server anyway, though nothing loads them.
Failed calls throw an InkpadError. Its code is one of:
| Code | Meaning |
|---|---|
E_NOT_MOUNTED |
The canvas isn't mounted, or it unmounted before the call finished. |
E_TIMEOUT |
The native view didn't answer within 15 seconds. |
E_INVALID_DOCUMENT |
setStrokes(), renderImage() or renderSVG() got a malformed document. |
E_UNSUPPORTED_VERSION |
setStrokes(), renderImage() or renderSVG() got a document saved by a newer version of the library. |
E_EMPTY |
trim was set but the canvas, or the document given to renderImage() or renderSVG(), has no strokes. |
E_EXPORT_FAILED |
The image couldn't be created or saved. |
E_IMAGE_LOAD |
The background image couldn't be loaded, or renderImage() or renderSVG() got an image with no usable URI, such as a numeric require() asset on web. On the canvas it goes to onError. |
E_NATIVE |
Any other native error. |
Change the import:
- import SignatureCanvas from 'react-native-signature-canvas';
+ import SignatureCanvas from 'react-native-inkpad/signature-canvas';Props, callbacks and ref methods keep working, and fromData loads signatures you saved with getData. A few things change:
- Props that don't apply anymore, like
customHtmlorminWidth, are ignored. A dev warning lists them. - The footer has a new look. Pass
renderFooterto draw your own. - The eraser removes whole strokes, not pixels. It can't remove a
dataURLimage. - A
dataURLimage keeps its aspect ratio instead of stretching to the pad.
From 1.0, releases follow semver:
- Patches fix bugs, minors add props, methods and error codes
- Removing or renaming anything public, the signature canvas adapter included, waits for a major
- Documents from
getStrokes()load in every later version - A minor can change how strokes look, never the saved document format
- 0.1: Android and iOS
- 0.2: React Native 0.84+ (Expo SDK 56+)
- 0.3: Web
- 0.4: Background images, stylus-only mode and
onError - 1.0: First stable release
- 1.1: Render a document without a canvas
- 1.2: Zoom and pan
- 1.3: Point eraser and stylus extras
- 1.4: Lasso select and move
See the contributing guide. Questions and ideas go to Discussions.
MIT
Made by SectionTN