Live training: next batches coming soon. Join the waitlist:Waitlists open:Developer Training →Developer →Copilot Studio Training →Copilot Studio →

Fix: Module ‘”@pnp/sp/presets/all”‘ has no exported member ‘sp’ (TS2305) in SPFx

The error Module '"@pnp/sp/presets/all"' has no exported member 'sp' means your code is PnPjs v2, but npm installed v3 or v4. Those versions dropped the global sp object. Create your own instance with spfi() instead:

// Old (PnPjs v2) - fails on v3/v4 with TS2305
// import { sp } from "@pnp/sp/presets/all";
// sp.setup({ spfxContext: this.context });

// New (PnPjs v3/v4)
import { spfi, SPFx } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";

const sp = spfi().using(SPFx(this.context));
const items = await sp.web.lists.getByTitle("SPGDemo Products").items();

That returns the list items as plain JSON. No sp.setup(), no .get().

In this tutorial, we’ll reproduce the Module ‘”@pnp/sp/presets/all”‘ has no exported member ‘sp’ error in a real SPFx project, fix it with getSP(), and cover the TS2307 “Cannot find module” variants too.

Tested withVersion
SharePoint Framework1.23.2 (Heft toolchain)
Node.js22 LTS
React17.0.1
PnPjs@pnp/sp and @pnp/logging 4.22.0

What the TS2305 error looks like

I pasted classic v2 code into a fresh SPFx 1.23.2 web part (scaffolded as in the SPFx setup with Heft). VS Code underlined sp right away and listed both errors in the Problems panel:

Module '"@pnp/sp/presets/all"' has no exported member 'sp'. ts(2305)
Module '"@pnp/sp"' has no exported member 'sp'. ts(2305)
VS Code Problems panel showing Module @pnp/sp/presets/all has no exported member sp ts(2305)

The web part file had the old setup in onInit:

import { sp } from "@pnp/sp/presets/all";

protected async onInit(): Promise<void> {
  await super.onInit();
  sp.setup({ spfxContext: this.context });
}

npm run build stops at the TypeScript step with the same two errors:

Encountered 2 errors
  [build:typescript] src/webparts/pnpProducts/PnpProductsWebPart.ts:9:10 - (TS2305) Module '"@pnp/sp/presets/all"' has no exported member 'sp'.
  [build:typescript] src/webparts/pnpProducts/services/ProductService.ts:1:10 - (TS2305) Module '"@pnp/sp"' has no exported member 'sp'.
npm run build failing in the VS Code terminal with TS2305 has no exported member sp errors

Why does @pnp/sp have no exported member ‘sp’?

PnPjs v3 moved away from the global sp and graph objects and doesn’t export one at all. You build an instance with spfi() and attach behaviors like SPFx(this.context). v4 kept that model.

@pnp/sp/presets/all still exists in 4.22.0. I checked its type file: it attaches every module and re-exports types like IWeb, but nothing named sp. So changing the path to "@pnp/sp" just gives you the second TS2305.

Downgrading to v2 isn’t a real fix on SPFx 1.23. It’s the code that needs to change, the same way moving from gulp to Heft changed the build commands.

Fix it with spfi() and a getSP() helper

1. Install the packages. Microsoft Learn’s PnPjs guide installs @pnp/sp plus @pnp/logging, which is no longer a peer dependency. Run this in the project folder:

npm install @pnp/sp@4.22.0 @pnp/logging@4.22.0 --save-exact
npm ls @pnp/sp

2. Add a config file at src/webparts/pnpProducts/pnpjsConfig.ts. It creates the instance once and holds your selective imports:

import { WebPartContext } from "@microsoft/sp-webpart-base";
import { spfi, SPFI, SPFx } from "@pnp/sp";
import { LogLevel, PnPLogging } from "@pnp/logging";
// Selective imports: attach only the parts of PnPjs this solution uses
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";

let _sp: SPFI | undefined = undefined;

export const getSP = (context?: WebPartContext): SPFI => {
  if (context !== undefined) {
    // Runs once, from onInit: binds PnPjs to the web part's context
    _sp = spfi().using(SPFx(context)).using(PnPLogging(LogLevel.Warning));
  }
  if (_sp === undefined) {
    throw new Error("Call getSP(this.context) in onInit before using PnPjs.");
  }
  return _sp;
};
pnpjsConfig.ts in VS Code with spfi, SPFx and the selective imports for webs, lists and items

@pnp/sp/webs, /lists and /items attach exactly what sp.web.lists.getByTitle().items needs. Forget one and you’ll get a “property does not exist” error instead.

3. Call it from onInit in src/webparts/pnpProducts/PnpProductsWebPart.ts. This line replaces sp.setup():

import * as React from "react";
import * as ReactDom from "react-dom";
import { Version } from "@microsoft/sp-core-library";
import { type IPropertyPaneConfiguration, PropertyPaneTextField } from "@microsoft/sp-property-pane";
import { BaseClientSideWebPart } from "@microsoft/sp-webpart-base";

import PnpProducts from "./components/PnpProducts";
import { IPnpProductsProps } from "./components/IPnpProductsProps";
import { getSP } from "./pnpjsConfig";
import { ProductService } from "./services/ProductService";

export interface IPnpProductsWebPartProps {
  listTitle: string;
}

export default class PnpProductsWebPart extends BaseClientSideWebPart<IPnpProductsWebPartProps> {
  protected async onInit(): Promise<void> {
    await super.onInit();
    getSP(this.context); // replaces the old sp.setup({ spfxContext: this.context })
  }

  public render(): void {
    const element: React.ReactElement<IPnpProductsProps> = React.createElement(PnpProducts, {
      service: new ProductService(this.properties.listTitle),
      listTitle: this.properties.listTitle
    });
    ReactDom.render(element, this.domElement);
  }

  protected onDispose(): void {
    ReactDom.unmountComponentAtNode(this.domElement);
  }

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

  protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
    return {
      pages: [
        {
          header: { description: "Read a SharePoint list with PnPjs v4" },
          groups: [
            {
              groupName: "Settings",
              groupFields: [PropertyPaneTextField("listTitle", { label: "List title" })]
            }
          ]
        }
      ]
    };
  }
}

4. Use it in a service. The model goes in src/webparts/pnpProducts/models/IProduct.ts:

export interface IProduct {
  Id: number;
  Title: string;
  Modified: string;
}

And the data class in src/webparts/pnpProducts/services/ProductService.ts. Note the call ends in (), not .get():

import { SPFI } from "@pnp/sp";
import { getSP } from "../pnpjsConfig";
import { IProduct } from "../models/IProduct";

export class ProductService {
  private readonly _sp: SPFI;

  constructor(private readonly _listTitle: string) {
    this._sp = getSP(); // the instance created in onInit
  }

  public async getWebTitle(): Promise<string> {
    const web = await this._sp.web.select("Title")();
    return web.Title;
  }

  public async getProducts(): Promise<IProduct[]> {
    return this._sp.web.lists
      .getByTitle(this._listTitle)
      .items.select("Id", "Title", "Modified")
      .orderBy("Title")();
  }
}

5. Render the results. Props in src/webparts/pnpProducts/components/IPnpProductsProps.ts:

import { ProductService } from "../services/ProductService";

export interface IPnpProductsProps {
  service: ProductService;
  listTitle: string;
}

The component, src/webparts/pnpProducts/components/PnpProducts.tsx, loads both calls and shows a Fluent UI spinner or error bar:

import * as React from "react";
import { MessageBar, MessageBarType, Spinner } from "@fluentui/react";
import styles from "./PnpProducts.module.scss";
import { IPnpProductsProps } from "./IPnpProductsProps";
import { IProduct } from "../models/IProduct";

const PnpProducts: React.FC<IPnpProductsProps> = ({ service, listTitle }) => {
  const [webTitle, setWebTitle] = React.useState<string>("");
  const [products, setProducts] = React.useState<IProduct[] | undefined>(undefined);
  const [error, setError] = React.useState<string>("");

  React.useEffect(() => {
    Promise.all([service.getWebTitle(), service.getProducts()])
      .then(([title, items]) => {
        setWebTitle(title);
        setProducts(items);
      })
      .catch((err: Error) => setError(err.message));
  }, [service, listTitle]);

  if (error) {
    return <MessageBar messageBarType={MessageBarType.error}>{error}</MessageBar>;
  }
  if (!products) {
    return <Spinner label={`Loading ${listTitle}...`} />;
  }

  return (
    <section className={styles.pnpProducts}>
      <h2>{listTitle}</h2>
      <p>Site: {webTitle} - {products.length} items read with PnPjs v4 (spfi + SPFx)</p>
      <table className={styles.table}>
        <thead>
          <tr><th>ID</th><th>Product</th><th>Modified</th></tr>
        </thead>
        <tbody>
          {products.map((p) => (
            <tr key={p.Id}>
              <td>{p.Id}</td>
              <td>{p.Title}</td>
              <td>{new Date(p.Modified).toLocaleDateString("en-US")}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </section>
  );
};

export default PnpProducts;

Styles go in PnpProducts.module.scss. For a richer grid, see displaying list items with a DetailsList:

.pnpProducts {
  color: var(--bodyText);

  h2 {
    margin: 0 0 4px;
  }

  .table {
    border-collapse: collapse;
    width: 100%;

    th,
    td {
      border-bottom: 1px solid #edebe9;
      padding: 8px 12px;
      text-align: left;
    }

    th {
      background: #f3f2f1;
    }
  }
}

In the manifest I set the toolbox title and the default list:

"title": { "default": "SPG PnPjs Products" },
"officeFabricIconFontName": "ProductList",
"properties": {
  "listTitle": "SPGDemo Products"
}

Test the fix in the SharePoint workbench

Point config/serve.json at your site’s workbench, replacing {tenantDomain} with your tenant:

{
  "$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json",
  "port": 4321,
  "https": true,
  "initialPage": "https://{tenantDomain}/sites/SPGuides/_layouts/workbench.aspx"
}

Then start the dev server in the VS Code terminal. It compiles with zero errors and opens the workbench:

heft start
VS Code terminal running heft start for the fixed PnPjs web part on localhost port 4321

Click Load debug scripts, hover the canvas, click + and pick SPG PnPjs Products. On my SPGuides site it read all four products:

SPFx web part in the SharePoint workbench listing SPGDemo Products read with PnPjs v4 spfi

The hosted workbench retires on 12/01/2026. After that, add the debug query string heft start prints to any modern page instead.

Stop the server with Ctrl+C and build the package. This time it finishes and writes spg-pnpjs-imports.sppkg:

npm run build
npm run build finished in VS Code and created spg-pnpjs-imports.sppkg

Upload the .sppkg to the app catalog when you’re ready, as in your first SPFx web part.

Error TS2307: Cannot find module ‘@pnp/sp/presets/all’

TS2307 is a different problem: TypeScript can’t find the package at all. I reproduced it by running the v2 code with the @pnp/sp folder missing from node_modules:

[build:typescript] Error: src/webparts/pnpProducts/PnpProductsWebPart.ts:9:20 - (TS2307) Cannot find module '@pnp/sp/presets/all' or its corresponding type declarations.
[build:typescript] Error: src/webparts/pnpProducts/services/ProductService.ts:1:20 - (TS2307) Cannot find module '@pnp/sp' or its corresponding type declarations.

That happens after cloning a project without running npm install, or when @pnp/sp isn’t in package.json. Run the install command above, then fix the imports.

The same error with other packages is covered in cannot find module jquery in SPFx and cannot find module sp-build-web.

v3 imports that break on PnPjs v4

Code already on v3 can still fail after upgrading to v4. I left a v3 import of @pnp/sp/items/get-all in the fixed project. TypeScript passed, since it doesn’t check side-effect imports, but webpack failed:

[build:webpack] Error: ... lib/webparts/pnpProducts/pnpjsConfig.js:7:0 - Module not found: Error: Can't resolve '@pnp/sp/items/get-all' in '...\lib\webparts\pnpProducts'

v4 removed get-all. Page through big lists with the async iterator instead:

// v4 replacement for items.getAll(): async iterator paging
const all: IProduct[] = [];
for await (const page of sp.web.lists.getByTitle("SPGDemo Products").items.top(500)) {
  all.push(...page);
}

Two more v4 changes: taxonomy moved to @pnp/graph, and add() returns the new item directly, so drop .data. For filtering, see filtering list items in SPFx with PnPjs.

Common problems and fixes

  • Property ‘lists’ does not exist: add the matching selective import, like import "@pnp/sp/lists";, in pnpjsConfig.ts.
  • Errors stay red after the fix: run “TypeScript: Restart TS Server” from the VS Code command palette.
  • Extra tsconfig warnings in Problems: VS Code’s newer TypeScript flags moduleResolution node10 and target ES5. npm run build uses the project’s 5.8 and ignores them.
  • Calls fail at runtime: call getSP(this.context) after super.onInit(), not inside event handlers.

More PnPjs patterns, like binding list items to a Fluent UI dropdown, use the same getSP() setup. Ready-made projects are on our Power Platform and SPFx solutions page.

A few more SPFx guides you might want next:

Frequently Asked Questions

Why does @pnp/sp/presets/all have no exported member ‘sp’?

Since PnPjs v3 there’s no global sp object. The preset still exists but only attaches modules and re-exports types. Create an instance with spfi().using(SPFx(this.context)) instead.

What replaces sp.setup({ spfxContext: this.context })?

Call spfi().using(SPFx(this.context)) once in onInit, usually through a getSP() helper in pnpjsConfig.ts, as the Microsoft Learn PnPjs guide shows.

What does error TS2307 mean in SPFx?

TypeScript can’t find the module or its type declarations. Usually the package isn’t installed: run npm install @pnp/sp @pnp/logging, then check package.json.

Should I still use @pnp/sp/presets/all?

It works in v4, but the PnPjs selective imports docs say presets aren’t recommended in client-side solutions. Import only webs, lists and items.

Can I downgrade to PnPjs v2 to keep my old code?

Not on current SPFx. v2 is out of support and was built for older TypeScript and gulp. Updating the imports takes a few minutes, as shown above.