sharepoint-spfx
v1.0.0Build, debug, package and deploy SharePoint Framework (SPFx) web parts, extensions and apps for SharePoint Online and Microsoft Teams.
by VladoIvankovic · 0 installs · updated 5/18/2026
Install
/skills install VladoIvankovic/sharepoint-spfxRuns in any Codeep session (TUI, Zed, or VS Code extension). Writes to .codeep/skills/sharepoint-spfx/SKILL.md in your project.
SKILL.md
---
name: sharepoint-spfx
description: Build, debug, package and deploy SharePoint Framework (SPFx) web parts, extensions and apps for SharePoint Online and Microsoft Teams.
version: 1.0.0
triggers:
- sharepoint
- spfx
- sharepoint framework
- sharepoint web part
- sharepoint extension
- teams app sharepoint
- app catalog
- yo @microsoft/sharepoint
- gulp serve
- sppkg
---
# SharePoint Framework (SPFx) — End-to-End
You are helping a developer build a SharePoint Framework solution
(SPFx) — Microsoft's modern client-side development model for SharePoint
Online, on-premises SharePoint 2019+, and Microsoft Teams. Follow this
guide whenever the user mentions SharePoint, SPFx, web parts,
extensions, or App Catalog deployment.
## Always check first
Before scaffolding or running any build, verify the environment.
**SPFx is notoriously picky about Node, npm, and gulp versions.** Mismatch
is the #1 source of "works on my machine" failures.
```bash
node --version # SPFx 1.18+ needs Node 18.x LTS (NOT 20+)
npm --version # 9.x or 10.x
gulp --version # CLI: 2.3+, Local: 4.0+
yo --version # 5.x
```
If Node is wrong, recommend `nvm install 18 && nvm use 18`. Do not
proceed until Node is on the right LTS for the target SPFx version:
| SPFx version | Node LTS | TypeScript | gulp |
| --- | --- | --- | --- |
| 1.18 (latest) | 18 | 4.7 | 4.0 |
| 1.17 | 16 or 18 | 4.5 | 4.0 |
| 1.16 | 16 | 4.5 | 4.0 |
| 1.15 | 14 or 16 | 4.5 | 4.0 |
## Scaffolding a new solution
Use the Microsoft Yeoman generator. Don't hand-roll the project — the
generator wires up gulp, tsconfig, package-solution.json, manifests
and the resource files correctly.
```bash
# One-time
npm install -g yo gulp-cli @microsoft/generator-sharepoint
# Scaffold (interactive)
mkdir my-spfx-solution && cd my-spfx-solution
yo @microsoft/sharepoint
```
The generator asks:
- **Solution name** — kebab-case
- **Target environment** — *SharePoint Online* (recommended) or *2019+ on-prem*
- **Folder** — current vs new
- **Tenant-wide deploy** — yes if you want to skip per-site enable later
- **Component type** — *Web Part* / *Extension* / *Library* / *ACE (Adaptive Card Extension)*
- **Web part name** — display name
- **Framework** — *No JavaScript framework* / *React* / *Minimal*
For most modern projects pick: **SharePoint Online → React → tenant-wide deploy: yes**.
## Project structure
```
my-spfx-solution/
├── config/
│ ├── config.json # bundle and externals
│ ├── package-solution.json # solution id, version, features
│ ├── serve.json # gulp serve URL + page
│ └── write-manifests.json # CDN url for prod bundle
├── src/webparts/<name>/
│ ├── <Name>WebPart.ts # entry point — property pane + render()
│ ├── <Name>WebPart.manifest.json # IDs, supported hosts, preconfigured entries
│ ├── loc/ # localisation strings (mystrings.d.ts + en-us.js)
│ └── components/ # React components (if React framework)
├── sharepoint/ # generated .sppkg lands here after `gulp package-solution`
├── package.json
├── tsconfig.json
└── gulpfile.js
```
Never edit anything in `lib/`, `dist/`, `temp/`, `release/`, or
`sharepoint/solution/` directly — they're build output.
## Local dev workflow
```bash
# Trust the dev cert (one-time per machine)
gulp trust-dev-cert
# Start the local workbench + watch
gulp serve
# Opens https://localhost:4321/temp/workbench.html — drop the web part on the canvas
```
For testing against a real SharePoint page (not the local workbench),
edit `config/serve.json` and set `initialPage` to a hosted workbench URL:
```json
{
"initialPage": "https://contoso.sharepoint.com/_layouts/15/workbench.aspx"
}
```
Then `gulp serve --nobrowser` and open that URL manually with the
`?loadSPFX=true&debugManifestsFile=https://localhost:4321/temp/manifests.js`
query string appended. SharePoint loads your local bundle from
`localhost:4321` instead of the CDN.
## Build and package for deployment
```bash
# Production bundle — minified, CDN-ready
gulp clean
gulp bundle --ship
# Package into .sppkg (lands in sharepoint/solution/<name>.sppkg)
gulp package-solution --ship
```
`--ship` flag is critical — without it the bundle is debug/dev quality
and the manifests point at `https://localhost:4321/`.
## Deploy to App Catalog
1. Upload `<name>.sppkg` from `sharepoint/solution/` to the
**tenant App Catalog**:
`https://<tenant>.sharepoint.com/sites/appcatalog/AppCatalog`
2. Click **Deploy** on the prompt. If `skipFeatureDeployment: true`
was set in `package-solution.json`, it's tenant-wide immediately;
otherwise you must add it per-site via Site Contents → New → App.
3. Bump version in `config/package-solution.json` (`solution.version`)
for every update — SharePoint deduplicates by version, an upload
with the same version silently no-ops.
## Common gotchas — surface these proactively
- **Node version drift.** If `gulp serve` errors with cryptic OpenSSL or
`digital envelope routines::unsupported`, the user is on Node 18+
with an SPFx version that wants Node 16. Either downgrade Node or
set `NODE_OPTIONS=--openssl-legacy-provider` as a workaround.
- **Cert not trusted.** Browser blocks `https://localhost:4321` with a
TLS warning even after `gulp trust-dev-cert`. On macOS: open
Keychain Access → log-in → search "SPFx" → set to Always Trust.
- **`gulp serve` shows blank workbench.** Check the browser console — the
manifest URL probably has a wrong port. Fix `config/serve.json` or
re-run with `--port 4322` if 4321 is taken.
- **`gulp package-solution` complains about icon.** SPFx requires a
`assets/iconColor.png` (96×96) and `iconOutline.png` (32×32). Add
them and reference in `package-solution.json` → `metadata.icon`.
- **Web part not appearing in modern toolbox.** Verify the manifest
`supportedHosts` includes the host you're on (e.g. `SharePointWebPart`,
`TeamsTab`, `TeamsPersonalApp`). Also: app must be **enabled** on
the site (Site Contents → Add an app).
- **App Catalog upload silently does nothing.** Either the user doesn't
have App Catalog permissions, or the package version equals the
previously uploaded one. Increment `solution.version` in
`config/package-solution.json` (4-segment, e.g. `1.0.0.1`).
- **API permissions for Graph / external APIs.** Declare in
`package-solution.json` under `webApiPermissionRequests` *and* approve
in **SharePoint admin center → Advanced → API Access** — declaring
alone is not enough.
## Property pane patterns
Property pane is the right sidebar where users configure the web part.
Most common controls:
```ts
import {
PropertyPaneTextField,
PropertyPaneDropdown,
PropertyPaneToggle,
PropertyPaneSlider,
PropertyPaneCheckbox,
} from '@microsoft/sp-property-pane';
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: { description: strings.PropertyPaneDescription },
groups: [
{
groupName: 'Settings',
groupFields: [
PropertyPaneTextField('listName', {
label: 'List name',
description: 'Name of the SharePoint list to query',
}),
PropertyPaneDropdown('itemLimit', {
label: 'Items to show',
options: [
{ key: 10, text: '10' },
{ key: 25, text: '25' },
{ key: 50, text: '50' },
],
}),
],
},
],
},
],
};
}
```
For async-loaded dropdowns (e.g. list of all lists on the current site),
use `PropertyFieldListPicker` from `@pnp/spfx-property-controls`.
## Calling SharePoint REST APIs
Use the SPFx HTTP client — it auto-handles auth tokens against the
current site.
```ts
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
async getItems(): Promise<IListItem[]> {
const response: SPHttpClientResponse = await this.context.spHttpClient.get(
`${this.context.pageContext.web.absoluteUrl}/_api/web/lists/getbytitle('${this.properties.listName}')/items?$top=${this.properties.itemLimit}`,
SPHttpClient.configurations.v1,
);
const data = await response.json();
return data.value;
}
```
For Microsoft Graph: use `this.context.msGraphClientFactory.getClient('3')`.
For external APIs: use `this.context.aadHttpClientFactory.getClient(resourceUri)`.
**Plain `fetch()` will not auth correctly** — always use the SPFx clients.
## PnPjs — the idiomatic data layer
If the project does any non-trivial SharePoint data work, recommend
PnPjs. It abstracts the REST + Graph boilerplate into a fluent API.
```bash
npm install @pnp/sp @pnp/graph
```
```ts
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('Tasks').items.top(10)();
```
## Microsoft Teams targeting
To make a web part also work as a Teams tab:
1. In manifest add `"supportedHosts": ["SharePointWebPart", "TeamsTab", "TeamsPersonalApp"]`
2. Generate Teams app manifest: `gulp package-solution --ship` includes it in `.sppkg`
3. Deploy `.sppkg` to App Catalog with **"Make this solution available to all sites"** ticked
4. Sync to Teams: App Catalog → ribbon → **Sync to Teams**
## What NOT to do
- Do NOT recommend the classic SharePoint Add-in model (`appmanifest.xml`
+ Office Store) — Microsoft has stopped accepting new submissions
since 2024 and the model is deprecated.
- Do NOT recommend SharePoint Designer or InfoPath — both EOL.
- Do NOT use jQuery in new SPFx solutions — React or vanilla TS.
- Do NOT commit `node_modules/`, `temp/`, `dist/`, `lib/`, `release/`,
or the generated `*.sppkg`. The `.gitignore` from `yo` covers this;
if missing, add it.
- Do NOT store secrets in `package-solution.json` or any client-side
file — SPFx bundles ship to every user. Use Azure AD app + Graph
on-behalf-of, or a Function App proxy.
## Sources of truth (let the user open these)
- Microsoft Learn — SPFx: <https://learn.microsoft.com/en-us/sharepoint/dev/spfx/sharepoint-framework-overview>
- Compatibility matrix: <https://learn.microsoft.com/en-us/sharepoint/dev/spfx/compatibility>
- PnP samples: <https://github.com/pnp/sp-dev-fx-webparts>
- PnPjs docs: <https://pnp.github.io/pnpjs/>