Functions & Hooks

useCameraFrame

function useCameraFrame<F>(onEachFrame?: (frame: F) => void, options?: CameraFrameOptions<F>): CameraFrameSource<F>;

Turn a stream of camera frames into a <CameraFrame> handle.

const { image, posture, onFrame } = useCameraFrame()
const output = useFrameOutput({ pixelFormat: 'yuv', onFrame })
// ...
<CameraFrame image={image} {...posture} />

The callback is returned rather than registered, so this package needs no camera dependency at all and any producer of a FrameLike works.

It owns the buffer's lifetime, which is the reason to prefer it over calling imageFromNativeBuffer yourself: the raw pointer never leaves this scope, so it cannot be stored, and a try/finally releases the buffer and disposes the frame on every path including a throw. A leaked frame stalls a bounded producer pool; a stored pointer is a use-after-free that does not throw.

The first frame creates the handle. Every later frame imports the new native buffer and replaces the registry entry behind that handle, so there is no React render, reconciler commit or program recompile. posture only reaches React state when it actually changes. The handle is disposed on unmount: an external registry entry is never evicted, so nothing else would ever free it.

onEachFrame is your own per-frame worklet - a detector, say. It runs inside the same try, so work that throws still cannot leak the frame. Memoise it, or the output re-registers every render.

Type Parameters

F

F extends FrameLike = FrameLike

Parameters

onEachFrame?

(frame: F) => void

options?

CameraFrameOptions<F>

Returns

CameraFrameSource<F>

Remarks

The frame onEachFrame receives dies when it returns. Read from it and let it go: do not retain it, do not call getNativeBuffer() on it again - that is a second +1 retain the hook will not release, and one unreleased buffer stalls the producer's bounded pool - and never store getNativeBuffer().pointer anywhere. A stale pointer does not throw; it imports whatever memory is now at that address. Everything the hook itself needs from the buffer is already done before your callback runs.

Needs react-native-reanimated: the handle has to outlive one callback on the producer's worklet runtime.

On this page