Send Email in SPFx using Microsoft Graph API and PnP JS

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.

How to Send Emails in SPFx Using SP Utility

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.

send an email webpart from spfx

We will be updating the files that I marked in the src folder.

Install & Setup PnP JS Library

Let’s get started!

  1. 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.

  1. 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";
Send an email from SPFx client-side webpart without exchange online
  1. Then, in the same file, under the export default class, provide this variable creation statement.
private _sp: SPFI;
  1. Also, update the onInit() method with the code below.
protected async onInit(): Promise<void> {
  this._sp = spfi().using(SPFx(this.context));
}
  1. 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.

  1. 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
}
spfx send email pnp

Install & Setup Fluent UI

  1. 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
  1. 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.

  1. 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.
  1. 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;
  }
}
  1. 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
  1. While the local workbench is running, open any SharePoint Online site workbench.
https://<tenantname>.sharepoint.com/sites/<SiteName>/_layouts/15/workbench.aspx
  1. 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.
Send Emails in SPFx Using SP Utility – The Easy Way!
  1. It will show a successful message. And if you check the email, it appears like:
Send an email from SPFx client-side webpart

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.

send mail to external users using spfx

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!

  1. 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.

Guest Users To Send Email from SPFx Webpart.
  1. Save the file and build the project with the following commands:
gulp clean
gulp build
gulp bundle --ship
gulp package-solution --ship
  1. 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.
spfx send mail to external users using microsoft graph api
  1. Again, another pop-up will appear to grant permission for this app, so click on Go to API access page.
Send an email from SPFx client-side webpart to external users
  1. 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.
Send mail via Microsoft Graph as Application (Any User)

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.

  1. Inside the SendEmailsWebpart.tsx file, add the following code:
sendUsingGraph: boolean;
  1. Also, initialize it in the constructor.
sendUsingGraph: false 
How to Send an Email Using a Graph API in SharePoint Framework (SPFx)
  1. 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';
  1. 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.”

  1. 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.

  1. 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:

3 thoughts on “Send Email in SPFx using Microsoft Graph API and PnP JS”

  1. 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}
    />

Leave a Comment

Power Apps functions free pdf

30 Power Apps Functions

This free guide walks you through the 30 most-used Power Apps functions with real business examples, exact syntax, and results you can see.

Download User registration canvas app

DOWNLOAD USER REGISTRATION POWER APPS CANVAS APP

Download a fully functional Power Apps Canvas App (with Power Automate): User Registration App