npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@remotedraw/receiver-web

v0.5.0

Published

Framework-independent RemoteDraw receiver and custom element.

Readme

@remotedraw/receiver-web

Framework-independent RemoteDraw receiver and custom element. Real-device acceptance is still pending. A phone browser or the existing iOS sender can test it; Android hardware is not required for those paths. Android sender compatibility remains unverified until separately tested.

Install and mount

npm install @remotedraw/receiver-web   # npm
pnpm add @remotedraw/receiver-web      # pnpm
yarn add @remotedraw/receiver-web      # Yarn
bun add @remotedraw/receiver-web       # bun

To use an unreleased build from a RemoteDraw checkout instead, see Working from a checkout.

import { mountReceiver, createHttpReceiverClient } from '@remotedraw/receiver-web';
const response = await fetch('/api/drawing-session', { method: 'POST' });
if (!response.ok) throw new Error('Could not create session');
// Your backend creates the session; never send an rd_sk_ API key to a browser.
const created = await response.json();
const session = {
  ...created.session,
  receiverToken: created.receiverToken,
  joinUrl: created.joinUrl,
  joinUrls: created.joinUrls,
  joinTokenExpiresAt: created.joinTokenExpiresAt,
};
const view = mountReceiver(document.querySelector<HTMLElement>('#board')!, {
  receiver: createHttpReceiverClient('https://api.remotedraw.com'),
  session,
  // Board aspect ratio is derived from the session's coordinate space.
});
// On route exit or window teardown:
view.dispose();

The view owns its store, starts it automatically, and stops it on disposal. Pass store to share an existing createReceiverStore instead; then the host owns its start/stop lifecycle. Use view.store.setSession(nextSession) to switch boards and view.store.clear(), .undo(), .refreshJoinToken() for controls. The shared client handles draft/commit handoff, ordering, stale draft cleanup, presence aging, polling, push-source fallback and reconnect backfill.

Use the same source option as the headless client for realtime updates. HTTP polling alone is functional but is not low-latency streaming. Supply scoped receiver credentials returned by your backend. The pairing panel includes a QR, link, expiry handling, and manual code refresh. It never puts receiver credentials in attributes or events. onStateChange does receive privileged store state; keep it within trusted application code.

Custom element

import { defineRemoteDrawReceiver } from '@remotedraw/receiver-web';
defineRemoteDrawReceiver();
const board = document.createElement('remote-draw-receiver');
board.options = { receiver, session }; // JavaScript property, not HTML attributes
container.append(board);
// Removing the element disposes its view. Reconnecting mounts a fresh view.

The module can be imported during SSR; register/mount only in the browser. Set options again to reconfigure. Parts: receiver, pairing, canvas. Use remote-draw-receiver::part(canvas) to style the canvas. showPairing: false hides the built-in panel for a host-owned pairing flow. The custom element must be registered in the same window/realm as its HTMLElement base.

Framework recipes

Vue: mount in onMounted(() => { view = mountReceiver(board.value, options) }) and dispose in onBeforeUnmount(() => view?.dispose()). Use a <div ref="board"/>. Angular: use @ViewChild('board') board: ElementRef, mount in ngAfterViewInit and dispose in ngOnDestroy; template <div #board></div>. No Angular schema change is needed when using the imperative mount. Svelte: onMount(() => { const view = mountReceiver(board, options); return () => view.dispose(); }) with <div bind:this={board}></div>. These recipes use the framework's normal lifecycle; the DOM implementation is shared. Vue, Angular, and Svelte compiler/runtime integration has not been tested yet.

Rendering boundaries

Basic normalized freehand/point strokes, line/arrow/rectangle/ellipse outlines, plain text, pressure/tilt stroke geometry and simple layered ink are implemented using the shared client geometry. The SVG follows session target.coordinateSpace width/height (square fallback), or an explicit aspectRatio override. It fits that coordinate aspect ratio without stretching. Resize the host freely; server coordinate aspect changes update automatically unless an explicit override is supplied.

This preview does not provide parity with the React receiver: images, annotation/document anchors, projection cameras/phone frames, textured substrate masks, wet-edge effects, accumulated highlighter film, physical material simulation, or WebRTC visual-context publication/consumption are not implemented. Unknown types and unsupported features are listed in the SVG's data-unsupported attribute, visible limitation notice, and onUnsupported callback; unsupported elements are omitted, while advanced materials use basic vector fallback. Text is plain SVG text, with fixed font size and no multiline layout. Large-board incremental rendering is not optimized. Do not accept visual fidelity or streaming parity based on this preview.

Desktop recipes

Electron: install this package in the renderer app, then use the mount example. Keep contextIsolation: true and nodeIntegration: false; session creation and any secret key belong to a trusted backend, not renderer code or a distributed binary. Dispose the receiver on SPA teardown. No Electron-specific transport is needed. Test background/resume, offline/reconnect and window resize.

Tauri: use the same frontend mount example. Allow the API origin in your CSP connect-src and data: QR images in img-src; use your backend for scoped sessions. Do not embed developer keys in Rust or frontend bundles. Test window close/reopen and suspend/resume. These are integration recipes, not independently validated Electron/Tauri starter applications.

Local fixture demo and release gate

From repository root:

/Users/wout/.local/bin/dev-external bun build packages/web/examples/demo.ts --outdir packages/web/examples/dist --target browser
/Users/wout/.local/bin/dev-external bun packages/web/examples/serve.ts

Open http://127.0.0.1:4319. This demo uses a local store fixture with no network credentials. It exercises draft/commit replacement, clear and session switching. For live acceptance, replace the fixture store with the scoped-session mount example above. Check pairing/QR from iPhone, draft ink, commit, undo, clear, reconnect after airplane mode, join-code refresh/expiry, two senders, board switching and resizing. Record browser, OS and device. Run bun run --cwd packages/web test and bun run --cwd packages/web build through dev-external before packing.