Cas pratique
Créer une web part SPFx qui lit et écrit dans une liste SharePoint : tutoriel PnPjs avec le code complet
Par Ktifa · ·
Pour qu’une web part SPFx lise et écrive dans une liste SharePoint, installez PnPjs (@pnp/sp), créez une instance avec spfi().using(SPFx(this.context)) dans onInit, puis appelez lists.getByTitle(…).items pour lire et items.add pour écrire. La web part agit avec les droits de l’utilisateur connecté : aucune inscription d’application n’est nécessaire.
Besoin d’un développeur SPFx pour aller plus vite ?Voir l’expertise SPFx
Pourquoi une web part plutôt qu’une simple vue de liste
Une liste SharePoint fait déjà beaucoup : colonnes typées, vues filtrées, droits, historique des versions. Pour beaucoup de besoins, une vue bien réglée suffit. Mais dès que vous voulez une saisie guidée sur la page d’accueil de l’intranet, un affichage propre à votre métier ou des règles que la vue standard ne sait pas exprimer, une web part SharePoint Framework (SPFx) devient l’outil adapté.
Bonne nouvelle : c’est inclus dans votre abonnement Microsoft 365. Pas de serveur à louer, pas de licence en plus. La web part s’exécute dans la page, avec l’identité de la personne connectée.
Ce tutoriel construit un exemple type : un petit suivi des demandes internes (demandes au service administratif, au support, aux services généraux). La web part affiche les 20 dernières demandes non terminées et permet d’en ajouter une. Le code a été compilé et empaqueté avec SPFx 1.23.2 et PnPjs 4.22.0 le 20 octobre 2026, sans erreur ni avertissement du linter.
Ce qu’il vous faut
| Élément | Version utilisée | Source |
|---|---|---|
| SharePoint Framework | 1.23.2 (dernière version stable sur npm) | Compatibilité SPFx |
| Node.js | 22 LTS | Compatibilité SPFx |
| TypeScript | 5.8 (fourni par le générateur) | Compatibilité SPFx |
| PnPjs | 4.22.0 (@pnp/sp) |
Documentation PnPjs |
| Chaîne de build | Heft (remplace gulp depuis SPFx 1.22) | Chaîne Heft |
À jour au 23 octobre 2026. La dernière version stable reste SPFx 1.23.2. La version candidate 1.24.0-rc.0 est désormais publiée sur npm : elle passera React 18.x en standard et prendra en charge Node.js 24 et 26, pour une disponibilité générale visée en octobre 2026 (feuille de route SPFx, Microsoft). Si vous avez des solutions existantes, testez-les sur 1.24 avant de migrer : le passage à React 18 peut changer le rendu. Ce tutoriel reste sur 1.23.2, la version stable.
Côté SharePoint, créez une liste Demandes sur votre site, avec la colonne Titre existante et une colonne de type Choix nommée Statut (valeurs : Nouvelle, En cours, Terminée). Créez la colonne directement sous le nom « Statut », sans accent ni espace : le code utilise le nom interne de la colonne. Pour le vérifier, ouvrez les paramètres de la liste, cliquez sur la colonne et lisez la fin de l’adresse (Field=Statut).
Étape 1 : créer le projet
Depuis SPFx 1.22, les nouveaux projets utilisent Heft à la place de gulp. Les commandes gulp serve et gulp bundle des anciens tutoriels ne s’appliquent donc plus aux projets récents (Microsoft Learn).
npm install -g yo @microsoft/generator-sharepoint@1.23.2
mkdir suivi-demandes && cd suivi-demandes
yo @microsoft/sharepoint
# Type de composant : WebPart · Nom : Demandes · Modèle : No framework
npm install @pnp/sp@4.22.0 --save-exact
Le choix « No framework » garde l’exemple lisible. Le même code PnPjs fonctionne à l’identique dans un composant React.
Étape 2 : le code de la web part
Remplacez le contenu de src/webparts/demandes/DemandesWebPart.ts par le fichier ci-dessous. Trois points comptent :
- Une seule instance PnPjs, créée dans
onInitavec le contexte de la web part. La documentation PnPjs signale comme erreur courante le fait de la recréer dans chaque gestionnaire d’événement (PnPjs). - Des imports sélectifs (
@pnp/sp/webs,lists,items) : vous n’embarquez que ce que vous utilisez, ce qui limite le poids du bundle. - Tout ce qui vient de la liste passe par
escapeavant d’être injecté dans la page. Un titre saisi par un utilisateur ne doit jamais devenir du HTML exécutable.
import { Version } from '@microsoft/sp-core-library';
import {
type IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { escape } from '@microsoft/sp-lodash-subset';
import { spfi, SPFx, type SPFI } from '@pnp/sp';
import '@pnp/sp/webs';
import '@pnp/sp/lists';
import '@pnp/sp/items';
import styles from './DemandesWebPart.module.scss';
export interface IDemandesWebPartProps {
listTitle: string;
}
interface IDemande {
Id: number;
Title: string;
Statut: string;
Created: string;
}
export default class DemandesWebPart extends BaseClientSideWebPart<IDemandesWebPartProps> {
private _sp!: SPFI;
protected async onInit(): Promise<void> {
await super.onInit();
// Une seule instance PnPjs, créée avec le contexte de la web part (jeton et URL du site).
this._sp = spfi().using(SPFx(this.context));
}
public render(): void {
this.domElement.innerHTML = `
<section class="${styles.demandes}">
<h2>Demandes en cours</h2>
<form class="${styles.form}">
<input name="titre" required maxlength="255" placeholder="Nouvelle demande" />
<button type="submit">Ajouter</button>
</form>
<ul class="${styles.liste}"><li>Chargement…</li></ul>
</section>`;
const form = this.domElement.querySelector('form') as HTMLFormElement;
form.addEventListener('submit', (event: Event) => {
event.preventDefault();
const champ = form.elements.namedItem('titre') as HTMLInputElement;
this._ajouter(champ.value.trim())
.then(() => { champ.value = ''; })
.catch((erreur) => console.error(erreur));
});
this._charger().catch((erreur) => console.error(erreur));
}
private async _charger(): Promise<void> {
const liste = this.domElement.querySelector('ul') as HTMLUListElement;
try {
const demandes: IDemande[] = await this._sp.web.lists
.getByTitle(this.properties.listTitle)
.items.select('Id', 'Title', 'Statut', 'Created')
.filter("Statut ne 'Terminée'")
.orderBy('Created', false)
.top(20)();
liste.innerHTML = demandes.length === 0
? '<li>Aucune demande en cours.</li>'
: demandes.map((d) => `
<li>
<strong>${escape(d.Title)}</strong>
<span>${escape(d.Statut)} · ${new Date(d.Created).toLocaleDateString('fr-FR')}</span>
</li>`).join('');
} catch (erreur) {
console.error(erreur);
liste.innerHTML = '<li>Impossible de lire la liste. Vérifiez son nom et vos droits.</li>';
}
}
private async _ajouter(titre: string): Promise<void> {
if (!titre) {
return;
}
await this._sp.web.lists
.getByTitle(this.properties.listTitle)
.items.add({ Title: titre, Statut: 'Nouvelle' });
await this._charger();
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: { description: 'Source des données' },
groups: [
{
groupName: 'Liste SharePoint',
groupFields: [
PropertyPaneTextField('listTitle', { label: 'Nom de la liste' })
]
}
]
}
]
};
}
}
Dans DemandesWebPart.manifest.json, mettez la valeur par défaut de la propriété : "properties": { "listTitle": "Demandes" }. Le nom de la liste reste modifiable dans le volet de propriétés, sans redéployer.
Ajoutez enfin les classes form et liste dans DemandesWebPart.module.scss (une mise en forme simple suffit), puis supprimez les références aux images et aux textes de bienvenue du modèle si vous ne les utilisez plus.
Étape 3 : tester sur une vraie page
Lancez le serveur local :
npm run start
Le script exécute heft start, qui sert vos fichiers sur https://localhost:4321 et affiche dans la console une chaîne de paramètres (?debugManifestsFile=…&debug=true&noredir=true) à ajouter à l’adresse d’une page de votre site (Microsoft Learn). Ouvrez une page de test, ajoutez ces paramètres, autorisez les scripts de débogage : la barre d’outils de débogage SPFx apparaît et votre web part est disponible dans la boîte à outils de la page (Microsoft Learn).
Prenez l’habitude dès maintenant : le workbench SharePoint en ligne est déprécié depuis le 13 mai 2026 et sera retiré le 1er décembre 2026 (Microsoft Learn).
Étape 4 : empaqueter et déployer
npm run build
Dans un projet SPFx 1.23, ce script lance les tests et le linter, compile en mode production puis produit le paquet sharepoint/solution/suivi-demandes.sppkg. Un administrateur SharePoint le charge ensuite dans le site Applications (centre d’administration SharePoint, Plus de fonctionnalités > Applications > Ouvrir, puis Charger), et peut choisir Activer cette application et ajoutez-la à tous les sites (Microsoft Learn). Microsoft recommande de tester une solution avant de la déployer à grande échelle : commencez par un seul site.
Les pièges les plus fréquents
| Symptôme | Cause probable | Correction |
|---|---|---|
| « Impossible de lire la liste » | Nom de liste erroné, ou l’utilisateur n’a pas accès à la liste | Vérifier le nom dans le volet de propriétés et les droits du site |
Erreur sur le filtre Statut |
Le nom interne de la colonne n’est pas « Statut » | Lire le nom interne dans l’adresse des paramètres de la colonne et l’utiliser dans select et filter |
| Erreur de seuil sur une grosse liste | Plus de 5 000 éléments et filtre sur une colonne non indexée | Indexer la colonne filtrée ; le seuil de 5 000 n’est pas modifiable dans SharePoint Online (Microsoft Learn, Support Microsoft) |
gulp: command not found |
Tutoriel écrit pour l’ancienne chaîne | Utiliser npm run start et npm run build (Heft) |
| Comportements étranges avec React | Version de React différente de celle attendue | SPFx 1.23 attend React 17.0.1 exactement, à installer avec --save-exact (Microsoft Learn) |
PnPjs ou SPHttpClient ?
SPFx fournit SPHttpClient, qui appelle l’API REST SharePoint avec les cookies d’authentification de l’utilisateur, sans dépendance supplémentaire. Microsoft le présente comme suffisant pour des requêtes simples, et conseille d’envisager PnPjs dès que les écritures et les requêtes complexes arrivent, parce que les requêtes REST écrites à la main sont sources d’erreurs (Microsoft Learn).
La même page rappelle la contrepartie : PnPjs est un projet communautaire, sans SLA ni support Microsoft, et c’est une dépendance à mettre à jour. Notre règle : PnPjs dès qu’une web part écrit dans une liste, avec une version figée (--save-exact) et une mise à jour planifiée à chaque montée de version SPFx.
Et ensuite ?
Cette web part est la brique de base d’une application métier sur SharePoint : la liste porte les données et les droits, la web part porte l’ergonomie, et Power Automate peut prendre le relais pour les notifications et les validations. Posée sur la page d’accueil, elle devient un élément de votre intranet SharePoint.
C’est le même principe que nous avons utilisé pour une réalisation réelle dans un cabinet de conseil : une web part SPFx où l’utilisateur sélectionne des fiches dans SharePoint et obtient un document Word unique et mis en page (assemblage de documents Word). Pour situer ce type de projet parmi vos priorités, lisez aussi les 12 usages concrets de SharePoint pour une PME.
Vous avez une liste Excel partagée qui mériterait de devenir une vraie application ? Réservez un appel de 30 minutes : nous regardons ensemble votre fichier et ce qu’une liste SharePoint et une web part changeraient pour vos équipes.