Getting Started with PnPjs in SPFx: Reading SharePoint Lists
When building solutions with Microsoft 365 and SharePoint Framework (SPFx), one of the most important skills is learning how to communicate with SharePoint data efficiently.
Before using advanced controls like PnP React Controls, it is essential to understand the foundation: PnPjs.
PnPjs is the data layer.
It simplifies communication with SharePoint REST APIs and provides a fluent, modern, and strongly typed way to query data.
This article introduces the initial setup and shows how to retrieve items from a SharePoint list.
Why PnPjs?
Without PnPjs, you would often write raw REST calls:
this.context.spHttpClient.get(...)
This works, but quickly becomes repetitive and harder to maintain.
PnPjs improves this by providing:
- Cleaner syntax
- Better readability
- Stronger maintainability
- Query chaining
- Easier filtering, sorting, and expanding
- Better scalability
Example:
Raw REST:
_api/web/lists/getbytitle('Bike Sales')/items
PnPjs:
sp.web.lists.getByTitle("Bike Sales").items()
Much cleaner.
Installing PnPjs
Install the required packages:
npm install @pnp/sp
Creating a central configuration
A common professional approach is to initialize PnPjs once and reuse it everywhere.
Create:
src/webparts/spPnPbasico/components/pnpjsConfig.ts
Code:
import { spfi, SPFI } from "@pnp/sp";import { SPFx } from "@pnp/sp/behaviors/spfx";import "@pnp/sp/webs";import "@pnp/sp/lists";import "@pnp/sp/items";let _sp: SPFI | undefined;export const getSP = (context?: any): SPFI => { if (!_sp && context) { _sp = spfi().using(SPFx(context)); } if (!_sp) { throw new Error("PnPjs has not been initialized."); } return _sp;};
This creates a singleton instance.
Benefits:
- One initialization
- Better performance
- Cleaner architecture
- Easier reuse
Passing SPFx Context
Inside your WebPart:
const element: React.ReactElement<ISpPnPbasicoProps> = React.createElement( SpPnPbasico, { context: this.context });
This is critical because PnPjs needs the SPFx context.
Without it, authentication and requests will fail.
Creating the React component
Inside:
SpPnPbasico.tsx
Code:
import * as React from 'react';import { useEffect } from "react";import { ISpPnPbasicoProps } from './ISpPnPbasicoProps';import { getSP } from "./pnpjsConfig";const SpPnPbasico: React.FC<ISpPnPbasicoProps> = (props) => { useEffect(() => { loadItems(); }, []); const loadItems = async (): Promise<void> => { try { const sp = getSP(props.context); const items = await sp.web.lists .getByTitle("Bike Sales") .items .select("Id", "Title")(); console.log("Bike Sales items:", items); } catch (error) { console.error("Error loading items:", error); } }; return ( <div> <h1>Welcome to SPFx with PnPjs!</h1> <p>Check the browser console to see the list items.</p> </div> );};export default SpPnPbasico;
Understanding the code
useEffect()
useEffect(() => { loadItems();}, []);
Runs once when the component loads.
Getting the PnP instance
const sp = getSP(props.context);
Retrieves the initialized singleton.
Getting list data
const items = await sp.web.lists .getByTitle("Bike Sales") .items .select("Id", "Title")();
Breakdown:
- getByTitle() → targets the list
- items → accesses list items
- select() → improves performance by limiting columns
- () → executes the query
Why select() matters
Bad:
.items()
Returns everything.
Good:
.items.select("Id", "Title")()
Returns only what you need.
This improves:
- performance
- payload size
- rendering speed
Always use select whenever possible.
What comes next?
This is the foundation.
In the next article, we will use this same PnPjs setup with PnP React Controls.
Specifically:
ListView
Why?
Because ListView is one of the most useful controls for rendering SharePoint data professionally.
Instead of:
console.log(items)
We will render:
- columns
- sorting
- selection
- custom rendering
- Fluent UI integration
Flow:
PnPjs → Data source
ListView → UI layer
This separation is one of the best architectural patterns in SPFx.
Final thoughts
PnPjs is not optional for serious SPFx development.
It is the foundation of:
- data retrieval
- filtering
- sorting
- paging
- CRUD operations
- integration with advanced controls
Mastering this first makes everything else easier.
In the next article:
PnP React Controls ListView with SharePoint List Data
