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 sketchUsage
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; passnative={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
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | |
colors | string[] | — | The swatches along the bottom. The custom-colour button sits before them. |
defaultColor | string | — | The ink to start with. Defaults to the theme's foreground. |
defaultTool | SketchTool | 'pen' | The tool selected when the surface opens. Default pen. |
defaultShape | SketchShape | 'rectangle' | The shape the shape tool draws until another is picked. Default rectangle. |
shapes | SketchShape[] | — | Which shapes the grid offers, in order. Defaults to all eight. |
defaultSize | number | 6 | Stroke width to start with, in points. Default 6. |
minSize | number | 2 | The thinnest stroke the slider reaches. Default 2. |
maxSize | number | 24 | The thickest stroke the slider reaches. Default 24. |
customColor | boolean | true | Show 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
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | |
onOpenChange | (open: boolean) => void | — | |
native | boolean | — | 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