Hi Friends, in the series of SharePoint Framework tutorials, today we will learn how to perform CRUD operations in SharePoint Framework (SPFx) using PNP JS.

PNP package contains the fluent API used to call the SharePoint rest services. In the previous article, we have seen how to perform SPFx CRUD operation using SPHttpClient.

SharePoint Framework (SPFx) CRUD operation using PNP JS.

Create a list with Name EmployeeDetails. Add column Age. We will use the Title column to Save the Full name and the Age column to add the age of the employee.

Step 1: Create a folder with the name SPFxCrudPnp on your local drive. I have used E drive.

SPFx CRUD Using PNP JS

Step 2: Open the location in the command prompt.

Open solution path in command promt

Step 3: Scaffold the SPFx solution using Yeoman Generator

yo @microsoft/sharepoint

Scaffold SPFx CRUD solution

Step 4: Give the webpart name and other details as shown below

Scaffolding spfx solution for crud operation

Step 5: Open the solution in VS Code. You can use the "Code ." command in the command prompt directly to open the solution in VS Code.

Step 6: In this SharePoint Framework webpart, we are using PNP JS for performing crud operations. Therefore we need to install the PNP in our solution. To install the PNP JS, run the below command in the command prompt
npm i @pnp/sp 
install PNP JS

Step 7: Once the PNP module is installed, you can see the message on the command prompt for the same

Install PNP JS

Step 8:We are going to write the entire logic for CRUD operations in SpfxCrudPnp.tsx file. For that, we need the context of SharePoint in .tsx file.

Step 9: In PNP it is very easy to set up the SPFx context. Just import the sp module and add the Oninit method in SpfxCrudPnpWebPart.ts as shown below:

import { sp } from "@pnp/sp/presets/all";
  protected onInit(): Promise<void> {
    return super.onInit().then(_ => {
      sp.setup({
        spfxContext: this.context
      });
    });
  }
After adding the above code, your file looks as below:

SPFx Crud using pnp


Step 10: Update the SpfxCrudPnp.tsx file with the below JSX (React way of writing HTML and javascript).
<div className={styles.spfxCrudPnp}>
        <div className={styles.container}>
          <div className={styles.row}>
            <div className={styles.column}>
              <div className={styles.itemField}>
                <div className={styles.fieldLabel}>Item ID:</div>
                <input type="text" id='itemId'></input>
              </div>
              <div className={styles.itemField}>
                <div className={styles.fieldLabel}>Full Name</div>
                <input type="text" id='fullName'></input>
              </div>
              <div className={styles.itemField}>
                <div className={styles.fieldLabel}>Age</div>
                <input type="text" id='age'></input>
              </div>
              <div className={styles.itemField}>
                <div className={styles.fieldLabel}>All Items:</div>
                <div id="allItems"></div>
              </div>
              <div className={styles.buttonSection}>
                <div className={styles.button}>
                  <span className={styles.label} onClick={this.createItem}>Create</span>
                </div>
                <div className={styles.button}>
                  <span className={styles.label} onClick={this.getItemById}>Read</span>
                </div>
                <div className={styles.button}>
                  <span className={styles.label} onClick={this.getAllItems}>Read All</span>
                </div>
                <div className={styles.button}>
                  <span className={styles.label} onClick={this.updateItem}>Update</span>
                </div>
                <div className={styles.button}>
                  <span className={styles.label} onClick={this.deleteItem}>Delete</span>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
Step 11: Import PNP modules in the SpfxCrudPnp.tsx file as shown below
import { sp } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
Step 12: Add CRUD functions as shown below in the same tsx file:

//Create Item
  private createItem = async () => {
    try {
      const addItem = await sp.web.lists.getByTitle("EmployeeDetails").items.add({
        'Title': document.getElementById("fullName")['value'],
        'Age': document.getElementById("age")['value']
      });
      console.log(addItem);
      alert(`Item created successfully with ID: ${addItem.data.ID}`);
    }
    catch (e) {
      console.error(e);
    }
  }

  //Get Item by ID
  private getItemById = async () => {
    try {
      const id: number = document.getElementById('itemId')['value'];
      if (id > 0) {
        const item: any = await sp.web.lists.getByTitle("EmployeeDetails").items.getById(id).get();
        document.getElementById('fullName')['value'] = item.Title;
        document.getElementById('age')['value'] = item.Age;
      }
      else {
        alert(`Please enter a valid item id.`);
      }
    }
    catch (e) {
      console.error(e);
    }
  }

  //Get all items
  private getAllItems = async () => {
    try {
      const items: any[] = await sp.web.lists.getByTitle("EmployeeDetails").items.get();
      console.log(items);
      if (items.length > 0) {
        var html = `<table><tr><th>ID</th><th>Full Name</th><th>Age</th></tr>`;
        items.map((item, index) => {
          html += `<tr><td>${item.ID}</td><td>${item.Title}</td><td>${item.Age}</td></li>`;
        });
        html += `</table>`;
        document.getElementById("allItems").innerHTML = html;
      } else {
        alert(`List is empty.`);
      }
    }
    catch (e) {
      console.error(e);
    }
  }

  //Update Item
  private updateItem = async () => {
    try {
      const id: number = document.getElementById('itemId')['value'];
      if (id > 0) {
        const itemUpdate = await sp.web.lists.getByTitle("EmployeeDetails").items.getById(id).update({
          'Title': document.getElementById("fullName")['value'],
          'Age': document.getElementById("age")['value']
        });
        console.log(itemUpdate);
        alert(`Item with ID: ${id} updated successfully!`);
      }
      else {
        alert(`Please enter a valid item id.`);
      }
    }
    catch (e) {
      console.error(e);
    }
  }

  //Delete Item
  private deleteItem = async () => {
    try {
      const id: number = parseInt(document.getElementById('itemId')['value']);
      if (id > 0) {
        let deleteItem = await sp.web.lists.getByTitle("EmployeeDetails").items.getById(id).delete();
        console.log(deleteItem);
        alert(`Item ID: ${id} deleted successfully!`);
      }
      else {
        alert(`Please enter a valid item id.`);
      }
    }
    catch (e) {
      console.error(e);
    }
  }
Step 13: After adding all the above functions, your SpfxCrudPnp.tsx file will look like below:

SPFx Crud using pnp js


Step 14: Update your SpfxCrudPnp.module.scss file with below CSS

@import "~office-ui-fabric-react/dist/sass/References.scss";

.spfxCrudPnp {
  .container {
    max-width: 700px;
    margin: 0px auto;
    box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
  }

  .row {
    @include ms-Grid-row;
    @include ms-fontColor-white;
    background-color: $ms-color-themeDark;
    padding: 20px;
  }

  .column {
    @include ms-Grid-col;
    @include ms-lg10;
    @include ms-xl8;
    @include ms-xlPush2;
    @include ms-lgPush1;
  }

  .title {
    @include ms-font-xl;
    @include ms-fontColor-white;
  }

  .subTitle {
    @include ms-font-l;
    @include ms-fontColor-white;
  }

  .description {
    @include ms-font-l;
    @include ms-fontColor-white;
  }

  .button {
    // Our button
    text-decoration: none;
    height: 32px;

    // Primary Button
    min-width: 80px;
    background-color: $ms-color-themePrimary;
    border-color: $ms-color-themePrimary;
    color: $ms-color-white;

    // Basic Button
    outline: transparent;
    position: relative;
    font-family: "Segoe UI WestEuropean", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue",
      sans-serif;
    -webkit-font-smoothing: antialiased;
    font-size: $ms-font-size-m;
    font-weight: $ms-font-weight-regular;
    border-width: 0;
    text-align: center;
    cursor: pointer;
    display: inline-block;
    margin: 10px;
    .label {
      font-weight: $ms-font-weight-semibold;
      font-size: $ms-font-size-m;
      height: 32px;
      line-height: 32px;
      margin: 0 4px;
      vertical-align: top;
      display: inline-block;
    }
  }

  .itemField {
    display: flex;
    padding: 5px;
    .fieldLabel {
      min-width: 100px;
    }
  }

  .buttonSection{
    padding-top: 20px;
    display: flex;
  }
}


Step 15: Run the gulp serve. Open the SharePoint workbench. Our UI will look as below:

SPFx Crud using pnp

Using this form, we can perform the CRUD operation on the SharePoint list in SPFx using PNP.