← all posts
SPFxSharePointReactTypeScript

SPFx in 2025: Patterns That Actually Work in Production

20 January 2025·3 min read·Debashis Mandal

SharePoint Framework has matured considerably since its 2017 debut. The tooling is better, the React version is modern, and the deployment story with App Catalog is reliable. But the patterns you see in Microsoft's own documentation don't always hold up at enterprise scale. Here's what I've learned shipping SPFx solutions across 20+ enterprise tenants.

State Management: Skip Redux

The SPFx documentation era of 2018–2020 is littered with Redux examples. For a web part that renders a list of items and maybe a form, Redux is serious overkill. The context overhead alone will cost you more debugging time than it saves.

In practice, I use:

  • React Context for cross-component theme/config sharing
  • useReducer for forms with non-trivial validation
  • SWR or React Query (bundled carefully) for data fetching with caching

The key constraint: your bundle must stay under ~500KB compressed. Every dependency counts.

Graph API: Always Use the SPFx Context Token

import { spfi, SPFx } from '@pnp/sp';

const sp = spfi().using(SPFx(this.context));

Never roll your own Graph token acquisition inside SPFx. The this.context.msGraphClientFactory (or PnPjs wrapping it) handles token refresh, consent, and the SPO app identity correctly. Custom MSAL flows inside web parts have bitten me twice with subtle token caching issues in multi-tab scenarios.

Property Pane: Validate Before Save

The default SPFx property pane validates nothing. Users will type invalid URLs, leave required fields empty, and then file a support ticket when the web part breaks. Add validation:

protected onPropertyPaneFieldChanged(propertyPath: string, oldValue: unknown, newValue: unknown): void {
  if (propertyPath === 'listUrl') {
    this.properties.listUrl = newValue as string;
    this._validateListUrl(newValue as string);
  }
  super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
}

Return a validation error string from your validator and SPFx will surface it inline — no custom UI needed.

Bundling: Externals Are Your Friend

The single biggest SPFx bundle mistake is including React and ReactDOM in your bundle. They're already available globally on every modern SharePoint page:

// config/config.json
{
  "externals": {
    "react": "React",
    "react-dom": "ReactDOM",
    "@microsoft/sp-lodash-subset": "@microsoft/sp-lodash-subset"
  }
}

This alone can cut your bundle from 800KB to under 200KB. Verify with gulp bundle --ship --analyze.

Deployment: Tenant-Wide vs Site Collection App Catalog

Default guidance is to push everything to the tenant app catalog. In practice, that means IT governance approval for every deployment. For solutions that serve a single department, use the site collection app catalog — it lets the site owner (not tenant admin) manage deployments. Faster iteration, fewer bottlenecks.

Error Boundaries Are Non-Negotiable

A crashed web part should show a graceful message, not break the entire SharePoint page. Wrap every top-level component:

class WebPartErrorBoundary extends React.Component<...> {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <div className={styles.errorState}>Something went wrong. Contact IT support.</div>;
    }
    return this.props.children;
  }
}

The Pattern I Avoid: Page Context Overuse

this.context.pageContext is tempting because it gives you the current user, site URL, and list context without a Graph call. But components that consume it directly become untestable and tightly coupled to the SPFx runtime. Extract what you need in the web part class and pass it as props — your components stay pure React, and unit tests don't need a SharePoint environment.

Closing Thought

SPFx is a mature platform now. The teams that struggle with it are usually fighting the framework — trying to use it as a generic React host rather than working with SharePoint's identity, permissions, and deployment model. Lean into PnPjs, use the Graph client factory, and keep your bundles honest.

← back to all posts