Recently, we developed a client-side web part to send an email to external users using the SharePoint framework. In this tutorial, I will explain how to send emails in SPFx using the Microsoft graph api and PnP JS. This SPFx client-side web part allows users to send emails to both people within and outside of the organization.
Send Email in SPFx Using PnP JS Library with React Framework [Internal People]
In this example, the web part lets you enter the To, Cc, Subject, and Body of an email. When you click the Send Mail button, the email is sent, and a success message appears. If you try to send the email without filling in the To address, a warning message will appear, saying, “Please select To address.”
Below is an example for your reference.

For this example, I will create a client-side web part using the React framework. Here, I will show you how to use the PnP JS library to send an email to selected users.
For the UI, we will use various Fluent UI React controls, including the People Picker and text inputs, to handle user input within the SPFx web part.
Note: We are using the SendEmail() method from the sp.utility namespace in the PnP JS library to send emails via SharePoint’s email utility service. This method takes a plain object (emailProps) that defines the email recipients, subject, and body content.
Usage:
Below is the PnP JS code to send an email in SharePoint Online.
import { spfi } from "@pnp/sp";
import "@pnp/sp/sputilities";
import { IEmailProperties } from "@pnp/sp/sputilities";
const sp = spfi(...);
const emailProps: IEmailProperties = {
To: ["user@site.com"],
CC: ["user2@site.com", "user3@site.com"],
BCC: ["user4@site.com", "user5@site.com"],
Subject: "This email is about...",
Body: "Here is the body."
};
await sp.utility.sendEmail(emailProps);
console.log("Email Sent!");
Now, let’s take a look at the steps to build this SPFx client-side web part.
Create a SPFx Webpart with React Framework
If you are new to the SharePoint Framework, then check out my tutorial on how to create a spfx client-side web part. Once the web part is created, the folder structure will look like this.

We will be updating the files that I marked in the src folder.
Install & Setup PnP JS Library
Let’s get started!
- Run the command below either in the command prompt or in the TERMINAL pane in VS Code.
npm install @pnp/sp --save
This installs the PnP library in your SPFx webpart.
- Once the library is installed, import the following statements into the .ts file located in the src folder, outside of the components folder.
import { spfi, SPFx } from "@pnp/sp";
import { SPFI } from "@pnp/sp";
import "@pnp/sp/webs";

- Then, in the same file, under the export default class, provide this variable creation statement.
private _sp: SPFI;
- Also, update the onInit() method with the code below.
protected async onInit(): Promise<void> {
this._sp = spfi().using(SPFx(this.context));
}
- Within the render() function, add the two props below under the default props.
sp: this._sp,
context: this.context
These props pass the configured PnP JS instance and the web part context to the React component.
- Update the Props.ts file with the code below. I removed some default props; you can keep them if you want.
import { SPFI } from "@pnp/sp";
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface ISendEmailsWebpartProps {
userDisplayName: string;
sp: SPFI;
context: WebPartContext
}

Install & Setup Fluent UI
- Run the command below to install the Fluent UI and reusable SPFx React controls to use pre-built components like people pickers and input fields in our web part.
npm install @fluentui/react @pnp/spfx-controls-react
- Then, import these statements into the .tsx file present within the component folder.
import { TextField, PrimaryButton, Label } from '@fluentui/react';
import { PeoplePicker, PrincipalType } from "@pnp/spfx-controls-react/lib/PeoplePicker";
import { IPeoplePickerContext } from "@pnp/spfx-controls-react/lib/PeoplePicker";
Implement Email Sending Functionality Using React Component
Till now, we have imported and set up the necessary libraries; now, let’s implement the functionality code for sending emails using a React component.
- Open the .tsx file and update the default code with the one below!
import * as React from 'react';
import styles from './SendEmailsWebpart.module.scss';
import type { ISendEmailsWebpartProps } from './ISendEmailsWebpartProps';
import "@pnp/sp/sputilities";
import { TextField, PrimaryButton, Label } from '@fluentui/react';
import { PeoplePicker, PrincipalType } from "@pnp/spfx-controls-react/lib/PeoplePicker";
import { IPeoplePickerContext } from "@pnp/spfx-controls-react/lib/PeoplePicker";
export interface ISendEmailStates {
ToEmail: any;
CcEmail: any;
EmailSubject: any;
EmailBody: any;
}
export default class SendEmailsWebpart extends React.Component<ISendEmailsWebpartProps,ISendEmailStates> {
constructor(props: ISendEmailsWebpartProps) {
super(props);
this.state = {
ToEmail: [],
CcEmail: [],
EmailSubject: '',
EmailBody: ''
};
}
private _getPeoplePickerItems = (items: any[], type: "ToEmail" | "CcEmail") => {
const emails = items.map(i => i.secondaryText);
if (type === "ToEmail") {
this.setState({ ToEmail: emails });
} else {
this.setState({ CcEmail: emails });
}
};
private sendEmail = async () => {
const { ToEmail, CcEmail, EmailSubject, EmailBody } = this.state;
if (ToEmail.length === 0) {
alert("Please select at least one recipient (To)");
return;
}
try {
await this.props.sp.utility.sendEmail({
To: ToEmail,
CC: CcEmail,
Subject: EmailSubject,
Body: EmailBody,
AdditionalHeaders: {
"content-type": "text/html"
}
});
alert("Email sent successfully!");
// Optionally clear the form
this.setState({
ToEmail: [],
CcEmail: [],
EmailSubject: '',
EmailBody: ''
});
} catch (error) {
console.error("Error sending email:", error);
alert("An error occurred while sending the email.");
}
};
public render(): React.ReactElement<ISendEmailsWebpartProps> {
const {
} = this.props;
const peoplePickerContext: IPeoplePickerContext = {
absoluteUrl: this.props.context.pageContext.web.absoluteUrl,
spHttpClient: this.props.context.spHttpClient,
msGraphClientFactory: this.props.context.msGraphClientFactory
};
return (
<section className={`${styles.sendEmailsWebpart}`}>
<div className={styles.welcome}>
<h2>Send Email Utility</h2>
</div>
<div className={styles.sendEmailsForm}>
<Label>To :</Label>
<PeoplePicker
context={peoplePickerContext}
personSelectionLimit={5}
principalTypes={[PrincipalType.User]}
resolveDelay={500}
onChange={(items) => this._getPeoplePickerItems(items, "ToEmail")}
/>
<Label>CC :</Label>
<PeoplePicker
context={peoplePickerContext}
personSelectionLimit={5}
principalTypes={[PrincipalType.User]}
resolveDelay={500}
onChange={(items) => this._getPeoplePickerItems(items, "CcEmail")}
/>
<Label>Subject :</Label>
<TextField
value={this.state.EmailSubject}
onChange={(_, newValue) => this.setState({ EmailSubject: newValue || '' })}
/>
<Label>Body :</Label>
<TextField
multiline
placeholder="Body.."
rows={5}
value={this.state.EmailBody}
onChange={(_, newValue) => this.setState({ EmailBody: newValue || '' })}
/>
<PrimaryButton
text="Send Mail"
onClick={this.sendEmail}
className={styles.sendButton}
/>
</div>
</section>
);
}
}
- Here:
- import “@pnp/sp/sputilities” = To use the SendEmail() pnp js method we are importing this.
- ISendEmailStates = Initially, we created an interface to define the structure of the email object.
- constructor(props: ISendEmailsWebpartProps) = Then, using the constructor, we initialized that object.
- _getPeoplePickerItems = Used to extract the emails that we selected in the people picker control.
- sendEmail = To send emails using this.props.sp
- render() = Within this, we added the To, Cc, Subject, and Body input controls and the send email button, along with its functionality.
- Add the styles below to the .scss file in the component folder.
.sendEmailsForm {
padding: 20px;
max-width: 600px;
margin: auto;
}
.title {
font-size: 20px;
font-weight: 600;
color: #333;
margin-bottom: 20px;
}
.sendButton {
margin-top: 15px;
background-color: #b59b00;
border-color: #b59b00;
&:hover {
background-color: #a38a00;
border-color: #a38a00;
}
}
- Now, our SPFx web part, which sends email using PnP, is ready to test. To test, run the command below, which will open the local workbench.
gulp serve
- While the local workbench is running, open any SharePoint Online site workbench.
https://<tenantname>.sharepoint.com/sites/<SiteName>/_layouts/15/workbench.aspx
- On this page, add the web part, and you will see the output as shown below. Fill in the details, such as Email To, CC, Subject, and Body, and then click the Send Email button.

- It will show a successful message. And if you check the email, it appears like:

So far, we have seen how to send emails to individuals within the organization. But in the section below, we will learn how to send an email to the users who are not part of the organization. To do this, we are using the Microsoft Graph API.
SPFx: Send Emails Using Microsoft Graph with React Framework [External Users]
Look at the example below; this time, I added a radio control for choosing whether to send an email to internal or external people. If you choose:
- Internal: You need to select the To and Cc email addresses from the People Picker.
- External: Needs to enter the To and Cc addresses in the text input controls.
For both users, the “Send Email” button is the same, so clicking this button will send emails. Additionally, validation will be in place. If you have not provided an email address for To and Cc, an alert message will be displayed, and the email will not be sent.

Microsoft Graph provides access to the data stored in Microsoft 365 services. To utilize that data in our applications, we can use Microsoft Graph API connections to access it.
In our SPFX web part, we are using the Microsoft Graph API to send emails, including to external users, which is not supported by the sp.utility.sendEmail method. To do this, we use the MSGraphClientV3 class provided by SPFx to make an authenticated HTTP request [/me/sendMail] to the Microsoft Graph endpoint:
POST /me/sendMail
This endpoint sends an email from the currently logged-in user’s mailbox, using the permissions granted by the tenant admin for (Mail.Send).
Follow the steps below to implement this!
- To use the above Microsoft Graph API endpoint, we need to make sure the Mail.Send permission is approved for the app. So, open the package-solution.json file and paste the code below into it.
"webApiPermissionRequests": [
{
"resource": "Microsoft Graph",
"scope": "Mail.Send"
}
]
It tells SharePoint that our webpart needs permission to send mail through Graph.

- Save the file and build the project with the following commands:
gulp clean
gulp build
gulp bundle --ship
gulp package-solution --ship
- Upload the .sppkg file to your SharePoint App Catalog. Then, on the right side, a pop-up will appear. Choose the option based on your requirements under App availability, and click Enable App.

- Again, another pop-up will appear to grant permission for this app, so click on Go to API access page.

- Under Pending requests, you can view the request, select it, and click “Approve” at the top. It opens a page on the right side, where you can view the details of this app, and you click the Approve button. Once approved, it is moved under Approved Requests.

We have now granted permission to use the Microsoft Graph API endpoint for sending emails to users. Return to the project now and update the code by following the steps below.
- Inside the SendEmailsWebpart.tsx file, add the following code:
sendUsingGraph: boolean;
- Also, initialize it in the constructor.
sendUsingGraph: false

- To allow users to choose an option for sending mail to external or internal recipients, I added a choice group control, so import the statement below.
import { ChoiceGroup } from '@fluentui/react';
- Add the code below to the render() method in the .tsx file.
<Label>Send Email To:</Label>
<ChoiceGroup
selectedKey={this.state.sendUsingGraph ? 'graph' : 'sp'}
options={[
{ key: 'sp', text: 'Internal Users' },
{ key: 'graph', text: 'External Users' }
]}
onChange={(_, option) => {
if (option) {
this.setState({ sendUsingGraph: option.key === 'graph' });
}
}}
/>
The above code creates a choice group, similar to a radio button, with the options “Internal Users” and “External Users.”
- The People Picker control is limited to internal users, so to enter an external user’s email address, we will add a text input control for both the To and Cc fields.
{
this.state.sendUsingGraph ? (
<>
<TextField
label="To :"
placeholder="Enter emails (e.g. user1@gmail.com, user2@outlook.com)"
value={this.state.ToEmail.join(',')}
onChange={(_, newValue) => this.setState({
ToEmail: newValue?.split(',').map(e => e.trim()) || []
})}
/>
<TextField
label="CC :"
placeholder="Enter emails (optional)"
value={this.state.CcEmail.join(',')}
onChange={(_, newValue) => this.setState({
CcEmail: newValue?.split(',').map(e => e.trim()) || []
})}
/>
</>
) : (
<>
<Label>To :</Label>
<PeoplePicker
context={peoplePickerContext}
personSelectionLimit={5}
principalTypes={[PrincipalType.User]}
resolveDelay={500}
onChange={(items) => this._getPeoplePickerItems(items, "ToEmail")}
/>
<Label>CC :</Label>
<PeoplePicker
context={peoplePickerContext}
personSelectionLimit={5}
principalTypes={[PrincipalType.User]}
resolveDelay={500}
onChange={(items) => this._getPeoplePickerItems(items, "CcEmail")}
/>
</>
)
}
This code will display people pickers for the To and Cc fields if we choose the ‘Internal Users’ option; if external, text input controls will be displayed.
- Then, update the sendEmails method to send emails for both internal and external users.
private sendEmail = async () => {
const { ToEmail, CcEmail, EmailSubject, EmailBody, sendUsingGraph } = this.state;
if (ToEmail.length === 0) {
alert("Please select at least one recipient (To)");
return;
}
if (sendUsingGraph) {
const message = {
message: {
subject: EmailSubject,
body: {
contentType: "HTML",
content: EmailBody
},
toRecipients: ToEmail.map((email: string) => ({ emailAddress: { address: email } })),
ccRecipients: CcEmail.map((email: string) => ({ emailAddress: { address: email } }))
},
saveToSentItems: true
};
try {
const client = await this.props.context.msGraphClientFactory.getClient("3");
await client.api('/me/sendMail').post(message);
alert("Succesfully Email sent to External User!");
} catch (error) {
console.error("Graph API error:", error);
alert("Error sending email with Microsoft Graph.");
}
} else {
try {
await this.props.sp.utility.sendEmail({
To: ToEmail,
CC: CcEmail,
Subject: EmailSubject,
Body: EmailBody,
AdditionalHeaders: {
"content-type": "text/html"
}
});
alert("Succesfully Email sent to Internal User!");
} catch (error) {
console.error("SP Utility error:", error);
alert("Error sending email with SharePoint Utility.");
}
}
this.setState({
ToEmail: [],
CcEmail: [],
EmailSubject: '',
EmailBody: '',
sendUsingGraph: this.state.sendUsingGraph
});
};
- Here:
- sendUsingGraph = This variable contains a true for the ‘External Users’ option selection. Otherwise false. If this is true, we are using the Microsoft Graph API method to send emails; otherwise, we are using sp.utility.sendEmail().
- await this.props.context.msGraphClientFactory.getClient(“3”); is the instance that is calling the MSGraphClientV3 class.
- await client.api(‘/me/sendMail’).post(message) = This HTTP request uses the JSON [message] object to store mail details, such as To, Cc, Subject, etc.
- This JSON object takes two parameters: message and saveToSentItems.
Here is the final code:
import * as React from 'react';
import styles from './SendEmailsWebpart.module.scss';
import type { ISendEmailsWebpartProps } from './ISendEmailsWebpartProps';
import "@pnp/sp/sputilities";
import { TextField, PrimaryButton, Label } from '@fluentui/react';
import { PeoplePicker, PrincipalType } from "@pnp/spfx-controls-react/lib/PeoplePicker";
import { IPeoplePickerContext } from "@pnp/spfx-controls-react/lib/PeoplePicker";
import { ChoiceGroup } from '@fluentui/react';
export interface ISendEmailStates {
ToEmail: any;
CcEmail: any;
EmailSubject: any;
EmailBody: any;
sendUsingGraph: boolean;
}
export default class SendEmailsWebpart extends React.Component<ISendEmailsWebpartProps,ISendEmailStates> {
constructor(props: ISendEmailsWebpartProps) {
super(props);
this.state = {
ToEmail: [],
CcEmail: [],
EmailSubject: '',
EmailBody: '',
sendUsingGraph: false
};
}
private _getPeoplePickerItems = (items: any[], type: "ToEmail" | "CcEmail") => {
const emails = items.map(i => i.secondaryText);
if (type === "ToEmail") {
this.setState({ ToEmail: emails });
} else {
this.setState({ CcEmail: emails });
}
};
private sendEmail = async () => {
const { ToEmail, CcEmail, EmailSubject, EmailBody, sendUsingGraph } = this.state;
if (ToEmail.length === 0) {
alert("Please select at least one recipient (To)");
return;
}
if (sendUsingGraph) {
// Use Graph API
const message = {
message: {
subject: EmailSubject,
body: {
contentType: "HTML",
content: EmailBody
},
toRecipients: ToEmail.map((email: string) => ({ emailAddress: { address: email } })),
ccRecipients: CcEmail.map((email: string) => ({ emailAddress: { address: email } }))
},
saveToSentItems: true
};
try {
const client = await this.props.context.msGraphClientFactory.getClient("3");
await client.api('/me/sendMail').post(message);
alert("Succesfully Email sent to External User!");
} catch (error) {
console.error("Graph API error:", error);
alert("Error sending email with Microsoft Graph.");
}
} else {
// Use SharePoint Utility
try {
await this.props.sp.utility.sendEmail({
To: ToEmail,
CC: CcEmail,
Subject: EmailSubject,
Body: EmailBody,
AdditionalHeaders: {
"content-type": "text/html"
}
});
alert("Succesfully Email sent to Internal User!");
} catch (error) {
console.error("SP Utility error:", error);
alert("Error sending email with SharePoint Utility.");
}
}
// Reset form
this.setState({
ToEmail: [],
CcEmail: [],
EmailSubject: '',
EmailBody: '',
sendUsingGraph: this.state.sendUsingGraph
});
};
public render(): React.ReactElement<ISendEmailsWebpartProps> {
const {
} = this.props;
const peoplePickerContext: IPeoplePickerContext = {
absoluteUrl: this.props.context.pageContext.web.absoluteUrl,
spHttpClient: this.props.context.spHttpClient,
msGraphClientFactory: this.props.context.msGraphClientFactory
};
return (
<section className={`${styles.sendEmailsWebpart}`}>
<div className={styles.welcome}>
<h2>Send Email Utility</h2>
</div>
<div className={styles.sendEmailsForm}>
<Label>Send Email To:</Label>
<ChoiceGroup
selectedKey={this.state.sendUsingGraph ? 'graph' : 'sp'}
options={[
{ key: 'sp', text: 'Internal Users' },
{ key: 'graph', text: 'External Users' }
]}
onChange={(_, option) => {
if (option) {
this.setState({ sendUsingGraph: option.key === 'graph' });
}
}}
/>
{
this.state.sendUsingGraph ? (
<>
<TextField
label="To :"
placeholder="Enter emails (e.g. user1@gmail.com, user2@outlook.com)"
value={this.state.ToEmail.join(',')}
onChange={(_, newValue) => this.setState({
ToEmail: newValue?.split(',').map(e => e.trim()) || []
})}
/>
<TextField
label="CC :"
placeholder="Enter emails (optional)"
value={this.state.CcEmail.join(',')}
onChange={(_, newValue) => this.setState({
CcEmail: newValue?.split(',').map(e => e.trim()) || []
})}
/>
</>
) : (
<>
<Label>To :</Label>
<PeoplePicker
context={peoplePickerContext}
personSelectionLimit={5}
principalTypes={[PrincipalType.User]}
resolveDelay={500}
onChange={(items) => this._getPeoplePickerItems(items, "ToEmail")}
/>
<Label>CC :</Label>
<PeoplePicker
context={peoplePickerContext}
personSelectionLimit={5}
principalTypes={[PrincipalType.User]}
resolveDelay={500}
onChange={(items) => this._getPeoplePickerItems(items, "CcEmail")}
/>
</>
)
}
<Label>Subject :</Label>
<TextField
value={this.state.EmailSubject}
onChange={(_, newValue) => this.setState({ EmailSubject: newValue || '' })}
/>
<Label>Body :</Label>
<TextField
multiline
placeholder="Body.."
rows={5}
value={this.state.EmailBody}
onChange={(_, newValue) => this.setState({ EmailBody: newValue || '' })}
/>
<PrimaryButton
text="Send Mail"
onClick={this.sendEmail}
className={styles.sendButton}
/>
</div>
</section>
);
}
}
Now it’s time to deploy the client-side web part to SharePoint Online so that we can use it on the SharePoint site.
Deploy SPFx Send Email Webpart
To deploy the SPFx client-side web part to your SharePoint Online app catalog site or site collection app catalog, run the commands below, which will create the .sppkg file under the SharePoint folder.
gulp bundle --ship
gulp package-solution --ship
This way, we can send an email from the SPFx web part. I hope you found this helpful article!
In this article, I explain how to create an SPFx web part that allows users to send an email to people within and outside of the organization. Additionally, I explained how to install and utilize the Fluent UI React controls, as well as the usage of Microsoft Graph. Follow this article if you are also trying to implement this type of web part.
You may also like the following SPFx tutorials:
- Display SharePoint list items using SPFx
- SPFx Fluent UI Basic List Example
- SPFx fluent UI react dropdown example
- Create and deploy SharePoint Framework (SPFx) listview command set extension
- SPFx SwatchColorPicker Office UI Fabric React Control example

Hey! I’m Bijay Kumar, founder of SPGuides.com and a Microsoft Business Applications MVP (Power Automate, Power Apps). I launched this site in 2020 because I truly enjoy working with SharePoint, Power Platform, and SharePoint Framework (SPFx), and wanted to share that passion through step-by-step tutorials, guides, and training videos. My mission is to help you learn these technologies so you can utilize SharePoint, enhance productivity, and potentially build business solutions along the way.
after downloading the solution, i m not able to open.
and I m getting error on isRequired type…
Property ‘isRequired’ does not exist on type ‘IntrinsicAttributes & IntrinsicClassAttributes & Readonly & Readonly’. Did you mean ‘required’?ts
this._getPeoplePickerItems(Items, “CcEmail”)}
showHiddenInUI={false}
principalTypes={[PrincipalType.User]}
resolveDelay={1000}
ensureUser={true}
/>
this does not seem to work
Can we send emails to distribution lists by storing them in lists by any chance ?
Please let me know if it possible it will be greatly appreciated …