JS
() =>
JavaScript · Web Share API · PWA · Mobile UX · Progressive Enhancement
Web Share API: Nativer Teilen-Dialog
navigator.share() und Share Target API meistern

Benutzerdefinierte Share-Buttons mit hartkodierten Social-Media-Plattformen sind veraltet. Die Web Share API öffnet den nativen Teilen-Dialog des Betriebssystems – der Nutzer entscheidet, welche App er verwendet: WhatsApp, Telegram, Email, Notizen oder eine andere. Das Ergebnis ist eine bessere UX mit weniger Code und ohne Abhängigkeiten von Third-Party-SDKs.

10 Min. Lesezeit Web Share API · Share Target · PWA · navigator.share() Android · iOS · Chrome · Safari · Edge

1. Warum die Web Share API Social-Buttons überholt

Der klassische Ansatz für Share-Funktionalität auf Websites – eine Reihe von Buttons für Twitter, Facebook, WhatsApp, LinkedIn und Co. – hat mehrere fundamentale Probleme. Erstens sind diese Buttons mit den SDKs der jeweiligen Plattformen verknüpft, die Tracking-Skripte laden und die Datenschutz-Compliance verkomplizieren. Zweitens muss der Entwickler entscheiden, welche Plattformen angeboten werden – und trifft dabei fast immer die falsche Auswahl für einen Teil der Nutzer. Drittens sehen diese Buttons auf mobilen Geräten fehl am Platz aus, weil Nutzer auf dem Smartphone gewohnt sind, den nativen Teilen-Dialog des Betriebssystems zu verwenden.

Die Web Share API löst alle drei Probleme auf einmal. navigator.share() öffnet den systemeigenen Teilen-Dialog – auf Android das Material-Design-Share-Sheet, auf iOS das UIActivityViewController. Der Nutzer sieht dort alle Apps, die auf seinem Gerät als Teilen-Ziel registriert sind: WhatsApp, Telegram, Gmail, Notizen, AirDrop, jede installierte App. Der Entwickler schreibt keine einzige SDK-Zeile und lädt kein externes Skript. Das Ergebnis ist eine vollständig datenschutzneutrale Teilen-Funktion, die dem Nutzer mehr Freiheit gibt und gleichzeitig weniger Code benötigt.

navigator.share() akzeptiert ein Options-Objekt mit bis zu vier Properties: title (String), text (String), url (String) und files (Array von File-Objekten). Die API gibt ein Promise zurück, das resolves, wenn der Nutzer den Teilen-Vorgang abschließt, und rejects mit einem AbortError, wenn der Nutzer den Dialog abbricht, oder mit einem NotAllowedError, wenn der Aufruf nicht innerhalb eines User-Gesture-Handlers stattfindet. Das sind die zwei häufigsten Fehlerquellen bei der Web Share API-Integration.

Die Properties title, text und url sind alle optional, aber mindestens eine muss angegeben sein. In der Praxis ist url am konsistentesten: Fast alle Teilen-Apps verwenden die URL. text wird von manchen Apps als Nachrichtentext vorab befüllt (z.B. WhatsApp), von anderen ignoriert. title ist in manchen Implementierungen der Betreff (Email) oder der Titel des geteilten Links. Das Verhalten ist also app-spezifisch und nicht vollständig kontrollierbar – was den Vorteil hat, dass die Ziel-App selbst am besten weiß, wie sie die Informationen darstellt.


// Complete Web Share API integration with all best practices

class ShareButton extends HTMLElement {
  #button
  #fallback

  connectedCallback() {
    this.innerHTML = `
      <button class="share-btn" type="button">
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/>
          <line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/>
          <line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/>
        </svg>
        Teilen
      </button>
      <div class="share-fallback" hidden>
        <!-- Fallback: copy-to-clipboard button -->
        <button class="copy-btn" type="button">Link kopieren</button>
      </div>
    `
    this.#button = this.querySelector('.share-btn')
    this.#fallback = this.querySelector('.share-fallback')

    // Feature detection — show native share or fallback
    if ('share' in navigator) {
      this.#button.addEventListener('click', () => this.#nativeShare())
    } else {
      this.#button.hidden = true
      this.#fallback.hidden = false
      this.#fallback.querySelector('.copy-btn')
        .addEventListener('click', () => this.#copyFallback())
    }
  }

  async #nativeShare() {
    const shareData = {
      title: this.dataset.title || document.title,
      text: this.dataset.text || '',
      url: this.dataset.url || location.href,
    }

    try {
      await navigator.share(shareData)
      // Promise resolves when sharing is complete (or dismissed on some platforms)
      this.dispatchEvent(new CustomEvent('share-success', { bubbles: true }))
    } catch (err) {
      if (err.name === 'AbortError') {
        // User cancelled — not an error, do nothing
        return
      }
      // Unexpected error: fall through to clipboard fallback
      console.warn('navigator.share() failed:', err)
      await this.#copyFallback()
    }
  }

  async #copyFallback() {
    const url = this.dataset.url || location.href
    await navigator.clipboard.writeText(url)
    this.dispatchEvent(new CustomEvent('share-copied', { bubbles: true }))
  }
}

customElements.define('share-button', ShareButton)

3. Feature Detection und Progressive Enhancement

Die Web Share API ist nicht auf allen Plattformen verfügbar. Der zuverlässigste Check ist 'share' in navigator. Safari auf iOS unterstützt die API seit iOS 12.2, Chrome auf Android seit Chrome 61. Desktop-Unterstützung ist seit 2023 deutlich besser: Chrome und Edge auf Windows und macOS unterstützen navigator.share(), Firefox hingegen bis 2026 nicht im stabilen Kanal. Die Feature-Detection muss deshalb zwingend implementiert werden – kein Code sollte navigator.share() aufrufen, ohne vorher zu prüfen, ob die Methode existiert.

Progressive Enhancement bedeutet hier: Die Basis-Funktionalität (Link teilen) funktioniert immer. Wenn die Web Share API verfügbar ist, wird der native Dialog gezeigt. Wenn nicht, gibt es einen Fallback auf "Link kopieren" mit der Clipboard API. Falls auch die Clipboard API nicht verfügbar ist (was in unsicheren Kontexten ohne HTTPS passiert), bleibt ein einfaches Text-Input, aus dem der Nutzer manuell kopieren kann. Diese dreistufige Degradation stellt sicher, dass die Funktion auf jedem Browser und in jeder Netzwerkkonfiguration nutzbar bleibt.

4. Dateien teilen mit navigator.share() und canShare()

Die Dateifreigabe über die Web Share API ist Level 2 der Spezifikation und erfordert eine explizite Prüfung mit navigator.canShare({ files }). Das ist aus gutem Grund so: Nicht alle Teilen-Ziele unterstützen Dateianhänge, und nicht alle Browser-Implementierungen erlauben alle Dateitypen. canShare() nimmt dasselbe Options-Objekt wie share() und gibt einen Boolean zurück – ohne Promise, synchron. Es ist der einzige zuverlässige Weg zu prüfen, ob eine spezifische Teilen-Konfiguration auf dem aktuellen Gerät unterstützt wird.

Erlaubte Dateitypen für die Web Share API sind von der Spezifikation auf "safe list"-Typen beschränkt: Bilder (image/png, image/jpeg, image/webp, image/gif), Audio (audio/flac, audio/mp4, audio/mpeg, audio/ogg, audio/wav), Video (video/mp4, video/ogg) und Text (text/plain, text/csv, text/html, application/pdf). Ausführbare Dateien sind bewusst ausgeschlossen. Das praktische Anwendungsbeispiel: Eine Web-App für Bildbearbeitung kann das bearbeitete Bild direkt an WhatsApp oder per AirDrop teilen – ohne Server-Upload, ohne externe Dependencies.


// Sharing files — Level 2 Web Share API with canShare() guard

async function shareCanvasAsImage(canvas, filename = 'image.png') {
  // Convert canvas to Blob
  const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png'))
  const file = new File([blob], filename, { type: 'image/png' })

  const shareData = {
    files: [file],
    title: 'Mein Bild',
    text: 'Erstellt mit mironsoft.de',
  }

  // canShare() is synchronous — check before calling share()
  if (!('share' in navigator) || !('canShare' in navigator)) {
    console.warn('Web Share API not available — fallback to download')
    return downloadFile(blob, filename)
  }

  if (!navigator.canShare(shareData)) {
    console.warn('File type not shareable on this device')
    return downloadFile(blob, filename)
  }

  try {
    await navigator.share(shareData)
  } catch (err) {
    if (err.name !== 'AbortError') {
      console.error('Share failed:', err)
      downloadFile(blob, filename)
    }
  }
}

function downloadFile(blob, filename) {
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = filename
  a.click()
  // Revoke object URL after download starts
  setTimeout(() => URL.revokeObjectURL(url), 1000)
}

5. User Gesture: Die versteckte Einschränkung

Eine der häufigsten Fehlerquellen bei der Web Share API-Implementierung ist die User-Gesture-Anforderung. navigator.share() darf nur als direkte Reaktion auf eine Benutzeraktion aufgerufen werden – also innerhalb eines Click-, Touch- oder Keyboard-Event-Handlers. Ein Aufruf aus einem setTimeout, einem Promise-Callback oder einem Fetch-Response-Handler heraus wird mit einem NotAllowedError abgelehnt. Browser erzwingen dies, um zu verhindern, dass Share-Dialoge ohne Nutzerabsicht geöffnet werden.

Das Problem tritt typischerweise auf, wenn der Share-Aufruf nach einer asynchronen Operation steht: "Lade zuerst die Daten, dann teile sie." Da der ursprüngliche Click-Event-Handler schon lange beendet ist, wenn das Fetch abgeschlossen ist, gilt der Kontext als "nicht vertrauenswürdig". Die Lösung: Daten vorab laden oder vorbereiten, bevor der Nutzer auf "Teilen" klickt, und dann den navigator.share()-Aufruf direkt im synchronen Teil des Event-Handlers platzieren. Bei der File-API ist eine Ausnahme möglich: Blobs können vorher erstellt werden, solange der finale share()-Aufruf synchron im Event-Handler erfolgt.

6. Share Target API: PWA als Empfänger registrieren

Die Share Target API ist die Umkehrung der Web Share API: Statt Inhalte zu teilen, registriert die PWA sich selbst als Empfänger für Inhalte anderer Apps. Wenn ein Nutzer ein Bild in der Galerie-App mit "Teilen" öffnet, erscheint die installierte PWA im Teilen-Dialog neben WhatsApp und anderen Apps. Die Registrierung erfolgt im Web App Manifest über den share_target-Schlüssel.

Die Share Target API unterstützt zwei Modi: GET-Parameter für einfache Text/URL-Shares und POST-Multipart für Datei-Shares. Im GET-Modus öffnet das Betriebssystem die PWA mit einer URL wie /share?title=...&text=...&url=.... Im POST-Modus sendet es einen Multipart-Form-Request an einen Service Worker. Der Service Worker empfängt die Daten, speichert sie in IndexedDB oder dem Cache und öffnet dann das richtige Fenster mit den Daten. Die Implementierung erfordert sowohl einen aktiven Service Worker als auch eine korrekte Manifest-Konfiguration.


// web app manifest snippet — Share Target API registration
// Add to manifest.json:
// {
//   "share_target": {
//     "action": "/share-handler",
//     "method": "POST",
//     "enctype": "multipart/form-data",
//     "params": {
//       "title": "title",
//       "text": "text",
//       "url": "url",
//       "files": [{ "name": "media", "accept": ["image/*", "video/*"] }]
//     }
//   }
// }

// Service worker: handle incoming share via fetch event
self.addEventListener('fetch', (event) => {
  const url = new URL(event.request.url)

  if (url.pathname === '/share-handler' && event.request.method === 'POST') {
    event.respondWith(handleShareTarget(event.request))
  }
})

async function handleShareTarget(request) {
  const formData = await request.formData()

  const title = formData.get('title') || ''
  const text  = formData.get('text')  || ''
  const url   = formData.get('url')   || ''
  const files = formData.getAll('media') // File objects

  // Store shared data for the app window to pick up
  const cache = await caches.open('share-data')
  const payload = JSON.stringify({ title, text, url, fileCount: files.length })
  await cache.put('/share-payload', new Response(payload))

  // Store file blobs
  for (let i = 0; i < files.length; i++) {
    const blob = files[i]
    await cache.put(`/share-file-${i}`, new Response(blob))
  }

  // Open or focus the share page
  const clients = await self.clients.matchAll({ type: 'window' })
  if (clients.length > 0) {
    clients[0].focus()
    clients[0].navigate('/app?share=1')
  } else {
    await self.clients.openWindow('/app?share=1')
  }

  // Respond with redirect — browser follows it after sharing
  return Response.redirect('/app?share=1', 303)
}

7. Fallback-Strategien für Desktop und Firefox

Für Desktop-Browser ohne Web Share API-Unterstützung – insbesondere Firefox, das die API bis 2026 noch nicht im stabilen Kanal unterstützt – braucht man einen ergonomischen Fallback. Die beste Fallback-Strategie 2026 ist die Kombination aus Clipboard API und einem kleinen, modal-artigen "Share-Panel". Das Panel zeigt einen vorausgefüllten Link-Input mit einem "Kopieren"-Button und optional direkte Links zu den wichtigsten Plattformen. Dieses Panel erscheint nur, wenn 'share' in navigator false ergibt.

Die Clipboard API (navigator.clipboard.writeText()) ist ebenfalls eine Promise-basierte API und erfordert einen sicheren Kontext (HTTPS oder localhost). Auf älteren Browsern oder bei fehlendem HTTPS ist der Fallback auf document.execCommand('copy') nötig, das als deprecated gilt, aber noch weit unterstützt wird. Die dreischichtige Strategie (native Share → Clipboard → execCommand) deckt nahezu alle Browser-Szenarien ab. Der fallback sollte gut aussehen und nützlich sein – nicht als "Notlösung" mit einem unlesbaren Rohlink behandelt werden.

8. Share-Events tracken ohne Datenschutzverletzung

Ein oft übersehener Vorteil der Web Share API: Sie teilt der Website nicht mit, welche App der Nutzer gewählt hat oder ob der Share tatsächlich gesendet wurde. Das Promise von navigator.share() resolved, sobald der Dialog geöffnet und die Auswahl getroffen wurde – nicht wenn die Nachricht in WhatsApp tatsächlich versendet wurde. Das ist aus Datenschutzsicht exzellent, aber für Analytics-Zwecke muss man mit dem begrenzen, was messbar ist: Share-Dialog wurde geöffnet (resolved) vs. abgebrochen (AbortError).

Für eine datenschutzkonforme Messung von Teilen-Events reicht es, einen Event beim erfolgreichen await navigator.share() zu loggen. Ein Custom Event (share-intent) kann mit dispatchEvent ausgelöst und von einem Analytics-System aufgefangen werden. Wichtig: Keine Plattform-spezifischen Informationen speichern, weil die Web Share API diese bewusst nicht liefert. Das ist der richtige Ansatz: Analytics soll wissen, dass geteilt wurde, nicht wie und wohin – das ist Privatsphäre by Design.

Browser/Plattform navigator.share() Datei-Share Share Target
Chrome Android Ja (seit 61) Ja Ja
Safari iOS Ja (seit 12.2) Ja (seit 15) Nein
Chrome Desktop Ja (seit 89) Ja Teilweise
Safari macOS Ja (seit 12.1) Eingeschränkt Nein
Firefox Nein (2026) Nein Nein
Edge Desktop Ja Ja Teilweise

9. Web Share API im Browser-Vergleich

Die Browser-Unterstützung der Web Share API ist in 2026 auf mobilen Plattformen ausgezeichnet: Nahezu alle Smartphones – iOS und Android – können mit einem einzigen navigator.share()-Aufruf den nativen Teilen-Dialog öffnen. Auf Desktop-Plattformen ist das Bild differenzierter. Chrome und Edge auf macOS und Windows unterstützen die API und öffnen ein OS-natives Share-Sheet oder ein einfaches Email-/Clipboard-Interface. Safari auf macOS öffnet den AirDrop/macOS-Sharing-Dialog. Firefox auf allen Plattformen fehlt.

Die Tatsache, dass Firefox die Web Share API nicht unterstützt, ist für die meisten Web-Apps kein kritisches Problem, da Firefox-Nutzer – besonders auf Desktop – erfahrungsgemäß eher die URL manuell kopieren oder Browser-eigene Teilen-Funktionen verwenden. Das Wichtigste: Die Feature-Detection und der Fallback müssen sauber implementiert sein. Eine Website, die bei Firefox-Nutzern keinen Teilen-Button zeigt, ist besser als eine, die auf einen Klick mit einem JavaScript-Fehler reagiert. Progressive Enhancement ist hier keine optionale Best Practice, sondern eine funktionale Notwendigkeit.

10. Zusammenfassung

Die Web Share API ist eine der elegantesten modernen Browser-APIs: Wenig Code, große UX-Verbesserung, null externe Abhängigkeiten und eingebauter Datenschutz. navigator.share() für Text/URL-Shares und navigator.canShare() plus File-Share für Dateien decken die meisten Anwendungsfälle ab. Die Share Target API macht eine PWA zum vollständigen Teilen-Ökosystem-Mitglied – die App erscheint im nativen Teilen-Dialog anderer Anwendungen.

Die Implementierungs-Checkliste für die Web Share API: Feature-Detection mit 'share' in navigator, User-Gesture-Kontexte strikt einhalten, AbortError von echten Fehlern unterscheiden, canShare() vor Datei-Shares aufrufen, und einen sauberen Fallback für Firefox und Desktop-Browser implementieren. Wer diese Punkte beachtet, ersetzt eine Sammlung von Third-Party-Share-Buttons durch eine einzige, saubere API-Integration, die auf mobilen Geräten die deutlich bessere User-Experience liefert.

Web Share API — Das Wichtigste auf einen Blick

Feature Detection

'share' in navigator vor jedem Aufruf prüfen. Firefox unterstützt die API nicht — Fallback auf Clipboard API implementieren.

User Gesture Pflicht

navigator.share() darf nur direkt in einem Click/Touch-Handler aufgerufen werden. Asynchrone Chains zerstören den Gesture-Kontext.

canShare() für Dateien

Vor jedem Datei-Share canShare({ files }) synchron prüfen. Nur erlaubte Dateitypen (Bilder, Audio, Video, Text) werden akzeptiert.

Share Target API

share_target im Manifest registrieren, Service Worker für POST-Handler einrichten — PWA erscheint im nativen Teilen-Dialog anderer Apps.

Mironsoft

Progressive Web Apps, Browser-APIs und mobile UX-Optimierung

PWA mit Web Share API und Share Target?

Wir implementieren die Web Share API mit korrekter Feature-Detection, vollständigem Fallback und Share Target-Integration in eure PWA – sauber, datenschutzkonform und ohne externe Share-Bibliotheken.

Share-Integration

navigator.share() mit korrektem Fallback und Datei-Share-Unterstützung

Share Target API

Manifest-Konfiguration und Service Worker für empfangene Shares

PWA-Audit

Vollständige PWA-Analyse: Manifest, Service Worker, Installierbarkeit und Web APIs

11. FAQ: Web Share API

1Was passiert ohne Feature-Check auf nicht unterstützten Browsern?
TypeError: navigator.share is not a function. Immer 'share' in navigator prüfen — nie direkt aufrufen ohne Check.
2Kann ich die gewählte App erfahren?
Nein. Die API gibt diese Info bewusst nicht preis. Das Promise resolved nur, dass eine Auswahl getroffen wurde — nicht welche App oder ob gesendet wurde.
3Warum NotAllowedError?
Kein User-Gesture-Kontext. navigator.share() muss direkt in einem Click/Touch-Handler stehen — nicht nach await oder setTimeout.
4Welche Dateitypen sind erlaubt?
Bilder, Audio, Video, Text und PDF. Ausführbare Dateien nicht. Immer canShare({ files }) prüfen bevor share() aufgerufen wird.
5Web Share API vs. Share Target API?
navigator.share() sendet Inhalte an andere Apps. Share Target empfängt Inhalte von anderen Apps. Beide zusammen = vollständiges PWA-Teilen-Ökosystem.
6Muss die Seite HTTPS haben?
Ja. Sicherer Kontext (HTTPS oder localhost) ist Pflicht. Auf HTTP ist navigator.share nicht verfügbar.
7Share Target für Datei-Empfang implementieren?
Manifest: share_target mit method POST, multipart. Service Worker: fetch-Event abfangen, FormData lesen, Dateien in Cache speichern, Fenster öffnen/fokussieren.
8AbortError vs. echte Fehler unterscheiden?
catch: if (err.name === 'AbortError') return; — Nutzer hat abgebrochen, kein Fehler. Alle anderen Namen: Fallback auf Clipboard zeigen.
9Produkt-Sharing im E-Commerce-Shop?
Ja, excellenter Anwendungsfall. title: Produktname, text: Beschreibung, url: Produkt-URL. Nativer Dialog auf Mobile, Clipboard-Fallback für Desktop.
10Daten dynamisch vor dem Share laden?
Daten beim Page-Load oder Hover vorladen. Dann navigator.share() synchron im Click-Handler aufrufen — nicht nach einem await im Handler selbst.