Sketch

Draw with a pen, shapes and an eraser, in a sheet or a whole screen.

A drawing surface with its tools around it: a pen, eight shapes, an eraser, a stroke-width slider, colours and undo. It hands back the drawing as SVG when you press confirm.

It fills the space it is given. Use Sketch.Sheet to present it in a bottom sheet, or put Sketch on a screen of its own.

For a signature on a form, use Signature. It is a single pad with no tools.

Installation

Sketch ships with the library — no separate install.

import { Sketch, Button, type SketchHandle } from 'panelui-native';
import { View } from 'react-native';

Or copy the source into your project, to own and edit it:

npx panelui-cli@latest add sketch

Usage

const [open, setOpen] = useState(false);

<Button onPress={() => setOpen(true)}>Draw</Button>
<Sketch.Sheet
  open={open}
  onOpenChange={setOpen}
  onDone={({ svg }) => attach(svg)}
/>

Composition

<Sketch.Sheet open={open} onOpenChange={setOpen} onDone={save} />

{/* or on a screen of its own */}
<Sketch onClose={goBack} onDone={save} />
  • Sketch.Sheet — The sketch in a bottom sheet that fills the screen. The sheet cannot be dragged or tapped away, so close and confirm are the ways out, and both close it. It is the platform's own sheet by default, painted solid; pass native={false} for the styled one.

Examples

In a sheet

Sketch.Sheet opens over the current screen and closes when the drawing is confirmed. onDone receives the SVG, the canvas size in points and the list of what was drawn.

const [open, setOpen] = useState(false);
const [drawing, setDrawing] = useState<string | null>(null);

<Button variant="outline" onPress={() => setOpen(true)}>Open sketch</Button>
<Sketch.Sheet
  open={open}
  onOpenChange={setOpen}
  onDone={({ svg }) => setDrawing(svg)}
/>

The styled sheet

native={false} uses PanelUI's own sheet instead of the platform's. It is also what you get when @expo/ui is not installed.

<Sketch.Sheet native={false} open={open} onOpenChange={setOpen} onDone={save} />

Choosing the tools

colors replaces the swatches and shapes chooses which shapes the grid offers, in order. customColor={false} removes the colour-picker button before the swatches.

<Sketch
  colors={['#111111', '#ef4444', '#3b82f6']}
  shapes={['arrow', 'rectangle', 'ellipse']}
  defaultSize={4}
  minSize={1}
  maxSize={12}
  onDone={save}
/>

Driving it from outside

A ref gives you undo, clear, isEmpty, toSVG and toPNG. toPNG needs the optional react-native-view-shot and throws by name without it.

const sketch = useRef<SketchHandle>(null);

<Sketch ref={sketch} onChange={setCount} />
<Button onPress={async () => share(await sketch.current?.toPNG({ scale: 3 }))}>
  Share as PNG
</Button>

Versions

Full page

The whole screen is the canvas. Pad it by the safe-area insets, since it draws edge to edge.

const insets = useSafeAreaInsets();

<Sketch
  style={{ paddingTop: insets.top, paddingBottom: insets.bottom }}
  onClose={() => router.back()}
  onDone={save}
/>

API Reference

Sketch

PropTypeDefaultDescription
classNamestring—
colorsstring[]—The swatches along the bottom. The custom-colour button sits before them.
defaultColorstring—The ink to start with. Defaults to the theme's foreground.
defaultToolSketchTool'pen'The tool selected when the surface opens. Default pen.
defaultShapeSketchShape'rectangle'The shape the shape tool draws until another is picked. Default rectangle.
shapesSketchShape[]—Which shapes the grid offers, in order. Defaults to all eight.
defaultSizenumber6Stroke width to start with, in points. Default 6.
minSizenumber2The thinnest stroke the slider reaches. Default 2.
maxSizenumber24The thickest stroke the slider reaches. Default 24.
customColorbooleantrueShow the custom-colour button before the swatches. Default true.
onClose() => void—Called by the close button in the top leading corner. Leave it out and there is no close button — for a sketch embedded in a screen that has its own way out.
onDone(result: SketchResult) => void—Called by the confirm button with the finished drawing. Leave it out and there is no confirm button. The button is disabled until something has been drawn.
onChange(itemCount: number) => void—The number of items in the document changed — by drawing, undo or clear.

Sketch.Sheet

PropTypeDefaultDescription
openboolean—
onOpenChange(open: boolean) => void—
nativeboolean—Present the platform's own sheet, painted solid in the theme's popover surface. On by default. Requires the optional @expo/ui package; without it the styled sheet is used instead. On iOS the sheet cannot be dragged away, so a downward stroke draws rather than moving the sheet. The close button is the way out.

Every part also accepts the underlying React Native props (ViewProps or TextProps) and a className for Tailwind utilities.

Notes

The eraser rubs out, it does not delete

The eraser paints a mask over what was drawn before it, so it removes only what is under the finger. Ink drawn afterwards over an erased patch is not affected. Undo takes an eraser stroke back like any other mark.

The drawing stays vector, and toSVG() writes the same masks, so an export matches the screen.

The sheet cannot be dragged away

On a canvas every drag is a stroke. On iOS the platform sheet is told not to take drags, and the styled sheet's drag is switched off. On Android, the platform sheet keeps its own drag handling.

It needs a height

Sketch fills its parent, like any flex-1 view. On a screen, that is the screen. Inside a scroll view it has no height to fill; give it one.

Colours

The chrome is drawn in theme tokens, so it follows light and dark. The swatches are ink colours rather than tokens, because a drawing keeps its colours when it leaves the app. The default ink is the theme's foreground.

Public exports

Values: Sketch, hasSketchRaster

Types: SketchProps, SketchSheetProps, SketchHandle, SketchResult, SketchItem, SketchTool, SketchShape

On this page