Implementing Cross-Device Share Sheets in PWAs Inspired by Pixel's AirDrop Move
PWAsharingcross-platform

Implementing Cross-Device Share Sheets in PWAs Inspired by Pixel's AirDrop Move

UUnknown
2026-03-01
12 min read
Advertisement

Step-by-step guide (2026) to build secure, low-friction PWA share sheets that mimic AirDrop between Pixel and iPhone using Web Share, WebRTC, and E2E crypto.

Hook — Why cross-device sharing still hurts and how this guide fixes it

As a React engineer shipping PWAs in 2026 you face familiar, practical problems: stakeholders want a frictionless way to drop links and files between phones, users expect AirDrop-like simplicity, and you must do this without shipping a native app. Recent developments — including reports about Pixel 9 adding AirDrop compatibility and steady improvements to the Web Share API and WebTransport — mean the web can finally meet this expectation. This article gives a pragmatic, step-by-step plan to build a secure, low-friction cross-device share sheet for PWAs that interoperates with Pixel and iPhone workflows while remaining robust across devices and browsers.

At-a-glance: what you’ll get from this guide

  • Architecture blueprint: discovery, negotiation, transfer, and fallbacks
  • Concrete React + PWA code samples: Web Share, Web Share Target, Service Worker integration
  • Interoperability tactics that consider Pixel 9 rumors and iOS constraints in early 2026
  • Security, testing, and DevOps best practices to make it production-ready

The state of sharing in 2026 — why now?

Late 2025 and early 2026 brought two important trends for web-based sharing. First, browser vendors expanded support for the Web Share API v2 and Web Share Target, making it possible for PWAs to both send and receive files and URLs more natively. Second, Android vendors — led by Google and reports around Pixel 9 — moved toward cross-platform AirDrop-like interoperability with iOS, which increases user expectation that web apps should play well across ecosystems.

That said, platform gaps remain: iOS historically limits low-level Bluetooth and background services for web apps, and not every browser supports the same set of APIs. The practical strategy is to combine native web sharing primitives where available and implement resilient fallbacks where they are not.

High-level architecture

Build the share sheet as four coordinated layers:

  1. Presentation: the UI inside your PWA (React) that exposes a single “Share nearby” entry point.
  2. Discovery & signaling: how two devices find each other and exchange ephemeral connection info (QR, BLE, Bluetooth LE advertising, or local network mDNS + a fallback relay).
  3. Secure negotiation: ephemeral key exchange (ECDH via SubtleCrypto) and capability negotiation (max chunk size, resume support, MIME types).
  4. Data transfer: file or link transfer over a secure channel using WebRTC DataChannel, WebTransport, or the Web Share API for one-shot link/file shares; support chunking and resume.

Design goals

  • Low friction: one tap for common flows, graceful fallbacks for less capable devices.
  • Secure: end-to-end encryption, ephemeral keys, strict permissions, no long-term server storage.
  • Interoperable: work with Android Pixel devices (including possible Pixel 9 AirDrop interoperability) and iPhones where possible.
  • Testable and maintainable: CI checks, E2E tests, and observability for transfers.

Step 1 — Build the UI and UX in React

Your PWA needs a single, consistent entry point for sharing. Keep it minimal: one modal that detects capabilities and recommends the best flow. Use feature detection first, then surface options.

Feature detection pattern (React)

const features = {
  canNavigatorShare: !!navigator.share,
  canShareFiles: !!navigator.canShare,
  canUseWebRTC: !!(window.RTCPeerConnection),
  canUseWebTransport: !!(window.WebTransport),
  // Web Bluetooth is available in many Chromium browsers, but not all iOS browsers
  canUseBluetooth: !!navigator.bluetooth,
};

Render the share modal to recommend the optimal flow. Example: if navigator.canShare supports files, call navigator.share; otherwise, fall back to the WebRTC path.

Step 2 — Outgoing shares: use the Web Share API first

The simplest, lowest-friction path is the Web Share API. For many share targets — links, short files — this offers the same experience as native share sheets.

Outgoing file share example

async function shareFiles(files, title, text) {
  if (navigator.canShare && navigator.canShare({ files })) {
    await navigator.share({ files, title, text });
    return { ok: true };
  }
  return { ok: false, reason: 'no-native-share' };
}

Use this as the primary action. On Pixel devices and many Android browsers in 2026 this will hand off to the system share sheet which can include Nearby Share or Pixel's cross-platform AirDrop layer if available.

Step 3 — Receiving in a PWA: Web Share Target and Service Workers

To accept shares directly into your PWA, register a share_target in your web manifest and handle the incoming POST in the PWA (usually routed through your Service Worker or a client page).

Manifest snippet

{
  "name": "My PWA",
  "start_url": "/?source=pwa",
  "share_target": {
    "action": "/share-receive",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "title": "title",
      "text": "text",
      "files": [{ "name": "file", "accept": ["image/*", "application/pdf"] }]
    }
  }
}

Handle the POST in the page at /share-receive or grab it in your Service Worker. Chrome-based browsers will surface the PWA as a share target; iOS is still limited in how it surfaces PWAs here, so plan fallbacks.

Service Worker snippet to read the share

self.addEventListener('fetch', event => {
  const url = new URL(event.request.url);
  if (url.pathname === '/share-receive' && event.request.method === 'POST') {
    event.respondWith((async () => {
      const formData = await event.request.formData();
      // process formData.get('file') etc.
      return Response.redirect('/shared-success', 303);
    })());
  }
});

Step 4 — Discovery and signaling: find the other device

Because PWAs cannot rely on OS-level discovery always being available, combine multiple options in order of preference:

  1. System share handoff (Web Share / OS): Pixel 9 or Nearby Share will handle discovery at OS level when available.
  2. Web Share Target (incoming POST): browser will surface the PWA as a target.
  3. Local signaling: use Web Bluetooth LE (where supported) for a tiny advertisement that carries an ephemeral rendezvous token.
  4. QR / Manual Code: show a QR code with a short code that the other device scans — the most universal fallback and useful for iPhone Safari where BLE for web may be restricted.
  5. Relay server: when direct local connectivity fails, use an authenticated, ephemeral relay for bridging signaling only (no storage).

BLE sample (discovery token broadcast)

async function advertiseToken(token) {
  const serviceUuid = '0000aaaa-0000-1000-8000-00805f9b34fb';
  try {
    const server = await navigator.bluetooth.requestDevice({
      filters: [{ services: [serviceUuid] }]
    });
    // Web Bluetooth APIs vary — this pattern is for capability detection and UX
  } catch (err) {
    console.warn('BLE not available', err);
  }
}

Note: Web Bluetooth in browsers and iOS support varies in 2026; always provide a QR and relay fallback.

Step 5 — Establish a secure channel (ECDH + WebRTC or WebTransport)

Once devices exchange a short rendezvous token, they need to build a secure channel. Use browser-native crypto (SubtleCrypto) to do a short-lived ECDH key exchange and derive an AES-GCM session key. Then use that key to encrypt file chunks before sending over WebRTC DataChannel or WebTransport.

Key exchange outline

  1. Generate ephemeral ECDH key pair on each device using ECDH P-256.
  2. Exchange public keys over the signaling channel (QR, BLE, or relay).
  3. Derive a shared secret using SubtleCrypto and then HKDF to derive AES-GCM keys.
async function generateKeyPair() {
  return crypto.subtle.generateKey({ name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveKey']);
}

async function deriveSharedKey(privateKey, peerPublicKey) {
  const derived = await crypto.subtle.deriveKey(
    { name: 'ECDH', public: peerPublicKey },
    privateKey,
    { name: 'AES-GCM', length: 256 },
    false,
    ['encrypt', 'decrypt']
  );
  return derived;
}

Transport choices

  • WebRTC DataChannel: widely supported for peer-to-peer; implement chunked, reliable ordered channels for file data.
  • WebTransport: lower-latency transport with multiplexing and better congestion control; increasingly available in modern browsers in 2025–2026.
  • Fallback relay: when NAT traversal fails, fall back to a short-lived relay server used for the data channel only; delete data immediately after transfer or don't persist files.

Step 6 — Chunking, resume, and progress UX

Large files require chunking, checksums, and resume support. This reduces user frustration and enables partial retries without restarting the entire transfer.

Chunk strategy

  • Pick a chunk size around 64KB–256KB depending on transport and latency.
  • Send a header message containing file metadata and a transfer UUID.
  • Use sequence numbers and HMAC of chunks or AES-GCM authentication tags for integrity.
  • Persist transfer state in IndexedDB so the receiver can resume if the PWA is backgrounded.
async function sendFileChunks(file, channel, sessionKey) {
  const chunkSize = 128 * 1024; // 128KB
  for (let offset = 0; offset < file.size; offset += chunkSize) {
    const slice = file.slice(offset, offset + chunkSize);
    const arrayBuffer = await slice.arrayBuffer();
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const encrypted = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, sessionKey, arrayBuffer);
    channel.send(encrypted);
    // update UI progress
  }
}

Security checklist — prevent abuse and privacy leaks

  • User confirmation: always require an explicit accept tap to receive any file or link.
  • Ephemeral keys: use ephemeral ECDH keys per session and discard them after transfer.
  • No server-side storage: if you must use a relay, clearly document and auto-delete relayed data; prefer signaling-only relays.
  • Content validation: check MIME and extension, integrate virus scanning in relay servers if you allow relays to store temporarily.
  • Permissions: use secure contexts (HTTPS), require installation for background features, and request Bluetooth permissions with a clear purpose string.
  • Rate limits and abuse controls: throttle share attempts and add a report/ban flow for suspicious devices.

DevOps and tooling: make this maintainable

Deploy this feature as part of your PWA pipeline with these recommendations:

  • Bundle with Vite (or modern bundlers) and code-split the crypto/transport bits so initial load is fast.
  • Use Workbox to generate a service worker for asset caching and to handle the Web Share Target route.
  • CI tests: run Lighthouse checks for PWA installability and accessibility; run Playwright/E2E tests that exercise share flow logic where possible.
  • Device testing: physical device matrix is essential (Android Pixel 9, Pixel 8, major Samsung phones, iPhone 15/16/17 families in 2026) because APIs and OS-level integrations vary.
  • Observability: instrument transfer start, success, fail, and average transfer times (no file content logged). Use privacy-preserving telemetry.

Testing strategies

  1. Unit test crypto and chunking logic in Node or browser-like runtimes.
  2. Automated E2E tests for share UI using Playwright — simulate navigator.share where possible.
  3. Manual, device-based QA: test QR path, BLE path (where available), and relay path across platforms.

Performance tips

  • Prefer WebCrypto over JS libraries for heavy crypto; it’s hardware-accelerated in many devices.
  • Defer loading large crypto/transport modules until the user opens the share modal (dynamic import in React).
  • Use streaming APIs (ReadableStream/WritableStream) with WebTransport where possible to avoid memory pressure for very large files.

Interoperability notes — Pixel 9, iPhone, and what to expect in 2026

Industry signals in late 2025 and early 2026 point toward fewer platform silos. Reports indicate Google is working to add cross-platform AirDrop compatibility for Pixel phones, and browser vendors are rapidly standardizing sharing-related APIs. However, do not rely on OS-level parity yet. Instead:

  • Treat system-level features (Nearby Share, Pixel AirDrop compatibility) as opportunistic performance/UX wins when available.
  • Design the PWA flow to work end-to-end without the OS feature: QR + WebRTC + E2E crypto will reach the same result in all browsers.
  • Monitor platform release notes and update the capability matrix in your app to take advantage of viral platform-level improvements as they ship.
Pro tip: if Pixel 9’s cross-platform layer is present, a simple navigator.share call often yields the best UX — treat browser-native integration as the high-priority path, but keep robust fallbacks.

Example end-to-end flow (User story)

A user on a Pixel 9 taps “Share nearby” inside your PWA. The app detects navigator.share with files support and calls navigator.share. The system share sheet appears and detects a nearby iPhone advertising the new cross-platform protocol — the OS does the heavy lifting. If the iPhone is not present or the web-based path is chosen, the PWA falls back to generating a QR code. The receiving device scans the QR, exchanges ephemeral public keys, creates a WebRTC connection via a short-lived relay, and receives the file chunks encrypted with AES-GCM — all with a single accept tap on the receiver.

Actionable checklist before you ship

  • Implement feature detection and a single share modal that chooses the best path.
  • Register a share_target in your manifest for Android/Chromium clients.
  • Implement ECDH key exchange via SubtleCrypto and use AES-GCM for chunk encryption.
  • Support WebRTC DataChannel and WebTransport for P2P data, with a relay fallback.
  • Persist transfer state for resume using IndexedDB and support background resume where the browser allows.
  • CI: add Lighthouse and Playwright tests; device lab tests for Pixel 9, other Androids, and iPhones.

Future predictions (2026–2027)

Expect the following trends to shape cross-device sharing:

  • More robust WebBluetooth or alternative proximity APIs on iOS, reducing the need for QR fallback.
  • Wider WebTransport adoption for low-latency, reliable transfers for PWAs.
  • Increased OS-level cross-platform sharing collaborations that web apps can leverage by calling navigator.share or using system-backed intents.

Final actionable takeaways

  • Default to platform primitives (Web Share API) for the lowest friction, but build a resilient web-native path that uses QR + WebRTC + E2E crypto.
  • Use ephemeral keys and SubtleCrypto to protect transfers; avoid shipping heavy JS crypto libs where possible.
  • Test on real devices, including Pixel 9 (or later) and iPhones — platform differences matter for sharing flows.
  • Automate with CI, Playwright, and Lighthouse checks, and instrument transfer metrics for ongoing optimization.

Call to action

Ready to build this into your next PWA? Start by adding a share_target to your manifest and wiring up an initial navigator.share fallback. If you want a jump-start, clone the sample starter repo (search for a PWA share-sheet starter in our GitHub org) and run the example across a Pixel and iPhone to see the fallbacks in action. Share your results with the community — file a PR with your findings so we can evolve the pattern as platforms continue to improve through 2026.

Advertisement

Related Topics

#PWA#sharing#cross-platform
U

Unknown

Contributor

Senior editor and content strategist. Writing about technology, design, and the future of digital media. Follow along for deep dives into the industry's moving parts.

Advertisement
2026-03-01T07:32:20.727Z