SharePoint Framework: CRUD Operations using ReactJS

For the last few weeks, I have been working with a client to build a SharePoint Framework web part. In that web part, we are required to retrieve SharePoint list items and allow users to update and delete the records.

As a SharePoint developer or SPFx developer, you should know how to do CRUD operations using React.

So, in this tutorial, I will explain how to perform CRUD operations using React in SPFx on SharePoint list items.

If you want to work with the No JavaScript framework, check out a tutorial on CRUD operations in SPFx using no JavaScript framework.

SharePoint List CRUD Operations using SPFx React

To do the CRUD operations using React JS in SPFx, we will use the following SharePoint Online list, which has a few columns, and the SharePoint list looks like the screenshot below.

SharePoint Framework CRUD operations using React JS

This SharePoint list has the following fields:

Column NameDataType
Title [EmployeeName]Default title field
JobDescriptionMultiple lines of text
HireDateDate and Time

To interact with SharePoint list items for CRUD operations, we use the PnPJS library. In the example below, you can see that I built an SPFx web part that includes a form at the top that lets users add a new employee record.

In the table below, you can see all the employee records we previously added. The user can edit a record by clicking the Edit button, and to delete a particular record, click the Delete button.

Here is a screenshot for your reference.

spfx crud operations using react

Now, we’ll see the steps below to perform the CRUD operations mentioned above.

Note: Here, we will use PnP JS to save data into the SharePoint list from the web part.

  1. We’ll start by creating the SPFx solution. So open the command prompt and run the code below.
md SPFxCURDReact

cd SPFxCURDReact

This will create an empty directory. Now, we need to scaffold the SPFx project.

  1. Run the below command to quickly create a SharePoint client-side solution project with the right toolchain and project structure.
yo @microsoft/sharepoint
  • What is your solution name? SPFxCURDReact
  • Which type of client-side component to create? WebPart
  • Add new Web part to solution sp-fx-crud-react.
  • What is your Web part name? ListCURDReact
  • Which template would you like to use? React
spfx crud operations using react
  1. To install the PnPJS library into our solution, run the command below.
npm install @pnp/sp --save
  1. Once it is installed successfully! Run the command below to open Visual Studio Code.
Code .
  1. Open the “ListCURDReactWebpart.ts” file and then provide the following PnP JS 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";
import "@pnp/sp/fields";
  1. In the same .ts file, add the below variable within the class.
private _sp: SPFI

We assigned the SPFI as a data type to this variable.

  1. Then, update your render() and onInit() methods as given below.
  public render(): void {
    const element: React.ReactElement<IListCrudReactProps> = React.createElement(
      ListCrudReact,
      {
        description: this.properties.description,
        isDarkTheme: this._isDarkTheme,
        environmentMessage: this._environmentMessage,
        hasTeamsContext: !!this.context.sdks.microsoftTeams,
        userDisplayName: this.context.pageContext.user.displayName,
        sp:this._sp,
        context:this.context,
      }
    );

    ReactDom.render(element, this.domElement);
  }

  protected onInit(): Promise<void> {
    return this._getEnvironmentMessage().then(message => {
      this._environmentMessage = message;
       this._sp = spfi().using(SPFx(this.context));
    });
  }

In the onInit() method, we created a PnP Instance [this._sp] by passing the current context. Then, in the render() method, along with default props, we additionally added the following ones:

  • sp:this._sp = Assigned PnP Instance to the prop sp.
  • context:this.context = Assigned the current context to the prop context
  1. Now, we need to update the “Props.ts”file with the props we passed in the render() method in the previous step.
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { SPFI } from "@pnp/sp";
export interface IListCrudReactProps {
  description: string;
  isDarkTheme: boolean;
  environmentMessage: string;
  hasTeamsContext: boolean;
  userDisplayName: string;
  sp:SPFI;
  context: WebPartContext
}

Here, we are adding the two props, sp and context. Now that the PnPJS setup is over, let’s see the actual web part code implementation in the step below.

  1. Open the “ListCrudReact.tsx” file and update your default code with the code below.
import * as React from "react";
import styles from "./ListCrudReact.module.scss";
import type { IListCrudReactProps } from "./IListCrudReactProps";

interface IState {
  items: any[];
  EmployeeName: string;
  JobDescription: string;
  HireDate: string;
  UpdateId: number | null;
}

export default class ListCrudReact extends React.Component<
  IListCrudReactProps,
  IState
> {
  constructor(props: IListCrudReactProps) {
    super(props);

    this.state = {
      items: [],
      EmployeeName: "",
      JobDescription: "",
      HireDate: "",
      UpdateId: null,
    };
  }

  public componentDidMount(): void {
    this.getItems();
  }
  private getItems = async (): Promise<void> => {
    try {
      const result = await this.props.sp.web.lists
        .getByTitle("EmployeeDetails")
        .items.select("Id", "Title", "JobDescription", "HireDate")();

      this.setState({ items: result });
    } catch (err) {
      console.error("Error reading items", err);
    }
  };

  private createItem = async (): Promise<void> => {
    try {
      await this.props.sp.web.lists.getByTitle("EmployeeDetails").items.add({
        Title: this.state.EmployeeName,
        JobDescription: this.state.JobDescription,
        HireDate: this.state.HireDate,
      });

      this.setState({
        EmployeeName: "",
        JobDescription: "",
        HireDate: "",
      });

      this.getItems();
    } catch (err) {
      console.error("Error creating item", err);
    }
  };

  private updateItem = async (): Promise<void> => {
    if (!this.state.UpdateId) return;

    try {
      await this.props.sp.web.lists
        .getByTitle("EmployeeDetails")
        .items.getById(this.state.UpdateId)
        .update({
          Title: this.state.EmployeeName,
          JobDescription: this.state.JobDescription,
          HireDate: this.state.HireDate,
        });

      this.setState({
        EmployeeName: "",
        JobDescription: "",
        HireDate: "",
        UpdateId: null,
      });

      this.getItems();
    } catch (err) {
      console.error("Error updating item", err);
    }
  };

  private editItem = (item: any): void => {
    this.setState({
      UpdateId: item.Id,
      EmployeeName: item.Title,
      JobDescription: item.JobDescription,
      HireDate: item.HireDate ? item.HireDate.split("T")[0] : "",
    });
  };

  private deleteItem = async (id: number): Promise<void> => {
    try {

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

      await this.props.sp.web.lists
        .getByTitle("EmployeeDetails")
        .items.getById(id)
        .delete();

      this.setState({
        EmployeeName: "",
        JobDescription: "",
        HireDate: "",
        UpdateId: null,
      });

      this.getItems();
    } catch (err) {
      console.error("Error deleting item", err);
    }
  };

  public render(): React.ReactElement<IListCrudReactProps> {
    return (
      <section className={styles.listCrudReact}>
        <h2>Employee CRUD Using React</h2>
        <div className={styles.formContainer}>

          <div className={styles.formRow}>
            <label className={styles.formLabel}>Employee Name</label>
            <div className={styles.formField}>
              <input
                type="text"
                value={this.state.EmployeeName}
                onChange={(e) =>
                  this.setState({ EmployeeName: e.target.value })
                }
              />
            </div>
          </div>
          <div className={styles.formRow}>
            <label className={styles.formLabel}>Job Description</label>
            <div className={styles.formField}>
              <textarea
                value={this.state.JobDescription}
                onChange={(e) =>
                  this.setState({ JobDescription: e.target.value })
                }
              />
            </div>
          </div>
          <div className={styles.formRow}>
            <label className={styles.formLabel}>Hire Date</label>
            <div className={styles.formField}>
              <input
                type="date"
                value={this.state.HireDate}
                onChange={(e) => this.setState({ HireDate: e.target.value })}
              />
            </div>
          </div>

          <div className={styles.formButton}>
            {this.state.UpdateId ? (
              <button onClick={this.updateItem}>Update Employee Record</button>
            ) : (
              <button onClick={this.createItem}>Add New Employee</button>
            )}
          </div>
        </div>
        <hr />
        <h3>Employees</h3>

        <table className={styles.itemsTable}>
          <thead>
            <tr>
              <th>Employee Name</th>
              <th>Job Description</th>
              <th>Hire Date</th>
              <th>Edit</th>
              <th>Delete</th>
            </tr>
          </thead>

          <tbody>
            {this.state.items.map((item) => (
              <tr key={item.Id}>
                <td>{item.Title}</td>
                <td>{item.JobDescription}</td>
                <td>{item.HireDate ? item.HireDate.split("T")[0] : ""}</td>
                <td>
                  <button onClick={() => this.editItem(item)}>Edit</button>
                </td>
                <td>
                  <button onClick={() => this.deleteItem(item.Id)}>
                    Delete
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </section>
    );
  }
}

Here:

  • IState will store the following details:
    • items = To store SharePoint list items.
    • EmployeeName = To store the employee name that we entered in the form.
    • JobDescription = Stores the job description entered in the form.
    • HireDate = The hire date we entered in the form.
    • UpdateId = To track the selected item in the table, used for edit and delete operations.
  • constructor() = Within this, we initialized the states with empty values.
  • componentDidMount() = This is a React life cycle method that will trigger when the web part is loaded. So we are calling the getItems () method to fetch the list items.
  • getItems() = In this method, we pass our list name to getByTitle() and retrieve only the Id, Title, JobDescription, and HireDate fields using select().
    • Then, the retrieved items were updated to the items‘ state.
  • render() = In this method, we displayed the input controls within a form and two buttons for saving a new record and updating an existing record, depending on the UpdateId.
    • Down to the form displaying the table, with the fields we are retrieving at the beginning. And iterated over each record in the list, adding the Edit and Delete buttons.
    • When clicking the edit button, we call the editItem(item) method with the selected item. Same for the delete button: we call deleteItem(item.Id) on click.
  • createItem() = This method takes the input values we enter in the form through the states and saves them to the list using the PnPJS method. It is called when we click the Add New Employee button.
  • updateItem() = This method will check if the UpdateId has a value; if yes, then it will get the updated information through the states and updates to the selected item.
  • editItem() = This method will update the state values with the selected item in the table.
  • deleteItem() = This method first shows a confirmation alert; if we select yes, it deletes the item; otherwise, it doesn’t.
  1. To apply the styles, update your .scss file styles with the one below.
@import '~@fluentui/react/dist/sass/References.scss';

.listCrudReact {
  max-width: 900px;       
  margin: 0 auto;         
  padding: 20px;
  background: #ffffff;
  border-radius: 8px;
  font-family: "Segoe UI", sans-serif;

  h2, h3 {
    margin-bottom: 15px;
    font-weight: 600;
  }
}

.formContainer {
  width: 100%;
  background: #fafafa;
  padding: 20px;
  border-radius: 10px;
  box-shadow: 0 2px 6px rgba(0,0,0,0.08);
  box-sizing: border-box;
  margin-bottom: 20px;
}

.formRow {
  display: flex;
  align-items: center;
  width: 100%;
  margin-bottom: 16px;
}

.formLabel {
  width: 180px;
  font-weight: 600;
  font-size: 14px;
}

.formField {
  flex: 1;

  input,
  textarea {
    width: 100%;
    padding: 10px;
    font-size: 14px;
    border: 1px solid #ccc;
    border-radius: 6px;
    box-sizing: border-box;
  }

  textarea {
    min-height: 90px;
  }
}

.formButton {
  width: 100%;
  margin-top: 10px;

  button {
    width: 100%;
    padding: 12px;
    background: #0078d4;
    border: none;
    border-radius: 6px;
    color: #fff;
    font-size: 15px;
    cursor: pointer;

    &:hover {
      background: #005a9e;
    }
  }
}

.itemsTable {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;

  th {
    background: #f3f2f1;
    padding: 12px;
    text-align: left;
    border-bottom: 2px solid #e1dfdd;
  }

  td {
    padding: 12px;
    border-bottom: 1px solid #e1dfdd;
  }

  tr:hover {
    background-color: #faf9f8;
  }

  button {
    padding: 6px 12px;
    background: #0078d4;
    color: #fff;
    border: none;
    border-radius: 4px;
    cursor: pointer;

    &:hover {
      background: #005a9e;
    }
  }

  td:last-child button {
    background: #d83b01;

    &:hover {
      background: #a52600;
    }
  }
}

Save the changes, and run the gulp serve command to test this web part locally.

Conclusion

This is how to do SPFx CRUD operations using ReactJS. Here we saw how to retrieve SharePoint list items and display them in a table, create a new item to add to the list, update the selected item’s details, and delete the selected item, all in a single SPFx web part using the React framework.

Download this solution and try it out with your own SharePoint list. Also, adjust this code to your own needs, and if you face any issues, comment below.

You can also check the SPFx tutorials below:

5 thoughts on “SharePoint Framework: CRUD Operations using ReactJS”

  1. context giving error in CrudReact.tsx
    src/webparts/crudReactDemo/components/CRUDReact.tsx(171,21): error TS2322: Type ‘WebPartContext’ is not assignable to type ‘BaseComponentContext’.
    [15:05:50] [tsc] Types have separate declarations of a private property ‘_serviceScope’.
    [15:05:50] Error – ‘tsc’ sub task errored after 19 s
    exited with code 2

    Please give me the solution

  2. First of all, thanks for the tutorial!!!
    I have a problem when i enter some data in the input field, it becomes read only and shows this text > [object Object]
    You know why?

    • Please check if your onChange function is used using arrow function. convert it into normal function

      private getName(e): void {
      this.setState({userName:e.target.value});
      }

  3. This is a fine tutorial and covers most of the issues I have in converting CEWP web parts to SPFx. I have been unable to correct three problems in the tutorial all in the tsx file: 1. styles.table in the var tabledata: table is unknown; 2. btngroup is unknown in styles.btngroup; and 3. context is unknown in context={this.props.context}. I face CRUD and PeoplePicker conversion issues in my CEWP apps. Your advice would be appreciated.

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