A minimal LWC that opens the device’s native share sheet (WhatsApp, Mail, Teams, etc.) so users can share a Salesforce record link in one tap — no Apex, no custom service integrations.
Salesforce is a hub of critical business information. Often, users need to share this information — a promising lead, a key account, or a public knowledge article — with colleagues. The default workflow of copy-pasting URLs is inefficient, especially on mobile devices. We can leverage the Web Share API, a standard browser feature, to provide a native sharing experience directly from a record page. This post demonstrates how to create a “Record Sharer” component that uses navigator.share()
to invoke the device’s own sharing functionality.
Solution Approach
The business problem is to allow a user to share a Salesforce record using their preferred communication tool (like Teams, Slack, WhatsApp, or email) with a single click.
This Solution delegates the entire sharing UI and logic to the client’s operating system. The LWC’s role is simply to gather the necessary information — the record’s title and URL — and hand it off to the browser. The LWC’s JavaScript controller calls navigator.share()
, passing an object with title
, text
, and url
. The browser then intercepts this call and opens the device’s native share sheet, populated with the user’s own apps.
Implementation Steps
LWC — Source Code
force-app/main/default/lwc/recordSharer/recordSharer.html
<template>
<lightning-card title="Share Record" icon-name="standard:share">
<div class="slds-p-around_medium">
<template if:true={isShareAvailable}>
<lightning-button
label="Share this Record"
variant="brand"
icon-name="utility:share"
onclick={handleShareClick}>
</lightning-button>
</template>
<template if:false={isShareAvailable}>
<p>Web Share is not available in this browser. To share, please copy the URL from the address bar.</p>
</template>
</div>
</lightning-card>
</template>
force-app/main/default/lwc/recordSharer/recordSharer.js
import { LightningElement, api, wire } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
// Example: Opportunity; change to your object as needed (e.g., Account.Name)
import NAME_FIELD from '@salesforce/schema/Opportunity.Name';
export default class RecordSharer extends NavigationMixin(LightningElement) {
@api recordId;
@api objectApiName;
@wire(getRecord, { recordId: '$recordId', fields: [NAME_FIELD] })
record;
get recordName() {
return getFieldValue(this.record?.data, NAME_FIELD) || this.recordId;
}
get isShareAvailable() {
return typeof navigator !== 'undefined' && !!navigator.share;
}
async handleShareClick() {
this[NavigationMixin.GenerateUrl]({
type: 'standard__recordPage',
attributes: { recordId: this.recordId, actionName: 'view' }
}).then((url) => this.invokeShare(url));
}
async invokeShare(recordUrl) {
try {
await navigator.share({
title: `Salesforce Record: ${this.recordName}`,
text: `Check out this record: ${this.recordName}`,
url: recordUrl
});
this.showToast('Success', 'Share dialog opened successfully.', 'success');
} catch (error) {
if (error?.name !== 'AbortError') {
this.showToast('Error', `Could not share: ${error.message}`, 'error');
}
}
}
showToast(title, message, variant) {
this.dispatchEvent(new ShowToastEvent({ title, message, variant }));
}
}
force-app/main/default/lwc/recordSharer/recordSharer.js-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>60.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
<target>lightning__AppPage</target>
<target>lightning__HomePage</target>
</targets>
</LightningComponentBundle>
(Optional) force-app/main/default/lwc/recordSharer/recordSharer.css
:host { display: block; }
Deploy & Open the Org
# (optional) create a clean project
sf project generate --name native-share --template standard
cd native-share
# login (sandbox example)
sf org login web --alias mySandbox --instance-url https://test.salesforce.com
# add the files above under force-app/
sf project deploy start --target-org mySandbox --source-dir force-app
# open the org
sf org open --target-org mySandbox
Create the Lightning Record Page
Open Lightning App Builder
Setup → App Builder → New → Record Page.
Choose Object & Template
- Label: Opportunity Record with Share (or your label)
- Object: Opportunity (or your target object)
- Pick a template (e.g., Header + Right Sidebar).
Add the standard Record Detail
- In the Standard components list, drag Record Detail into the main content region.
- (Optional but common) Drag Highlights Panel into the header region for actions/key fields.
- If you use Dynamic Forms (custom objects or supported core objects), you can instead add Field Section + Fields; both approaches are fine.
Add the share component
- In Custom components, drag RecordSharer into a side region (e.g., Right Sidebar) or under the details section — wherever it best fits.
Save & Activate
- Click Save, then Activate.
- Set as Org Default (or assign to specific Apps/Profiles/Record Types).
Test
- Open any record for that object.
- Click Share this Record.
- On supported browsers/devices, the native share sheet opens; otherwise, you’ll see the fallback message.
Object swap (if not Opportunity)
Change the field import in recordSharer.js
and redeploy:
- Account →
@salesforce/schema/Account.Name
- Case →
@salesforce/schema/Case.CaseNumber
sf project deploy start --target-org mySandbox --source-dir force-app
Quick tips
- User gesture + HTTPS are required for
navigator.share()
—the button click covers this. - Mobile-first: iOS Safari and Android Chrome are the most reliable; desktop support varies.
- If the button doesn’t render, your browser likely doesn’t support Web Share — use the fallback.