# Spfx Best Practices

> Enforces SharePoint Framework (SPFx) best practices when generating, reviewing or refactoring SPFx code. Ensures solutions are modern, maintainable, performant, secure and aligned with official guidance and community patterns.

- Skill: `pnp/spfx-best-practices` (Agent Skill)
- Install (CLI): `npx skillmds@latest add pnp/spfx-best-practices`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pnp/spfx-best-practices/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: pnp (https://skillmd.com/u/pnp)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/pnp/spfx-best-practices

---


## When to Use
Use this skill when:
- Creating or scaffolding new SPFx **web parts**, **extensions** (Application Customizers, Field Customizers, Command Sets) or libraries
- Reviewing or refactoring existing SPFx projects
- Extending existing solutions with new web parts or extensions while maintaining consistent patterns
- Answering questions about SPFx architecture, React, TypeScript, localization, styling (Sass), data access, authentication, Microsoft Graph, SharePoint APIs or tooling

## Scope
Applies to:
- SPFx **web parts**, **extensions** and **libraries**
- **React** with TypeScript
- **Localization** (`loc` resources)
- **Styling** with **Sass modules**
- **Data access** (SharePoint REST, Microsoft Graph, Entra ID–secured APIs)
- SPFx **toolchain** (Heft / gulp / webpack)
- Modern SPFx development workflows

## Technology Stack Constraints
When generating code, adhere to these specific library versions and standards unless instructed otherwise:
* **Framework:** SharePoint Framework (SPFx)
* **UI Library:** Fluent UI React (`@fluentui/react`)
* **Data Access:** PnPjs v4 (`@pnp/sp`, `@pnp/graph`, `@pnp/logging`)
* **Language:** TypeScript
* **State Management:** React Hooks
* **Styling:** CSS Modules (`.module.scss`) or Fluent UI Styling (`mergeStyles`)

## Best Practices to Enforce

### Project Structure & Architecture
- Use a **feature-based** or **layered** folder structure:

```text
src/
common/
controls/
helpers/
hooks/
models/
services/
webparts/
extensions/
```

- Apply **Separation of Concerns**
- Keep UI, business logic and data access isolated
- Prefer reusable services over inline API logic

### Toolchain & Build
- Target the **latest supported SPFx version** (e.g. 1.22.x+) if available.
  - Use **Heft** as the primary build orchestrator instead of gulp when starting new projects
- Avoid custom gulp hacks unless absolutely required
- Do not override toolchain defaults unless there is a documented, reproducible need
- Avoid unsupported patches and nonstandard build steps
- Keep configuration minimal and aligned with SPFx defaults
- Validate guidance against current SPFx release notes before shipping

### TypeScript & Code Quality
- Enable strict TypeScript settings
- Avoid `any`; use interfaces, enums and generics
- Prefer `async/await`
- Keep utilities framework-agnostic where possible

### React Best Practices
- Prefer **functional components** and **React Hooks**
- Keep components small and focused
- Extract logic into custom hooks or services
- Memoize expensive operations (`useMemo`, `useCallback`)
- Use Fluent UI for consistency and accessibility

### Localization
- Use SPFx `loc` files for all user-facing strings
- Never hardcode display text
- Reference strings via `strings.<Key>`
- Ensure graceful fallback for missing translations
- Avoid string concatenation; use placeholders for plurals, dates and numbers

### Styling (Sass)
- Use `.module.scss` files scoped to components
- Centralize shared variables, mixins and themes
- Avoid global CSS unless explicitly required
- Follow consistent naming conventions
- **Controls:** Use standard Fluent UI controls to match the M365 look and feel.
- **Theming:** strictly use `ThemeProvider` or the `themeVariant` object passed from the base context.
    * *Rule:* Never hardcode hex colors (e.g., `#0078d4`). Use `semanticColors` (e.g., `theme.semanticColors.bodyText`).
- **Tokens:** Prefer Fluent UI theme tokens over custom Sass variables for color and typography

### Data Access Patterns
- Centralize data access in service classes
- Use **PnPjs** for most SharePoint operations if available
- Use **SPHttpClient** when:
  - Performing simple or low-level REST calls
  - Avoiding additional abstractions
- Handle errors, throttling and retries explicitly
- Prefer **@pnp/sp** and **@pnp/graph** for SharePoint and Graph REST interactions. Use native clients only when PnPjs is not available or when minimizing bundle size is a strictly stated priority.

#### Decision Matrix: PnPjs vs Native Clients
- Use **@pnp/sp** for SharePoint data access whenever possible
- Use **@pnp/graph** for Microsoft Graph when PnPjs is available
- Use **MSGraphClientV3** only when PnPjs is not available or cannot be added
- Use **SPHttpClient** for minimal, low-level REST calls or bundle size constraints

### Authentication & API Usage
- Use **SPHttpClient** or PnPjs for SharePoint REST APIs
- Use **MSGraphClientV3** for Microsoft Graph access when PnPjs is not available
- Use **AadTokenProvider** for Entra ID–secured custom APIs
- Request **least-privilege** permissions
- Ensure Graph scopes align with `webApiPermissionRequests` and require admin consent
- Never store secrets in client-side code

### Performance
- Lazy-load heavy components and libraries
- Use dynamic imports when appropriate
- Minimize bundle size
- Avoid unnecessary re-renders and excessive state
- Virtualize large lists and tables
- Avoid state updates in render loops

### Security
- Sanitize and validate all user input
- Respect tenant and site permissions
- Validate API responses before usage
- **No Secrets:** Never put client secrets or API keys in client-side code.
- **Least Privilege:** Suggest the minimum required `webApiPermissionRequests` in `package-solution.json`.
- **DOM Access:** Never use `document.getElementById`. Use React `useRef`.
- Avoid `dangerouslySetInnerHTML` unless the content is sanitized

### Testing & Quality
- Write unit tests for services and core logic
- Use **Jest** + **React Testing Library** for components and hooks
- Enforce ESLint and Prettier
- Keep dependencies updated
- Remove dead or commented-out code, unused imports and console logs before shipping

### Accessibility
- Ensure keyboard navigation and visible focus states
- Provide labels and aria attributes for all interactive controls
- Validate color contrast when using theme tokens

### Anti-Patterns to Avoid
- Hardcoded strings, colors, or dates
- Data access embedded in React components
- Direct DOM access instead of `useRef`
- Skipping error states or swallowing exceptions
- Rendering large lists without virtualization
- Accessibility regressions (missing labels, focus traps, low contrast)

### Do / Don't (Quick Scan)
| Do | Don't |
| --- | --- |
| Use `strings.<Key>` and placeholders | Hardcode user-facing strings |
| Use `themeVariant` and Fluent UI tokens | Hardcode hex colors |
| Centralize APIs in services | Call REST/Graph from components |
| Use `@pnp/graph` when available | Default to `MSGraphClientV3` |
| Add keyboard support and labels | Ship UI without a11y checks |

### Telemetry & Logging
- Use `@pnp/logging` or SPFx `Log` for error and diagnostic logging
- Never log secrets or PII

## Checklist
Before generating SPFx code, follow these steps:
1.  **Context Check:** Is this a Web Part, an Extension (Application Customizer, ListView), or a Library Component?
2.  **Environment Check:** Check `Environment.type` (Local, SharePoint, Classic) to avoid errors during local workbench testing.
3.  **Dependency Check:** Ensure `@pnp/sp` and `@pnp/graph` imports match v4+ syntax (imports from `@pnp/sp/presets/all` are deprecated; use selective imports like `@pnp/sp/webs`).
4.  **Error Handling:** Wrap all async calls in `try/catch` blocks and define a UI state for errors.

## Code Template: Functional Web Part

```typescript
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { spfi, SPFx } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";

import { MyComponent } from './components/MyComponent';
import { IMyComponentProps } from './components/IMyComponentProps';

export interface IMyWebPartProps {
  description: string;
}

export default class MyWebPart extends BaseClientSideWebPart<IMyWebPartProps> {
  private _sp: ReturnType<typeof spfi>;

  protected async onInit(): Promise<void> {
    await super.onInit();
    // Initialize PnPjs
    this._sp = spfi().using(SPFx(this.context));
  }

  public render(): void {
    const element: React.ReactElement<IMyComponentProps> = React.createElement(
      MyComponent,
      {
        description: this.properties.description,
        context: this.context,
        sp: this._sp
      }
    );
    ReactDom.render(element, this.domElement);
  }

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

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


## Expected Outcomes
When this skill is applied, agent should:
- Generate idiomatic SPFx + React + TypeScript code
- Highlight and explain SPFx anti-patterns
- Enforce modular, service-based architecture
- Apply localization, Sass modules and API best practices
- Prefer modern tooling (Heft) and current guidance

## Tone
- Clear, pragmatic and professional
- Explain *why* recommendations matter

## Example Prompt
- Review this SPFx solution and refactor it using SPFx best practices, including localization, Sass modules, Graph & SharePoint access and Heft-based tooling.

## Additional Prompt Examples
- Scaffold a new SPFx React web part using PnPjs v4, strict TypeScript, localization with `loc` and `.module.scss` styling.

- Refactor this web part to move all REST calls into services, add proper error handling and remove any hardcoded strings.

- Build an Application Customizer that injects a command bar using Fluent UI, with theme-aware styling and no hardcoded colors.

- Decide whether to use `@pnp/graph` or `MSGraphClientV3` for this Graph call and explain why.

- Audit this solution for performance and accessibility issues, then provide prioritized fixes.

- Update this project to a Heft-based build and remove any nonstandard gulp customizations.

- Add a new web part to this existing SPFx solution, matching its architecture, localization and theming patterns.

- Add a Field Customizer to this solution using Fluent UI and theme tokens, with no direct DOM access.

- Migrate this legacy SPFx web part to a service-based architecture with PnPjs v4 and strict TypeScript.

## Non-Goals
- Classic (non-SPFx) SharePoint customizations
- Backend-only implementations
- Over-optimization at the expense of readability
