SPFx CRUD Operations using No JavaScript Framework

As a beginner in SharePoint Framework, you should know how to implement CRUD operations on a SharePoint list in SPFx.

In this article, I will explain the steps to implement CRUD (Create, Read, Update, Delete) operations using the PnP JS library in an SPFx web part. Now, let’s get started!

SharePoint Framework CRUD Operations

Look at the example below. The table displays all the items in the SharePoint list, with a radio button at the start of each item for selection purposes. The bottom three button controls are for creating, updating, and deleting an item.

In addition to these buttons, the form contains input controls to take input for creating and updating items. After performing all actions, such as creation, updation, and deletion, you will be notified.

SPFx CRUD Operations with No Framework Using PnPJs Library

I created a SharePoint Online list with the following columns for this example.

Column NameData Type
ProjectNameSingle line text
CompletionStatusYes/No
TechnologyChoice(SharePoint, PowerBI, PowerApps, Power Automate, SPFx, ReactJS)

The SharePoint list, having some data, looks like this:

spfx sharepoint framework crud operations no javascript framework

So let’s start the implementation!

If you are new to SPFx development, I would suggest you to check an article on setting up development environment for SharePoint Framework.

Create the SPFx Project and PnP JS Installation

Let’s first create the SharePoint Framework client-side web part to perform the CRUD operations in this SPFx example.

  1. Open the command prompt, and run the command below. You can also use the Windows Command Prompt or PowerShell Command Prompt.
md CRUD_Operations

cd CRUD_Operations
  1. After creating the directory, run the following command:
yo @microsoft/sharepoint
sharepoint framework no javascript
  1. Now, it will ask you a few questions, as shown below. Please provide the answers as I have given them, or you can change them.
    • What is your solution name? CRUD_Operations
    • Which type of client-side component to create? WebPart
    • Add a new Web part to the solution crud-operations.
    • What is your Web part name? SPFx_CRUD_Webpart
    • Which template would you like to use? No framework
crud operations using sharepoint framework and pnp js library
  1. Then it will take some time, and the SPFx solution will get created, and you will see a successful message like below:
crud operations with sharepoint framework client-side web parts
  1. Type the following command to open the solution using Visual Studio Code.
code .

The SPFX CRUD operations solution looks like this:

crud operations using sharepoint framework
  1. Since we are using the PnPjs library to interact with SharePoint in the SPFx project, run the following command in the TERMINAL pane.
npm install @pnp/sp --save

This installs the PnPJS library in your SPFx webpart.

Once it is installed, open the src\webparts\spFxCRUDWebpart\SpFxCrudWebpart.ts file and write the import statements.

import { spfi, SPFx } from "@pnp/sp";
import { SPFI } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
  1. PnP needs to be initialized with the SharePoint context to connect properly to the SharePoint site. So, update your onInit() as below.
protected async onInit(): Promise<void> {
  this._sp = spfi().using(SPFx(this.context));
}
  1. Also, create a private variable within the export default class.
private _sp: SPFI;

You will get a warning like, variable declared but never read; Now, ignore it. While implementing CRUD operations in this web part, we will use this variable.

How to Use sp-pnp-js with SharePoint Framework

So far, we have created an SPFx web part and successfully installed and set up the PnP JS library. Let’s see the implementation for the CRUD operation on the SharePoint list in the sections below.

SharePoint Framework – CRUD Operations Using PNP JS

To display the SharePoint list items in the SPFx web part, we required an HTML table, as well as three button controls for performing create, update, and delete actions, and a form control to provide input.

First, we will add all the controls required for this web part. Follow the section below to do this!

Add Table, Form, and Button Controls to the SPFX Webpart

To add the table, buttons, and form control to the SPFx webpart, update your render() method with the code below.

public render(): void {
  this.domElement.innerHTML = `
  <section class="${styles.spFxCrudWebpart} ${!!this.context.sdks.microsoftTeams ? styles.teams : ''}">
    <div class="${styles.crudContainer}">
      <h2>CRUD with No Javascript Framework</h2>
      <table class="${styles.projectTable}">
        <thead>
          <tr>
            <th></th>
            <th>Project Name</th>
            <th>Completion Status</th>
            <th>Technology</th>
          </tr>
        </thead>
        <tbody id="projectTableBody">
          <!-- Items will be added here -->
        </tbody>
      </table>

      <div class="${styles.buttonGroup}">
        <button id="createBtn">Create</button>
        <button id="updateBtn">Update</button>
        <button id="deleteBtn">Delete</button>
      </div>

      <div class="${styles.formGroup}">
        <label>Project Name:</label>
        <input type="text" id="projectNameInput" />
        <br/>

        <label>Completion Status:</label>
        <input type="radio" name="completionStatus" value="true" id="completedRadio" /> Completed
        <input type="radio" name="completionStatus" value="false" id="incompleteRadio" /> Incomplete

      <br/>
      <br/>
        <label>Technology:</label>
        <select id="technologyDropdown">
          <option value="PowerApps">PowerApps</option>
          <option value="SPFx">SPFx</option>
          <option value="PowerBI">PowerBI</option>
          <option value="SharePoint">SharePoint</option>
        </select>
      </div>
    </div>
  </section>`;
}

Then, save the changes. Add the following CSS styles to the .scss file to apply styles to the tables and buttons.

.crudContainer {
  padding: 20px;
}

.projectTable {
  border: 1px solid #ddd;
  width: 100%;
  border-collapse: collapse;
  text-align: center;

  th, td {
    border: 1px solid #ccc;
    padding: 8px;
  }

  th {
    background-color: #f2f2f2;
  }
}

.buttonGroup {
  margin-top: 10px;

  button {
    margin-right: 10px;
    padding: 5px 10px;
  }
}

.formGroup {
  margin-top: 15px;

  label {
    display: block;
    margin-bottom: 5px;
  }

  input[type="text"], select {
    width: 200px;
    padding: 5px;
    margin-bottom: 15px;
  }
}

Note: You might face warnings with import statements and variables used within the extended class, which are listed below:

  • import { escape } from ‘@microsoft/sp-lodash-subset’;
  • private _isDarkTheme: boolean = false;
  • private _environmentMessage: string = ”;

Remove these statements to have clear code; otherwise, keep them commented. A default function [ _getEnvironmentMessage()] displays welcome messages; remove this function.

SPFx CRUD Operations: Display SharePoint List Items

In the previous section, we created a table in the SPFx web part to display SharePoint list items. Now, we will see how to display all list items in this table.

We need to use the following command to retrieve all SharePoint list items using the PnP JS library in the SPFx web part.

const items = await this._sp.web.lists.getByTitle("ProjectDetails").items();

Within the getByTitle(“”) function, provide the SharePoint list name.

  1. To display SharePoint list items in the SPFx web part HTML table, add the function below after render().
private async getAllItems(): Promise<void> {
  try {
    const items = await this._sp.web.lists.getByTitle("ProjectDetails").items();

    const tableBody: HTMLElement | null = this.domElement.querySelector("#projectTableBody");

    if (tableBody) {
      tableBody.innerHTML = ""; 

      items.forEach(item => {
        const row = document.createElement("tr");

        row.innerHTML = `
          <td><input type="radio" name="itemSelect" data-id="${item.Id}" /></td>
          <td>${item.ProjectName || ''}</td>
          <td>${item.CompletionStatus ? "Completed" : "Incomplete"}</td>
          <td>${item.Technology || ''}</td>
        `;
        tableBody.appendChild(row);
        const radioBtn = row.querySelector("input[type='radio']");
        radioBtn?.addEventListener("click", (e) => {
          e.stopPropagation();
          this.loadItemToForm(item); 
        });

      });
    }
  } catch (err) {
    console.error("Error fetching list items:", err);
  }
}

private loadItemToForm(item: any): void {

  this._selectedItemId = item.Id;

  const projectNameInput = this.domElement.querySelector("#projectNameInput") as HTMLInputElement;
  const completedRadio = this.domElement.querySelector("#completedRadio") as HTMLInputElement;
  const incompleteRadio = this.domElement.querySelector("#incompleteRadio") as HTMLInputElement; 
  const technologyDropdown = this.domElement.querySelector("#technologyDropdown") as HTMLSelectElement;

  if (projectNameInput) {
    projectNameInput.value = item.ProjectName || "";
  }

  if (completedRadio && incompleteRadio) {
    if (item.CompletionStatus === true) {
      completedRadio.checked = true;
    } else {
      incompleteRadio.checked = true;
    }
  }

  if (technologyDropdown) {
    technologyDropdown.value = item.Technology || "";
  }
}
  1. Also, add the variable within the export default class.
private _selectedItemId: number | null = null;
  1. Call the getAllItems() function within the render() function.
this.getAllItems();

The above code will get the list data into the items variable. Then, it will iterate over each item and add it to the HTML table rows.

Additionally, we added a method named loadItemToForm(). This method is triggered when the radio button next to an item is clicked. It retrieves that item’s field data to display its details in the form below for updating.

SPFx CRUD Operations: Insert Item to SharePoint List

In this section, let’s see how to insert an item into a SharePoint list using the SPFx web part. To create an item in a SharePoint list using PnP JS in an SPFx web part, we need to use the .add () method, as shown below.

await this._sp.web.lists.getByTitle("ProjectDetails").items.add({
ProjectName: projectNameInput,
CompletionStatus: completionStatusInput === "true",
Technology: technologyInput
});

To do this! Follow the steps below!

  1. Add the function below within the export default class, anywhere after the render() function.
private async createItem(): Promise<void> {
  const projectNameInput = (document.getElementById("projectNameInput") as HTMLInputElement).value;
  const completionStatusInput = (document.querySelector('input[name="completionStatus"]:checked') as HTMLInputElement)?.value;
  const technologyInput = (document.getElementById("technologyDropdown") as HTMLSelectElement).value;

  if (!projectNameInput || !technologyInput) {
    alert("Please fill in all fields before creating the item.");
    return;
  }

  try {
    await this._sp.web.lists.getByTitle("ProjectDetails").items.add({
      ProjectName: projectNameInput,
      CompletionStatus: completionStatusInput === "true",
      Technology: technologyInput
    });

    alert("Item created successfully!");
    this.clearFormFields();
    this.getAllItems(); 
  } catch (error) {
    console.error("Error creating item:", error);
    alert("Error creating item. Check console for details.");
  }
}
private clearFormFields(): void {
  (document.getElementById("projectNameInput") as HTMLInputElement).value = "";
  (document.getElementById("completedRadio") as HTMLInputElement).checked = false;
  (document.getElementById("technologyDropdown") as HTMLSelectElement).selectedIndex = 0;
  this._selectedItemId = null; 
}
  1. To create the SharePoint list item when the Create button is clicked, add the following code to the render() method.
document.getElementById("createBtn")?.addEventListener("click", () => {
  this.createItem();
});

In the above code, I have also added the clearFormFields() function to clear the data entered in the form after the item is created.

The createItem() function first checks whether the project name and technology fields are empty. If they are, it displays an alert like “Please fill in all fields before creating the item,” and after creating an item, it also shows a success message.

SPFx CRUD Operations: Update SharePoint List Item

Now, let us see how to update an item in the SPFx Webpart. To update an item, first select it from the HTML table, which will populate the existing values in the form below. Then, you can change the value and click on the Update button.

The SharePoint list item will be updated, and then the HTML table will be reloaded with the list data.

We need to use the update() method to update the selected item using PnP JS.

await this._sp.web.lists.getByTitle("ProjectDetails").items.getById(1).update({
      ProjectName: projectNameInput,
      CompletionStatus: completionStatusInput === "true",
      Technology: technologyInput
    });
  1. Add the below updateItem() function within the export default class.
private async updateItem(): Promise<void> {
  if (!this._selectedItemId) {
    alert("Please select an item to update.");
    return;
  }

  const projectNameInput = (document.getElementById("projectNameInput") as HTMLInputElement).value;
  const completionStatusInput = (document.querySelector('input[name="completionStatus"]:checked') as HTMLInputElement)?.value;
  const technologyInput = (document.getElementById("technologyDropdown") as HTMLSelectElement).value;

  if (!projectNameInput || !technologyInput) {
    alert("Please fill in all fields before updating.");
    return;
  }

  try {
    await this._sp.web.lists.getByTitle("ProjectDetails").items.getById(this._selectedItemId).update({
      ProjectName: projectNameInput,
      CompletionStatus: completionStatusInput === "true",
      Technology: technologyInput
    });

    alert("Item updated successfully!");
    this.clearFormFields();
    this.getAllItems();
  } catch (error) {
    console.error("Error updating item:", error);
    alert("Error updating item. See console for details.");
  }
}
  1. Call the updateItem() function to update the item when we click the update button.
document.getElementById("updateBtn")?.addEventListener("click", () => {
  this.updateItem();
});

In the above updateItems() function, after updating the item, we need to call the following functions. It will clear the form fields and refresh the table, allowing us to see the updates immediately after making changes.

this.clearFormFields();
this.getAllItems();

SPFx CRUD Operations: Delete SharePoint List Item

Till now, we have covered all CRUD operations except the delete operation. Let’s see how to perform this.

The command below can delete a particular item from a SharePoint list. Therefore, we must provide the SharePoint list item ID in the getById () function.

await this._sp.web.lists.getByTitle("ProjectDetails").items.getById(this._selectedItemId).delete();
  1. Add the below deleteItem() function within the export default class.
private async deleteItem(): Promise<void> {
  if (!this._selectedItemId) {
    alert("Please select an item to delete.");
    return;
  }

  const confirmDelete = confirm("Are you sure you want to delete this item?");
  if (!confirmDelete) return;

  try {
    await this._sp.web.lists.getByTitle("ProjectDetails").items.getById(this._selectedItemId).delete();

    alert("Item deleted successfully!");

    this._selectedItemId = null;         
    this.clearFormFields();             
    this.getAllItems();                  
  } catch (error) {
    console.error("Error deleting item:", error);
    alert("Error deleting item. See console for details.");
  }
}
  1. Now, call the deleteItem() function within the render() function.
document.getElementById("deleteBtn")?.addEventListener("click", () => {
  this.deleteItem();
});

Whenever we click the delete button after selecting an item, it calls the delete function, and then that item is deleted from the SharePoint list. Additionally, you will see an alert message confirming that the item was deleted successfully.

CRUD operations in SharePoint Framework: Complete Code

Below is the complete SPFx CRUD operations code:

import { Version } from '@microsoft/sp-core-library';
import {
  type IPropertyPaneConfiguration,
  PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import type { IReadonlyTheme } from '@microsoft/sp-component-base';
import styles from './SpFxCrudWebpartWebPart.module.scss';
import * as strings from 'SpFxCrudWebpartWebPartStrings';
import { spfi, SPFx } from "@pnp/sp";
import { SPFI } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";

export interface ISpFxCrudWebpartWebPartProps {
  description: string;
}

export default class SpFxCrudWebpartWebPart extends BaseClientSideWebPart<ISpFxCrudWebpartWebPartProps> {
private _sp: SPFI;
private _selectedItemId: number | null = null;

public render(): void {
  this.domElement.innerHTML = `
  <section class="${styles.spFxCrudWebpart} ${!!this.context.sdks.microsoftTeams ? styles.teams : ''}">
    <div class="${styles.crudContainer}">
      <h2>CRUD with No Javascript Framework</h2>
      <table class="${styles.projectTable}">
        <thead>
          <tr>
            <th></th>
            <th>Project Name</th>
            <th>Completion Status</th>
            <th>Technology</th>
          </tr>
        </thead>
        <tbody id="projectTableBody">
          <!-- Items will be injected here -->
        </tbody>
      </table>

      <div class="${styles.buttonGroup}">
        <button id="createBtn">Create</button>
        <button id="updateBtn">Update</button>
        <button id="deleteBtn">Delete</button>
      </div>

      <div class="${styles.formGroup}">
        <label>Project Name:</label>
        <input type="text" id="projectNameInput" />
        <br/>

        <label>Completion Status:</label>
        <input type="radio" name="completionStatus" value="true" id="completedRadio" /> Completed
        <input type="radio" name="completionStatus" value="false" id="incompleteRadio" /> Incomplete

      <br/>
      <br/>
        <label>Technology:</label>
        <select id="technologyDropdown">
          <option value="PowerApps">PowerApps</option>
          <option value="SPFx">SPFx</option>
          <option value="PowerBI">PowerBI</option>
          <option value="SharePoint">SharePoint</option>
        </select>
      </div>
    </div>
  </section>`;
this.getAllItems();
document.getElementById("createBtn")?.addEventListener("click", () => {
  this.createItem();
});
document.getElementById("updateBtn")?.addEventListener("click", () => {
  this.updateItem();
});
document.getElementById("deleteBtn")?.addEventListener("click", () => {
  this.deleteItem();
});

}

protected async onInit(): Promise<void> {
  this._sp = spfi().using(SPFx(this.context));
}
private async getAllItems(): Promise<void> {
  try {
    const items = await this._sp.web.lists.getByTitle("ProjectDetails").items();

    const tableBody: HTMLElement | null = this.domElement.querySelector("#projectTableBody");

    if (tableBody) {
      tableBody.innerHTML = ""; 

      items.forEach(item => {
        const row = document.createElement("tr");

        row.innerHTML = `
          <td><input type="radio" name="itemSelect" data-id="${item.Id}" /></td>
          <td>${item.ProjectName || ''}</td>
          <td>${item.CompletionStatus ? "Completed" : "Incomplete"}</td>
          <td>${item.Technology || ''}</td>
        `;
        tableBody.appendChild(row);
        const radioBtn = row.querySelector("input[type='radio']");
        radioBtn?.addEventListener("click", (e) => {
          e.stopPropagation();
          this.loadItemToForm(item); 
        });

      });
    }
  } catch (err) {
    console.error("Error fetching list items:", err);
  }
}
private async createItem(): Promise<void> {
  const projectNameInput = (document.getElementById("projectNameInput") as HTMLInputElement).value;
  const completionStatusInput = (document.querySelector('input[name="completionStatus"]:checked') as HTMLInputElement)?.value;
  const technologyInput = (document.getElementById("technologyDropdown") as HTMLSelectElement).value;

  if (!projectNameInput || !technologyInput) {
    alert("Please fill in all fields before creating the item.");
    return;
  }

  try {
    await this._sp.web.lists.getByTitle("ProjectDetails").items.add({
      ProjectName: projectNameInput,
      CompletionStatus: completionStatusInput === "true",
      Technology: technologyInput
    });

    alert("Item created successfully!");
    this.clearFormFields();
    this.getAllItems(); 
  } catch (error) {
    console.error("Error creating item:", error);
    alert("Error creating item. Check console for details.");
  }
}
private clearFormFields(): void {
  (document.getElementById("projectNameInput") as HTMLInputElement).value = "";
  (document.getElementById("completedRadio") as HTMLInputElement).checked = false;
  (document.getElementById("technologyDropdown") as HTMLSelectElement).selectedIndex = 0;
  this._selectedItemId = null; 
}
private async updateItem(): Promise<void> {
  if (!this._selectedItemId) {
    alert("Please select an item to update.");
    return;
  }

  const projectNameInput = (document.getElementById("projectNameInput") as HTMLInputElement).value;
  const completionStatusInput = (document.querySelector('input[name="completionStatus"]:checked') as HTMLInputElement)?.value;
  const technologyInput = (document.getElementById("technologyDropdown") as HTMLSelectElement).value;

  if (!projectNameInput || !technologyInput) {
    alert("Please fill in all fields before updating.");
    return;
  }

  try {
    await this._sp.web.lists.getByTitle("ProjectDetails").items.getById(this._selectedItemId).update({
      ProjectName: projectNameInput,
      CompletionStatus: completionStatusInput === "true",
      Technology: technologyInput
    });

    alert("Item updated successfully!");
    this.clearFormFields();
    this.getAllItems();
  } catch (error) {
    console.error("Error updating item:", error);
    alert("Error updating item. See console for details.");
  }
}

private loadItemToForm(item: any): void {
  this._selectedItemId = item.Id;

  const projectNameInput = this.domElement.querySelector("#projectNameInput") as HTMLInputElement;
  const completedRadio = this.domElement.querySelector("#completedRadio") as HTMLInputElement;
  const incompleteRadio = this.domElement.querySelector("#incompleteRadio") as HTMLInputElement; 
  const technologyDropdown = this.domElement.querySelector("#technologyDropdown") as HTMLSelectElement;

  if (projectNameInput) {
    projectNameInput.value = item.ProjectName || "";
  }

  if (completedRadio && incompleteRadio) {
    if (item.CompletionStatus === true) {
      completedRadio.checked = true;
    } else {
      incompleteRadio.checked = true;
    }
  }

  if (technologyDropdown) {
    technologyDropdown.value = item.Technology || "";
  }
}
private async deleteItem(): Promise<void> {
  if (!this._selectedItemId) {
    alert("Please select an item to delete.");
    return;
  }

  const confirmDelete = confirm("Are you sure you want to delete this item?");
  if (!confirmDelete) return;

  try {
    await this._sp.web.lists.getByTitle("ProjectDetails").items.getById(this._selectedItemId).delete();

    alert("Item deleted successfully!");

    this._selectedItemId = null;         
    this.clearFormFields();              
    this.getAllItems();                  
  } catch (error) {
    console.error("Error deleting item:", error);
    alert("Error deleting item. See console for details.");
  }
}
 protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {
    if (!currentTheme) {
      return;
    }
    const {
      semanticColors
    } = currentTheme;

    if (semanticColors) {
      this.domElement.style.setProperty('--bodyText', semanticColors.bodyText || null);
      this.domElement.style.setProperty('--link', semanticColors.link || null);
      this.domElement.style.setProperty('--linkHovered', semanticColors.linkHovered || null);
    }

  }

  protected get dataVersion(): Version {
    return Version.parse('1.0');
  }

  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: {
            description: strings.PropertyPaneDescription
          },
          groups: [
            {
              groupName: strings.BasicGroupName,
              groupFields: [
                PropertyPaneTextField('description', {
                  label: strings.DescriptionFieldLabel
                })
              ]
            }
          ]
        }
      ]
    };
  }
}

Once the code is ready, you can run the following command to run the SPFx solution locally.

gulp serve

This will open the local workbench. While the local workbench is running, open the SharePoint workbench and make sure the list is present in that site.

In the SharePoint Online site workbench, add the SPFxCrud webpart, and it will appear as shown below:

spfx crud operations using pnp

If you want to deploy the CRUD operations with SharePoint Framework client-side web parts to SharePoint Online App catalog or site collection app catalog in SharePoint Online, then we need to create the .sppkg file by running the below command:

gulp bundle --ship

gulp package-solution --ship

Once you run the above commands, it will create the .sppkg file in the sharepoint\solution folder.

I hope you found this article helpful!, I explained the steps for implementing CRUD operations in an SPFx web part using the PnP JS library and without a JavaScript framework. Also, I explained how the functions work for each CRUD operation.

You may like the following SPFx tutorials:

2 thoughts on “SPFx CRUD Operations using No JavaScript Framework”

Leave a Comment

Live Webinar

Build an IT Help Desk App using Power Apps and Power Automate

Join this free live session and learn how to build a fully functional IT Help Desk application using Power Apps and Power Automate—step by step.

📅 29th Apr 2026 – 10:00 AM EST | 7:30 PM IST

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

Power Platform Tutorial

FREE Power Platform Tutorial PDF

Download 135+ Pages FREE PDF on Microsoft Power Platform Tutorial. Learn Now…