Build APK + AAB

JavaScript bridge API

Share, clipboard and print

The Android share sheet, the system clipboard, text another app shared into yours, and the print dialogue.

navigator.clipboard web standard#

navigator.clipboard.writeText / readText

Example

The standard Clipboard API for text, answered by the phone's real clipboard inside the app.

Returns: writeText(text) → a Promise that resolves when copied and rejects with Error('Copy failed') otherwise. readText() → a Promise with the clipboard text ("" when empty). Needs: nothing.

Copy:

async function copyLink(link) {
  if (!(navigator.clipboard && navigator.clipboard.writeText)) { alert('Copy is not available'); return; }
  try {
    await navigator.clipboard.writeText(link);
    showMessage('Link copied');
  } catch (e) {
    showMessage('Could not copy');
  }
}

Paste - from a tap:

document.getElementById('paste').onclick = async function () {
  try {
    var text = await navigator.clipboard.readText();
    document.getElementById('input').value = text;
  } catch (e) {
    showMessage('Could not read the clipboard');
  }
};

Images and HTML - write() / read() with ClipboardItem:

async function copyChart(canvas) {
  const png = await new Promise((r) => canvas.toBlob(r, "image/png"));
  await navigator.clipboard.write([new ClipboardItem({ "image/png": png })]);
  showMessage("Chart copied — paste it into a chat");
}

async function pasteAnything() {
  const [item] = await navigator.clipboard.read();
  if (!item) return;
  if (item.types.includes("image/png")) showImage(URL.createObjectURL(await item.getType("image/png")));
  else if (item.types.includes("text/plain")) insertText(await (await item.getType("text/plain")).text());
}

Notes: The app version is installed before the page first runs (document start); writeText/readText call WebToApk.copyToClipboard / WebToApk.readClipboard, and write/read carry text/plain, text/html and one image (PNG, JPEG, WebP) - the image is copied as a real image, so it pastes into WhatsApp or Gmail. Other types reject with NotAllowedError. A foreign website opened inside the app reads "" and read() rejects NotAllowedError. In a browser the same code works with the browser's own permission rules.

navigator.share web standard#

navigator.share(data) / navigator.canShare(data)

Example

The standard Web Share API, made to work inside the app: text and links open the Android share sheet, and files are shared as real files (WhatsApp, Gmail, Drive…).

Returns: navigator.share → a Promise that resolves when the sheet opens (it does not tell you which app was picked). navigator.canShare → true/false. Needs: nothing. Installed at document start, so a check made while your page parses already sees it.

Text and a link:

async function shareItem(item) {
  if (!navigator.share) { alert('Sharing is not available here'); return; }
  try {
    await navigator.share({ title: item.name, text: 'Look at this', url: item.link });
  } catch (e) { /* in a browser: AbortError when the user closes the sheet */ }
}

Files - check with canShare first:

async function sharePdf(blob) {
  var file = new File([blob], 'invoice.pdf', { type: 'application/pdf' });
  var data = { files: [file], title: 'Invoice', text: 'Invoice for March' };
  if (!(navigator.canShare && navigator.canShare(data))) {
    alert('File sharing is not available here');
    return;
  }
  try {
    await navigator.share(data);
  } catch (e) { console.log('Share failed', e); }
}

Several files the user picked - photos of one type share with that type:

document.getElementById('photos').onchange = async function (e) {
  var files = Array.from(e.target.files);
  if (!files.length) return;
  var data = { files: files, title: 'Trip photos' };
  if (!(navigator.canShare && navigator.canShare(data))) {
    alert('File sharing is not available here');
    return;
  }
  try {
    await navigator.share(data);
  } catch (err) { console.log('Share failed', err); }
};

Notes: In the app title becomes the subject, and text + url are sent as one text. With files, each file is read in the page and sent as base64 (WebToApk.shareFiles), so keep files to a few MB. If the share sheet cannot open, the files are saved as downloads instead and the promise still resolves. canShare returns true for any text share. AI-built apps can use share from @/lib/appmintNative.

window.print web standard#

window.print()

Example

The standard print call, made to work inside the app: it opens Android's print screen, where the user prints on paper or saves a PDF.

Returns: nothing. Needs: nothing.

document.getElementById('print-receipt').onclick = function () {
  window.print();
};

Hide the app's controls on paper with a print stylesheet:

<style>
  @media print {
    nav, .toolbar, button { display: none !important; }
    body { background: #fff; color: #000; }
  }
</style>

Print a shipping label without the page around it - a same-origin srcdoc iframe prints only its own document:

function esc(s) {
  return String(s).replace(/[&<>"]/g, function (c) {
    return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c];
  });
}
function printLabel(name, address) {
  var frame = document.createElement('iframe');
  frame.style.cssText = 'position:fixed;width:0;height:0;border:0';
  frame.srcdoc = '<title>Label</title><style>body{font:18px sans-serif;margin:24px}</style>' +
                 '<b>' + esc(name) + '</b><br>' + esc(address).replace(/\n/g, '<br>');
  frame.onload = function () {
    frame.contentWindow.print();
    setTimeout(function () { frame.remove(); }, 1000);
  };
  document.body.appendChild(frame);
}

Notes: window.print() prints the main page as it is drawn now. A same-origin <iframe> - the hidden iframe a print library fills with a receipt and calls iframe.contentWindow.print() on, or an iframe page calling its own window.print() - prints THAT iframe's document (see __printHtml). A cross-origin iframe cannot be printed from the page. The replacement exists before the page's first script. beforeprint cannot be cancelled (in any browser).

__clipboardRead bridge#

window.WebToApk.__clipboardRead(): String

navigator.clipboard.read(): `{parts:[{type, text|base64}]}`, or `{denied:true}` for a page that is not one of this app's own origins.

Example

Internal transport behind navigator.clipboard.read(). Pages use the standard API; never call this directly - the __ methods may change without notice.

Returns: {parts:[{type, text|base64}]}, or {denied:true} on a page that is not one of the app's own origins. Needs: nothing to switch on.

Use the public API:

canvas.toBlob(async (png) => {
  await navigator.clipboard.write([new ClipboardItem({ 'image/png': png })]);
});
const [item] = await navigator.clipboard.read();
if (item && item.types.includes('image/png')) preview(await item.getType('image/png'));

Paste formatted text into an editor, keeping the formatting when the clipboard has HTML:

async function pasteRich(editor) {
  try {
    var items = await navigator.clipboard.read();
    if (!items.length) { showMessage('The clipboard is empty'); return; }
    var item = items[0];
    if (item.types.includes('text/html')) {
      editor.insertHtml(sanitize(await (await item.getType('text/html')).text()));
    } else if (item.types.includes('text/plain')) {
      editor.insertText(await (await item.getType('text/plain')).text());
    }
  } catch (e) {
    showMessage('Could not read the clipboard');
  }
}

Paste any copied picture (PNG, JPEG, WebP…) as a profile photo, and tell a refused read apart:

document.getElementById('paste-photo').onclick = async function () {
  try {
    var items = await navigator.clipboard.read();
    var item = items[0];
    var type = item && item.types.find(function (t) { return t.indexOf('image/') === 0; });
    if (!type) { showMessage('Copy a picture first'); return; }
    uploadAvatar(await item.getType(type));   // a Blob of that type
  } catch (e) {
    if (e.name === 'NotAllowedError') showMessage('This page cannot read the clipboard');
    else showMessage('Could not paste the picture');
  }
};

Notes: Android 12+ shows its own "pasted from your clipboard" message when an app reads the clipboard.

__clipboardWrite bridge#

window.WebToApk.__clipboardWrite(partsJson: String): Boolean

navigator.clipboard.write(): [partsJson] is `[{type, text}|{type, base64}]` for one ClipboardItem - text/plain, text/html, or an image that is copied as a content URI.

Example

Internal transport behind navigator.clipboard.write([ClipboardItem]). Pages use the standard API; never call this directly - the __ methods may change without notice.

Returns: true when the clip was placed. Needs: nothing to switch on.

Use the public API:

canvas.toBlob(async (png) => {
  await navigator.clipboard.write([new ClipboardItem({ 'image/png': png })]);
});
const [item] = await navigator.clipboard.read();
if (item && item.types.includes('image/png')) preview(await item.getType('image/png'));

Copy formatted text and its plain version in one item, so rich editors get the table and plain fields get the text:

async function copyTable(table) {
  if (!(navigator.clipboard && navigator.clipboard.write && window.ClipboardItem)) return;
  try {
    await navigator.clipboard.write([new ClipboardItem({
      'text/html': new Blob([table.outerHTML], { type: 'text/html' }),
      'text/plain': new Blob([table.innerText], { type: 'text/plain' })
    })]);
    showMessage('Table copied');
  } catch (e) {
    showMessage('Could not copy (' + e.name + ')');   // NotAllowedError when the write fails
  }
}

Copy an image the page shows, such as a QR code, so it can be pasted into a chat:

async function copyImage(img) {
  try {
    var blob = await (await fetch(img.src)).blob();   // an image/* type, up to 16 MB
    await navigator.clipboard.write([new ClipboardItem({ [blob.type]: blob })]);
    showMessage('Image copied');
  } catch (e) {
    showMessage('Could not copy the image');
  }
}

Notes: Text, HTML and one image per item. The image is copied as a content URI through the app's FileProvider, so it pastes into WhatsApp, Gmail or Keep.

__printHtml bridge#

window.WebToApk.__printHtml(html: String, baseUrl: String, title: String)

Prints [html] - a same-origin iframe's serialised document, sent by the `window.print()` shim ([PrintShim]) - through an offscreen WebView at [baseUrl], because the main WebView's print adapter can only print the whole page. [title] names the print job ("<App name> Document" when blank).

Example

Internal transport behind printing an iframe: iframe.contentWindow.print() (and an iframe page's own window.print()) sends that iframe's document here, and the app prints it through Android's print screen. Pages use the standard calls; never call this directly.

Returns: nothing. Needs: nothing.

The receipt pattern this makes work - a hidden iframe filled with just the receipt:

function printReceipt(order) {
  var frame = document.createElement('iframe');
  frame.style.display = 'none';
  document.body.appendChild(frame);
  var doc = frame.contentWindow.document;
  doc.open();
  doc.write('<html><head><title>Receipt ' + order.id + '</title>' +
            '<style>body{font:14px sans-serif} td{padding:2px 8px}</style></head><body>' +
            '<h2>Receipt ' + order.id + '</h2><table>' +
            order.lines.map(function (l) { return '<tr><td>' + l.name + '</td><td>' + l.price + '</td></tr>'; }).join('') +
            '</table></body></html>');
  doc.close();
  frame.contentWindow.print();   // prints the RECEIPT, not the page behind it
  setTimeout(function () { frame.remove(); }, 1000);
}

A ticket page of your own, loaded into a hidden iframe from the same origin - its CSS and images load, and its <title> names the print job:

function printTicket(id) {
  var frame = document.createElement('iframe');
  frame.style.cssText = 'position:fixed;width:0;height:0;border:0';
  frame.src = 'ticket.html?id=' + encodeURIComponent(id);
  frame.onload = function () {
    frame.contentWindow.print();   // prints ticket.html's DOM as it stands now
    setTimeout(function () { frame.remove(); }, 1000);
  };
  document.body.appendChild(frame);
}

Adjust the iframe's document just before the snapshot with beforeprint on the iframe's window:

function printWithTimestamp(frame) {
  var w = frame.contentWindow;
  w.addEventListener('beforeprint', function () {
    var p = w.document.createElement('p');
    p.textContent = 'Printed ' + new Date().toLocaleString();
    w.document.body.appendChild(p);
  }, { once: true });
  w.print();   // the printed copy includes the line added above
}

Notes: Arguments are (html, baseUrl, title): the iframe's serialised document, the address its relative links resolve against (the page's own address for an about:blank iframe), and the print job name (the iframe's <title>, else the page's). The snapshot is taken when print() is called - scripts inside it do not run again - and beforeprint / afterprint fire on the iframe's window around it. A cross-origin iframe cannot be printed from the page.

clearSharedText bridge#

window.WebToApk.clearSharedText()

Clears the kept share once the page has consumed it.

Example

Forgets the text or link that was shared into the app, once your page has used it.

Returns: nothing. Needs: nothing (the share itself needs Receive Shares From Other Apps with Text and links ticked).

Read the kept share, use it, then clear it so it is not handled again after a reload:

if (window.WebToApk && typeof window.WebToApk.getSharedText === 'function') {
  var raw = window.WebToApk.getSharedText();
  if (raw) {
    var shared = JSON.parse(raw);          // { text, subject, url, receivedAt }
    saveNote(shared.subject, shared.text);
    window.WebToApk.clearSharedText();     // getSharedText() now returns ""
  }
}

window.addEventListener('appmint:shared', function (e) {
  saveNote(e.detail.subject, e.detail.text);
  if (window.WebToApk && window.WebToApk.clearSharedText) window.WebToApk.clearSharedText();
});

Ask first, and clear the share whichever button the user taps:

function askAboutShare(s) {
  var dialog = document.getElementById('share-dialog');
  document.getElementById('share-preview').textContent = s.text;
  dialog.hidden = false;
  function done() {
    dialog.hidden = true;
    if (window.WebToApk && typeof window.WebToApk.clearSharedText === 'function') {
      window.WebToApk.clearSharedText();
    }
  }
  document.getElementById('share-save').onclick = function () { saveNote(s.subject, s.text); done(); };
  document.getElementById('share-discard').onclick = done;
}

Clear it only once it is safely saved, so a failed upload can be retried:

async function importShare() {
  if (!(window.WebToApk && typeof window.WebToApk.getSharedText === 'function')) return;
  var raw = window.WebToApk.getSharedText();
  if (!raw) return;
  var s = JSON.parse(raw);
  try {
    var res = await fetch('/api/bookmarks', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ url: s.url || s.text, title: s.subject })
    });
    if (!res.ok) throw new Error('HTTP ' + res.status);
    window.WebToApk.clearSharedText();
  } catch (e) {
    showMessage('Could not save the link — tap Retry');   // Retry calls importShare() again
  }
}

Notes: Clearing only affects getSharedText(). A new share replaces the kept one and fires appmint:shared again. Files shared into the app are released separately with AppMint.releaseOpenedFile().

copyToClipboard bridge#

window.WebToApk.copyToClipboard(text: String): Boolean

The Android share sheet, the system clipboard, and closing a window the page opened. These are the plain-web equivalents (navigator.share, navigator.clipboard, window.close) answered natively so they behave the same inside the app.

Described by its group, Share, clipboard and window, rather than on its own.

Example

Copies plain text to the phone's clipboard. Pages normally use the standard navigator.clipboard.writeText(text), which the shell routes here.

Returns: true on success, false on failure (synchronously). Needs: nothing.

The standard API first:

document.getElementById('copy-btn').onclick = async function () {
  try {
    await navigator.clipboard.writeText('PROMO-2026');
    showMessage('Copied');
  } catch (e) {
    showMessage('Could not copy');
  }
};

The raw call:

if (window.WebToApk && typeof window.WebToApk.copyToClipboard === 'function') {
  var ok = window.WebToApk.copyToClipboard('PROMO-2026');
  showMessage(ok ? 'Copied' : 'Could not copy');
}

A "Copy" button on every code block, using the synchronous answer to update the label:

document.querySelectorAll('pre').forEach(function (pre) {
  var btn = document.createElement('button');
  btn.textContent = 'Copy';
  btn.onclick = function () {
    var ok = !!(window.WebToApk && typeof window.WebToApk.copyToClipboard === 'function' &&
                window.WebToApk.copyToClipboard(pre.innerText));
    btn.textContent = ok ? 'Copied' : 'Copy failed';
    setTimeout(function () { btn.textContent = 'Copy'; }, 1500);
  };
  pre.parentNode.insertBefore(btn, pre);
});

Notes: Plain text only (no images or HTML). Android 13+ shows its own small "Copied" preview, so you may not need your own message there.

getSharedText bridge#

window.WebToApk.getSharedText(): String

Text or a link another app shared INTO this one ("Share to <app>" - a YouTube video shared from the YouTube app arrives as its link), as JSON `{text, subject, url, receivedAt}` or "" when nothing was shared. Kept until read, so an app that mounts after the share still sees it; a share arriving while the app runs also fires `appmint:shared`. A photo or video shared with a caption delivers the file through AppMint.getOpenedFile() and the caption here. Requires the build's "Receive shares from other apps" option with "Text and links" ticked, which registers the share-sheet entry.

Example

Text or a link that another app shared INTO your app ("Share → your app"), as a JSON string, or "" when nothing was shared.

Returns: a JSON string {text, subject, url, receivedAt} or "" (synchronously). url is the first http(s):// link found in text (or ""); receivedAt is a time in milliseconds. Needs: Receive Shares From Other Apps switched on when you build, with Text and links ticked - that puts your app in other apps' Share menu.

The share is kept until you clear it, so read it on start, and listen for shares that arrive while the app runs:

function handleShare(s) {
  addBookmark(s.url || s.text, s.subject);
  if (window.WebToApk && window.WebToApk.clearSharedText) window.WebToApk.clearSharedText();
}

// 1. On start (the app may have been opened by the share).
if (window.WebToApk && typeof window.WebToApk.getSharedText === 'function') {
  var raw = window.WebToApk.getSharedText();
  if (raw) handleShare(JSON.parse(raw));
}

// 2. While running: detail has the same keys { text, subject, url, receivedAt }.
window.addEventListener('appmint:shared', function (e) {
  handleShare(e.detail);
});

A global hook receives the same object, if you prefer one:

window.onAppMintShared = function (s) { handleShare(s); };

Open a YouTube video that was shared into the app, using the url field:

function openSharedVideo() {
  if (!(window.WebToApk && typeof window.WebToApk.getSharedText === 'function')) return;
  var raw = window.WebToApk.getSharedText();
  if (!raw) return;
  var s = JSON.parse(raw);
  var m = /(?:youtu\.be\/|[?&]v=)([\w-]{11})/.exec(s.url || s.text);
  if (m) {
    showVideo(m[1]);
    window.WebToApk.clearSharedText();
  } else {
    showMessage('That share is not a video link');
  }
}

Notes: A YouTube video shared from the YouTube app arrives as its link. A photo or video shared WITH a caption delivers the file through AppMint.getOpenedFile() and the caption here. Clear the share after you use it, or you will see it again on the next start. AI-built apps can use sharedText() / onShared() from @/lib/appmintNative.

print bridge#

window.WebToApk.print()

Hands the page as it currently stands to Android's print dialogue, which can print on paper or save a PDF. Nothing to configure - the WebView renders the document.

Described by its group, Printing, rather than on its own.

Example

Opens Android's print screen for the page as it looks now - print on paper or "Save as PDF". Pages normally use the standard window.print(), which the shell routes here.

Returns: nothing. Needs: nothing.

The standard API first:

document.getElementById('print-btn').onclick = function () {
  window.print();   // in the app: the Android print screen
};

The raw call does the same:

if (window.WebToApk && typeof window.WebToApk.print === 'function') {
  window.WebToApk.print();
}

Print one invoice out of a list by marking it for a print stylesheet (leave the mark on - the print screen lays the page out after print() returns):

// CSS: @media print { .invoice:not(.print-me), nav, button { display: none !important; } }
function printInvoice(id) {
  document.querySelectorAll('.invoice.print-me').forEach(function (el) {
    el.classList.remove('print-me');
  });
  document.getElementById('invoice-' + id).classList.add('print-me');
  if (window.WebToApk && typeof window.WebToApk.print === 'function') {
    window.WebToApk.print();
  } else {
    window.print();   // a normal browser
  }
}

Notes: It prints the whole WebView - the main page. A same-origin iframe's own print() prints just that iframe (see window.print). Use a @media print stylesheet to hide buttons and menus. The job is named "<App name> Document". Errors are only logged.

readClipboard bridge#

window.WebToApk.readClipboard(): String

The Android share sheet, the system clipboard, and closing a window the page opened. These are the plain-web equivalents (navigator.share, navigator.clipboard, window.close) answered natively so they behave the same inside the app.

Described by its group, Share, clipboard and window, rather than on its own.

Example

Reads the text that is on the phone's clipboard now. Pages normally use the standard navigator.clipboard.readText(), which the shell routes here.

Returns: the text, or "" when the clipboard is empty or cannot be read (synchronously). Needs: nothing.

The standard API first - for example a "Paste code" button:

document.getElementById('paste-btn').onclick = async function () {
  try {
    var text = await navigator.clipboard.readText();
    if (text) document.getElementById('code').value = text.trim();
    else showMessage('The clipboard is empty');
  } catch (e) {
    showMessage('Could not read the clipboard');
  }
};

The raw call:

if (window.WebToApk && typeof window.WebToApk.readClipboard === 'function') {
  var text = window.WebToApk.readClipboard();
  if (text) document.getElementById('code').value = text;
}

A "Paste link" button that takes the first web address out of whatever was copied:

document.getElementById('paste-link').onclick = function () {
  if (!(window.WebToApk && typeof window.WebToApk.readClipboard === 'function')) return;
  var text = window.WebToApk.readClipboard();
  var m = /https?:\/\/\S+/.exec(text);
  if (m) document.getElementById('link').value = m[0];
  else showMessage('There is no link on the clipboard');
};

Notes: Only your own app's pages can read it: a foreign website opened inside the app gets "". Android only lets the app read the clipboard while it is on screen, and Android 12+ shows a "pasted from clipboard" notice - read it on a user tap, not on page load.

shareFiles bridge#

window.WebToApk.shareFiles(filesJson: String, title: String, text: String): Boolean

TRUE native file share for navigator.share({files:[...]}). Android WebView has no Web Share API, and text/plain ACTION_SEND can't carry a file - so before this a shared image was merely SAVED, not shared. filesJson is [{name, mimeType, base64}, ...]; we write each to cache, expose it via the app's FileProvider, and fire ACTION_SEND / ACTION_SEND_MULTIPLE so the real Android share sheet (WhatsApp, Gmail, …) opens. Returns true if the sheet opened.

Example

Shares one or more real files (an image, a PDF…) through the Android share sheet. Pages normally use the standard navigator.share({ files: [...] }), which the shell routes here.

Returns: true if the share sheet opened, false otherwise (synchronously). Needs: nothing.

The standard API first:

async function shareCanvas(canvas) {
  var blob = await new Promise(function (r) { canvas.toBlob(r, 'image/png'); });
  var file = new File([blob], 'drawing.png', { type: 'image/png' });
  if (!(navigator.canShare && navigator.canShare({ files: [file] }))) {
    alert('File sharing is not available here');
    return;
  }
  try {
    await navigator.share({ files: [file], title: 'My drawing', text: 'Made in my app' });
  } catch (e) { /* in a browser: the user closed the sheet */ }
}

The raw call takes a JSON string of [{name, mimeType, base64}] (base64 without the data: prefix), then title and text:

function shareBase64Png(base64) {
  if (!(window.WebToApk && typeof window.WebToApk.shareFiles === 'function')) return false;
  var files = [{ name: 'receipt.png', mimeType: 'image/png', base64: base64 }];
  return window.WebToApk.shareFiles(JSON.stringify(files), 'Receipt', 'Your receipt');
}

Export a table as a CSV file (text is UTF-8 encoded before base64):

function shareCsv(rows) {
  if (!(window.WebToApk && typeof window.WebToApk.shareFiles === 'function')) return;
  var csv = rows.map(function (r) {
    return r.map(function (c) { return '"' + String(c).replace(/"/g, '""') + '"'; }).join(',');
  }).join('\n');
  var base64 = btoa(unescape(encodeURIComponent(csv)));
  var files = [{ name: 'expenses.csv', mimeType: 'text/csv', base64: base64 }];
  if (!window.WebToApk.shareFiles(JSON.stringify(files), 'Expenses', '')) {
    showMessage('Could not open the share sheet');
  }
}

Notes: Files are written to the app's cache and shared through its FileProvider. Unsafe characters in name become _. Several files of one type share with that type; mixed types share as */*. The whole file travels as base64 over the bridge, so keep it to a few MB. When navigator.share({files}) cannot open the sheet, the shell saves the files as downloads instead and the promise still resolves.

shareNative bridge#

window.WebToApk.shareNative(title: String, text: String, url: String)

The Android share sheet, the system clipboard, and closing a window the page opened. These are the plain-web equivalents (navigator.share, navigator.clipboard, window.close) answered natively so they behave the same inside the app.

Described by its group, Share, clipboard and window, rather than on its own.

Example

Opens the Android share sheet with a title, text and link. Pages normally use the standard navigator.share({title, text, url}), which the shell routes here.

Returns: nothing (it cannot tell you which app the user picked). Needs: nothing.

The standard API first - it also works in a mobile browser:

document.getElementById('share-btn').onclick = async function () {
  if (!navigator.share) { alert('Sharing is not available here'); return; }
  try {
    await navigator.share({ title: 'My app', text: 'Look at this', url: 'https://mysite.com/item/42' });
  } catch (e) {
    // in a browser: AbortError when the user closes the sheet
  }
};

The raw call, all three arguments are strings (use '' to leave one out):

if (window.WebToApk && typeof window.WebToApk.shareNative === 'function') {
  window.WebToApk.shareNative('My app', 'Look at this', 'https://mysite.com/item/42');
}

Text only, no link - for example a game score:

function shareScore(score) {
  if (!(window.WebToApk && typeof window.WebToApk.shareNative === 'function')) return;
  window.WebToApk.shareNative('My score', 'I scored ' + score + ' points. Can you beat it?', '');
}

Notes: The shared text is text + a new line + url (or just one of them). title becomes the email subject and the share sheet's heading. Text and links only - to share a file, use navigator.share({files}) (see shareFiles).

Generated from the app runtime and its example files on every docs build. Read it as Markdown · All families.

Checked against the shipped bridge on 2026-11-04.