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 |
Join the conversation! Your thoughts help the community grow.