SharePoint Framework solutions often remain in production for years, while their underlying dependencies continue to evolve. A major dependency change can therefore become a significant migration task, especially when a project moves to a newer SPFx release and React version.
SPFx 1.24 introduces a React 18-based development environment, which means applications built with older React assumptions should be reviewed before migration. The migration is not simply a matter of changing a package version. React rendering behavior, dependency compatibility, TypeScript configuration, testing, and third-party components all need to be considered.
This article presents a practical approach for migrating an existing SharePoint Framework solution to SPFx 1.24 while minimizing unnecessary changes and reducing the risk of runtime problems.
What Changes When Moving to React 18?
React 18 introduced changes to the rendering API and added capabilities such as concurrent rendering and automatic batching.
One of the most visible changes is the move from the older rendering API:
import * as ReactDOM from 'react-dom';
ReactDOM.render(
<App />,
document.getElementById('root')
);to the React 18 root API:
import { createRoot } from 'react-dom/client';
const container = document.getElementById('root');
if (container) {
const root = createRoot(container);
root.render(<App />);
}However, an SPFx application should not blindly replace every rendering call with createRoot. The SPFx component lifecycle and framework-generated code should be treated separately from application-specific React code.
The first migration task should therefore be identifying which parts of the solution are controlled by SPFx and which parts are owned by your application.
Why SPFx Migration Requires Careful Planning
An SPFx solution is more than a React application.
A typical project can contain:
SPFx Solution
│
├── Web Parts
├── Extensions
├── React Components
├── TypeScript
├── Fluent UI
├── SharePoint APIs
├── Third-Party Packages
├── Build Configuration
└── TestsChanging the SPFx version can affect several of these areas simultaneously.
A successful migration should therefore follow a controlled process rather than updating packages until the project builds.
Step 1: Create a Migration Branch
Before changing dependencies, create a dedicated Git branch.
git checkout -b spfx-1-24-migrationCommit the existing working state before starting:
git add .
git commit -m "chore: prepare solution for SPFx 1.24 migration"This gives you a known rollback point if the migration introduces unexpected problems.
For larger projects, migrating one web part or extension at a time can also reduce troubleshooting complexity.
Step 2: Record the Existing Environment
Before upgrading, document the current versions.
Check the SPFx project configuration and package manifest.
For example:
{
"dependencies": {
"@microsoft/sp-core-library": "...",
"@microsoft/sp-webpart-base": "...",
"react": "...",
"react-dom": "..."
}
}Also inspect the development environment:
node --version
npm --versionRecord these values before making changes.
This becomes useful when diagnosing whether a problem is caused by SPFx, Node.js, React, TypeScript, or another dependency.
Step 3: Review package.json
The package.json file is one of the most important files during an SPFx migration.
Look for:
SPFx packages
React
React DOM
TypeScript
Fluent UI packages
Testing libraries
Build-related dependencies
Third-party React components
For example:
{
"dependencies": {
"@microsoft/sp-core-library": "~1.24.0",
"@microsoft/sp-webpart-base": "~1.24.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}Do not assume that every third-party package supporting React 17 will automatically work correctly with React 18.
Step 4: Update SPFx Dependencies Together
Avoid upgrading only one Microsoft SPFx package.
A project can contain several packages that must remain aligned.
Typical packages include:
@microsoft/sp-core-library
@microsoft/sp-webpart-base
@microsoft/sp-property-pane
@microsoft/sp-http
@microsoft/sp-lodash-subsetIf these packages are on different SPFx versions, you can encounter confusing build or runtime problems.
After updating the package versions, perform a clean dependency installation.
For example:
rm -rf node_modules
rm -f package-lock.json
npm installOn Windows PowerShell, the equivalent cleanup can be performed with:
Remove-Item -Recurse -Force node_modules
Remove-Item -Force package-lock.json
npm installDeleting the lock file should be done deliberately because it causes dependencies to be resolved again. In controlled enterprise environments, preserve the lock file when your migration strategy requires reproducible dependency resolution.
Step 5: Check React Rendering Code
Search the solution for older React rendering APIs.
Look for:
ReactDOM.render(...)and:
ReactDOM.unmountComponentAtNode(...)If these APIs are used by your own application code, evaluate whether they should be migrated to the React 18 root API.
A React 18-compatible pattern is:
import { createRoot, Root } from 'react-dom/client';
let root: Root | undefined;
export function renderApp(
element: HTMLElement,
component: React.ReactNode
): void {
root ??= createRoot(element);
root.render(component);
}For cleanup:
root?.unmount();
root = undefined;The important point is to understand the lifecycle before changing rendering code.
Step 6: Review useEffect Behavior
React 18 development behavior can expose assumptions in existing useEffect implementations.
Consider:
React.useEffect(() => {
loadData();
}, []);If loadData() performs an operation that is not safe to execute more than once, the component should be designed accordingly.
For example:
React.useEffect(() => {
let cancelled = false;
async function load() {
const result = await loadData();
if (!cancelled) {
setData(result);
}
}
load();
return () => {
cancelled = true;
};
}, []);This pattern prevents stale asynchronous work from updating component state after the component has been cleaned up.
The exact implementation depends on how the API request and cancellation are handled.
Step 7: Check Third-Party React Components
Third-party components are one of the most common areas to investigate during a React migration.
For every important dependency, check:
| Dependency type | What to verify |
|---|---|
| UI component | React 18 compatibility |
| Date picker | Rendering and event behavior |
| Grid/table | Lifecycle compatibility |
| Rich text editor | React peer dependencies |
| Charts | Browser and React compatibility |
| Testing library | React 18 support |
| Utility package | Peer dependency warnings |
Run:
npm installand carefully inspect warnings.
A peer-dependency warning should not automatically be ignored.
For example:
npm WARN ERESOLVE overriding peer dependencycan indicate that two packages expect incompatible versions.
Step 8: Check Fluent UI Dependencies
Many SharePoint solutions use Fluent UI components.
Review your existing imports and package versions carefully.
For example:
import {
PrimaryButton,
TextField
} from '@fluentui/react';Avoid upgrading UI libraries at the same time unless there is a clear requirement.
This is an important migration principle:
Change the minimum number of variables necessary to complete the migration.
If you simultaneously upgrade SPFx, React, Fluent UI, TypeScript, and every third-party component, identifying the cause of a regression becomes significantly harder.
Step 9: Build the Solution
After dependency changes, run the standard build process.
gulp clean
gulp buildThen package the solution:
gulp bundle --ship
gulp package-solution --shipThe exact build commands should match the project's existing SPFx toolchain configuration.
Do not proceed directly to production deployment after a successful build.
A successful compilation only proves that the source can be processed. It does not prove that the application behaves correctly in SharePoint.
Step 10: Test the Web Part in SharePoint
Test the migrated solution in an environment that resembles production.
Check:
Web part loading
Property pane behavior
SharePoint API calls
Authentication
Lists and libraries
User permissions
React state updates
Event handlers
Dialogs and overlays
Responsive layouts
Error handling
Extension behavior
Pay particular attention to components that depend on asynchronous data.
For example:
const [items, setItems] = React.useState<Item[]>([]);
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
let active = true;
async function loadItems() {
try {
const result = await service.getItems();
if (active) {
setItems(result);
}
}
finally {
if (active) {
setLoading(false);
}
}
}
loadItems();
return () => {
active = false;
};
}, []);This makes component lifecycle behavior explicit.
Migration Comparison
| Area | Older SPFx Solution | SPFx 1.24 Migration |
|---|---|---|
| React | Older React assumptions may exist | React 18 compatibility required |
| Rendering | Legacy APIs may exist | Review root-based rendering |
| Dependencies | Existing package versions | Align with SPFx requirements |
| Third-party components | Existing peer dependencies | Verify React 18 support |
| Testing | Existing test assumptions | Retest component lifecycle |
| Build | Existing toolchain | Validate supported environment |
| Deployment | Existing package | Validate in SharePoint environment |
Common Migration Problems
Peer Dependency Errors
You may encounter dependency conflicts such as:
Could not resolve dependencyFirst identify which package requires the conflicting version.
Do not immediately use dependency override options to suppress the warning.
Determine whether the package actually supports the React and SPFx versions used by the application.
Web Part Builds but Fails at Runtime
This usually indicates a runtime compatibility problem rather than a TypeScript problem.
Inspect:
Browser console errors
Network requests
Package versions
React component lifecycle
Third-party libraries
Component Behaves Differently in Development
React development behavior can expose unsafe assumptions around effects and component lifecycle.
Check whether effects perform operations that are expected to run only once and whether cleanup is implemented correctly.
SharePoint API Calls Fail
A React migration should not normally change SharePoint permissions.
If API calls begin failing after the migration, compare:
Request URLs
Authentication context
Permissions
API response codes
Environment configuration
Avoid assuming every post-migration problem is caused by React.
Best Practices for a Safe Migration
Keep the Migration Incremental
Upgrade the framework first, then address application compatibility issues.
Use Version Control Aggressively
Make small commits:
chore: update SPFx dependencies
fix: migrate custom React rendering
fix: update third-party component
test: update component testsThis makes regression analysis much easier.
Test Critical User Journeys
Do not test only whether the web part opens.
Test the workflows users actually depend on.
For example:
Open Web Part
↓
Load SharePoint Data
↓
Filter Data
↓
Edit Item
↓
Save Item
↓
RefreshAvoid Unnecessary Dependency Upgrades
A migration should not become an opportunity to upgrade every package in the project.
Keep unrelated changes separate.
Validate Production-Like Permissions
A component that works for a SharePoint administrator may still fail for a standard user.
Test with realistic permission levels.
Advantages and Disadvantages
Advantages
Provides a supported path toward newer React capabilities
Allows existing SPFx solutions to adopt the newer framework environment
Improves compatibility with modern React libraries when supported
Creates an opportunity to remove outdated dependencies
Encourages cleaner component lifecycle handling
Makes long-term maintenance easier
Disadvantages
Existing third-party components may require upgrades or replacement
React lifecycle assumptions can expose existing bugs
Dependency conflicts can make migration time-consuming
Testing requirements increase
Build and deployment environments may need adjustment
Large SPFx solutions can require significant regression testing
Troubleshooting Checklist
If an SPFx 1.24 migration fails, work through the problem systematically:
Confirm the supported Node.js and package-manager environment.
Check that SPFx packages use compatible versions.
Inspect React and React DOM versions.
Review peer-dependency warnings.
Remove stale dependencies and reinstall when appropriate.
Run a clean build.
Search for legacy React rendering APIs.
Test third-party components individually.
Check browser console errors.
Test SharePoint API calls.
Validate user permissions.
Test the packaged solution in a production-like environment.
Avoid changing multiple unrelated dependencies while troubleshooting. Isolating one change at a time makes the root cause much easier to identify.
Conclusion
Migrating an existing SharePoint Framework solution to SPFx 1.24 and React 18 should be treated as a compatibility project rather than a simple package upgrade.
The safest approach is to first understand the existing dependency tree, create a rollback point, align SPFx packages, review React-specific code, verify third-party dependencies, and then perform functional testing in a realistic SharePoint environment.
Most importantly, do not assume that a successful gulp build means the migration is complete. React rendering, asynchronous operations, SharePoint APIs, permissions, UI components, and third-party libraries all need to be validated.
A controlled, incremental migration keeps the change set manageable and makes it much easier to identify and resolve compatibility problems before the updated solution reaches production.

Comments
Join the conversation! Your thoughts help the community grow.