Modernize SharePoint Script Editor & Custom JavaScript with SPFx
Assess legacy Script Editor and custom JavaScript, choose between native SharePoint, JSON formatting, Power Platform, or SPFx — and modernize without porting debt.
- Published
- Reading time
- 18 min read
What you’ll learn
- Where legacy JavaScript may exist
- Inventory each customization
- Classify what the script actually does
- First decision: is it still required
- Modernization decision tree
On this page (42 sections)
Direct answer: Legacy SharePoint environments run business functionality through Script Editor Web Parts, Content Editor Web Parts, custom JavaScript files, and page-level script injection. The wrong modernization question is "how do we move this JavaScript to SPFx?" The better question is "what business requirement does this JavaScript satisfy, and what is the simplest supported modern architecture for that requirement?"
Do not port legacy JavaScript line-by-line before deciding whether the customization is still needed. Most scripts implement presentation, process, or form needs that modern capabilities already cover; SPFx is reserved for what remains justifiably custom.
- Legacy JavaScript — as found, wherever it loads from.
- Understand requirement — the business outcome, not the code.
- Still required? — undefended customizations retire here.
- Supported native capability? — native first, always.
- JSON, Power Platform, or SPFx — simplest fitting architecture wins.
- Modern SharePoint — rebuilt, secured, and owned.
For the surrounding strategy, use Classic SharePoint to Modern SharePoint, Migration Assessment & Readiness, and the SPFx hub.
Where legacy JavaScript may exist
Search beyond visible Script Editor Web Parts. Customizations load globally and indirectly: Script Editor and Content Editor Web Parts, classic and publishing pages, custom master pages, page layouts, referenced JavaScript files, Site Assets, Style Library, custom actions, JSLink, User Custom Actions, third-party scripts, embedded HTML, legacy add-ins, and custom web parts. Anything missed here resurfaces as a cutover surprise.
Inventory each customization
Capture location, page and site, script file, owner, business owner, business purpose, usage, dependencies, SharePoint APIs used, external APIs, libraries and frameworks, authentication, permissions, hard-coded URLs, DOM dependencies, data sources, business criticality, and target requirement:
| Customization | Business requirement | Dependencies | Usage | Modernization decision |
|---|---|---|---|---|
| Location and script, as found | Outcome the business defends | APIs, libraries, data, auth | Active, rare, or abandoned | Recorded with an owner |
No production data belongs in this table — it is a working template, not a sample report.
Classify what the script actually does
Sort each script into functional categories before choosing architecture: UI formatting (DOM changes, colors, icons, conditional presentation, list rendering), data access (lists, libraries, files, users, sites), business logic (validation, calculations, rules), automation (item creation and updates, notifications, workflow-like behavior), navigation (headers, menus, links), integration (external APIs, line-of-business systems), identity (current user, groups, permissions), and search (custom queries and results).
- Legacy script — classified by function, not by file name.
- Classify function — formatting, data, logic, automation, integration.
- Select modern architecture — the simplest supported fit for that function.
First decision: is it still required
Usage and business ownership decide. An unused customization is retired regardless of how clever its code is; a used one proceeds only with a business requirement its owner defends. Never modernize abandoned customizations simply because they exist — every ported script carries migration, testing, and maintenance cost.
- Legacy customization — with usage evidence.
- Used? — no goes to retire.
- Business requirement still valid? — no goes to retire.
- Modern solution required — proceeds down the decision tree.
Modernization decision tree
The central routing. The simplest supported architecture that satisfies the requirement is usually preferable to rebuilding custom code — and complex solutions may combine technologies, so treat the tree as guidance, not law:
- Business requirement — confirmed and owned.
- Native modern SharePoint capability? — yes goes native.
- Presentation or formatting only? — yes evaluates JSON formatting where appropriate.
- Workflow or automation? — yes evaluates Power Automate where appropriate.
- Business form or application? — yes evaluates Power Apps where appropriate.
- Custom SharePoint or Microsoft 365 client experience? — yes goes to SPFx.
- External or server-side requirement? — evaluate secure API, Azure, or backend architecture.
Native modern SharePoint first
Before recommending SPFx, check modern web parts, modern pages, lists, libraries, views, formatting, navigation, search, forms, and Microsoft 365 integrations. Never rebuild functionality that a supported native feature already handles adequately — the cheapest custom code is the code never written.
JSON formatting
Legacy JavaScript used only for presentation — column formatting, view formatting, conditional styling, icons, visual indicators, supported actions — is the prime JSON formatting candidate. Route presentation-only scripts to an evaluation, and remember the boundary: JSON formatting is declarative, not arbitrary JavaScript execution, and must never be presented as a replacement for complex application logic.
- Legacy JavaScript — presentation behavior isolated.
- Only changes list or view presentation? — yes evaluates JSON formatting.
Power Automate for scripted processes
Scripts performing notifications, approvals, document processes, item updates, scheduled processes, or cross-service automation are often better redesigned as Power Automate flows. Never move browser-side code into flows mechanically — redesign the process around business requirements first. Patterns: approval workflows, SharePoint trigger patterns, and the Power Automate hub.
- Legacy JavaScript automation — process extracted.
- Business process — redesigned, not transcribed.
- Power Automate? — evaluated with triggers, permissions, and error handling re-proven.
Power Apps for scripted forms and apps
Business forms, data-entry applications, multi-screen experiences, mobile business applications, and Dataverse-backed applications may fit Power Apps better than SPFx. Power Apps and SPFx solve different classes of problems — the requirement decides, per form and per application. Direction: Dataverse vs SharePoint data sources and Power Apps vs SPFx.
- Legacy custom form or application — requirements extracted.
- Business requirements — screens, data, rules, devices.
- Power Apps? — evaluated where the class fits.
When SPFx is appropriate
SPFx earns its place for custom SharePoint web parts, complex client-side UI, React components, custom list and library experiences, application extensions, command sets, field customizers, Microsoft Graph, SharePoint APIs, external APIs, enterprise integrations, and Teams integration where applicable. Depth: Explore SharePoint Framework (SPFx), what SPFx is, how extensions behave.
- Modern SharePoint — the host and its supported surfaces.
- SPFx — web parts and extensions as the custom layer.
- React, PnPjs, Microsoft Graph, SharePoint REST, external APIs — composed per requirement below.
Do not copy the script into SPFx
A line-by-line port preserves technical debt, hard-coded URLs, unsupported DOM assumptions, old libraries, weak error handling, security problems, and poor architecture — inside a newer wrapper. Instead: extract requirements from the legacy JavaScript, identify data and API dependencies, separate UI from business logic, design the modern architecture, then implement. The old script is evidence for the design, never the design itself.
DOM manipulation
Scripts querying SharePoint-generated CSS selectors, changing page chrome, hiding platform elements, injecting buttons, moving elements, or modifying menus need special attention: undocumented DOM structures change without notice. Route each case through its requirement — supported extension point or API goes to SPFx or supported architecture; anything else forces a requirement redesign. Never recommend relying on undocumented SharePoint DOM structures in new code.
- DOM manipulation — requirement extracted.
- Supported extension point? — yes goes to SPFx or supported API.
- Otherwise — redesign the requirement.
SPFx web part or extension
Custom content and components placed on pages belong in SPFx web parts; supported application-level extension scenarios belong in Application Customizers where appropriate; custom commands in supported list and library experiences belong in ListView Command Sets; custom field rendering belongs in Field Customizers where appropriate. Verify current SPFx capabilities before committing — extension surfaces evolve.
- Requirement — classified by surface.
- Page component? — web part.
- Application-level extension? — Application Customizer.
- List or library command? — Command Set.
- Field rendering? — Field Customizer.
React modernization
Legacy scripts built from HTML generation, jQuery, event handlers, global variables, and direct DOM manipulation map onto React components with props, state, hooks, Fluent UI where appropriate, reusable services, and TypeScript interfaces. jQuery itself is neither inherently insecure nor prohibited — the migration argument is maintainability and architecture, so evaluate each script on structure rather than on its dependencies:
| Legacy | Modern SPFx |
|---|---|
| jQuery and inline HTML | React components |
| Global variables | TypeScript, props, and state |
| Direct DOM manipulation | Declarative rendering and services |
| Inline everything | Components, services, supported APIs |
Separate UI from data access
Data access scattered through UI components is the signature flaw of ported scripts. Structure modern solutions as React components calling a service layer, which in turn calls PnPjs, Graph, REST, or external APIs. A components, services, models, hooks, and utils layout is one reasonable shape — never a mandatory prescription.
- React components — render props and state.
- Service layer — owns every external call and its errors.
- PnPjs, Graph, REST, or external API — chosen per operation.
SharePoint REST modernization
Legacy calls against _api/web, _api/lists, and _api/web/lists endpoints should be reviewed rather than automatically replaced. Modern approaches include continued SharePoint REST, PnPjs, and Microsoft Graph — chosen by workload and requirement. Never claim Graph replaces all SharePoint REST functionality; list schemas, versioning detail, and fine-grained permission structures often still belong on SharePoint-side APIs.
JSOM modernization
For scripts using legacy SharePoint JavaScript object model APIs, identify the actual operations first — which lists, which items, which user data — then evaluate PnPjs, SharePoint REST, Microsoft Graph, or other supported APIs per operation. No one-to-one JSOM conversion table is published here; operation-level refactoring against current documentation is the method.
- JSOM call — operation identified.
- What data or operation? — stated plainly.
- Supported modern API — selected and verified.
- Refactor — rebuilt with tests, not transliterated.
PnPjs in modernized solutions
PnPjs simplifies supported SharePoint and Microsoft Graph operations — lists, items, libraries, files, users, groups, batching, filtering — behind a fluent, typed API used from the SPFx service layer. Align the PnPjs major version with the SPFx release being targeted and verify current package names and patterns against official PnP documentation before implementing; the example below follows the long-standing service-layer shape.
- SPFx — context and lifecycle.
- Service layer — typed functions per business operation.
- PnPjs — fluent SharePoint and Graph calls.
- SharePoint and Graph — the data behind the UI.
Microsoft Graph in modernized solutions
Graph fits users, groups, Teams, files, and Microsoft 365 services reached from SPFx through the Graph client — under delegated permissions a tenant administrator approves, following least privilege, with 401 and 403 handled distinctly and errors surfaced usefully. Graph never automatically has access to all Microsoft 365 data; every scope is requested, approved, and tested. Concepts: Microsoft Graph for beginners and what Microsoft Graph is.
- SPFx — user context and component lifecycle.
- Graph client — authenticated delegated calls.
- Microsoft Graph — the Microsoft 365 data surface.
- Microsoft 365 — users, groups, files, and services.
SharePoint REST versus PnPjs versus Graph
| Approach | Best fit | Advantages | Considerations |
|---|---|---|---|
| SharePoint REST | SharePoint-specific operations | Precise control, no abstraction | Verbose; manual batching and typing |
| PnPjs | Developer convenience over supported APIs | Fluent, typed, batteries included | Version alignment with the SPFx release |
| Microsoft Graph | Cross-Microsoft 365 data | One surface, delegated permissions | Not every SharePoint operation is exposed |
No universal winner exists. SharePoint-specific operations lean toward REST or PnPjs; cross-workload data leans toward Graph; convenience leans toward PnPjs — and the actual operation always decides.
External API integration
Inventory endpoint, authentication, credentials, CORS behavior, request and response shapes, business dependency, and owner for every external call. The standing rule: never place client secrets, passwords, or privileged credentials in SPFx or browser code. Route through Microsoft Entra authentication to a secure API and business system — adding a secure backend or API mediation layer wherever the external system cannot authenticate the user directly. Browser code cannot securely hide a secret, so any design requiring hidden browser secrets is redesigned, not implemented.
- SPFx — user-context request.
- Microsoft Entra authentication — identity without embedded secrets.
- Secure API — mediated, authorized endpoint.
- Business system — data returned under proper authorization.
Authentication review
Legacy assumptions — embedded credentials, basic authentication, hard-coded tokens, legacy cookies, old OAuth approaches, cross-domain shortcuts — are inventoried as defects and replaced with appropriate Microsoft Entra authentication and supported OAuth patterns. No insecure examples are published here; describe the target pattern and verify it against current documentation during implementation.
API permissions
Two different permissions govern every protected call and must never be confused: SharePoint user permissions decide what the current user may touch; Microsoft Graph and API permission grants decide what the solution may call, approved by administrators. Request least privilege, test both layers independently, and handle denial distinctly:
- User — authenticated identity with SharePoint access.
- SPFx — solution with approved API permissions.
- SharePoint access plus API permission — both required.
- Graph or API — succeeds only when both hold.
Hard-coded URLs
Site, tenant, list, API, image, script, and environment-specific IDs baked into scripts are a migration defect class: source URLs keep working just long enough to hide breakage, then fail after cutover. Extract every hard-coded value into environment configuration — SPFx properties, configuration, or another appropriate mechanism — and re-test against target URLs. Inventory hard-coded values during assessment, not during cutover triage.
- Hard-coded configuration — inventoried per script.
- Extract — into configuration, never inline constants.
- Environment configuration — per source and target.
- SPFx properties, config, or appropriate mechanism — resolved at runtime.
Third-party libraries
For jQuery, Bootstrap, older frameworks, utility libraries, and vendor SDKs, ask whether each is still required, maintained, compatible, replaceable with a modern alternative, already covered better by SPFx patterns, and what it costs the bundle and performance. Never upgrade dependencies blindly — compatibility-test against the SPFx release being targeted, and remove what the redesign no longer needs.
TypeScript
Moving surviving logic into TypeScript buys types, interfaces, maintainability, IDE tooling, refactoring confidence, and compile-time checking over legacy JavaScript. It does not eliminate runtime errors — network, permission, and data failures still need runtime handling, which the error-handling section below covers.
Error handling
Legacy scripts often fail silently. Modernization defines loading, empty, API-failure, authentication-failure, permission-failure, and network-failure states, plus logging and user-facing messages that never expose sensitive internals:
- API request — issued through the service layer.
- Success — renders.
- Failure — classified by cause.
- Log appropriately — diagnostics without tokens or personal data.
- Useful user message — actionable, never a stack trace.
Security review
Performance
Assess multiple and repeated API calls, large scripts, blocking libraries, excessive DOM manipulation, full-list loading, unnecessary fields, and duplicate dependencies. Modernize with selected fields only, batching where appropriate, caching where appropriate, lazy loading, bundle management, efficient React rendering, and loading states. No performance benchmarks are published here — measure your solution against its own baseline.
Accessibility
Preserve or improve keyboard access, focus management, labels, semantic elements, ARIA where required, contrast, and screen reader behavior — using accessible Fluent UI or native components where appropriate. Accessibility is designed from the first sprint, never bolted on after development.
Example 1: simple formatting
Illustrative scenario, not a customer case. A Script Editor recolors list items by status. Assessment finds a purely visual requirement, so JSON formatting implements it with no custom code at all — SPFx is unnecessary, and the script retires with its page.
Example 2: business automation
Illustrative scenario, not a customer case. JavaScript creates and updates SharePoint items and sends notifications on user actions. Assessment separates the UI need from the automation need, landing on modern SharePoint or Power Apps for interaction with Power Automate behind it for the process — with architecture depending on the confirmed requirements.
Example 3: complex custom UI
Illustrative scenario, not a customer case. A Script Editor dashboard queries multiple SharePoint lists. The modern shape is SharePoint Online with an SPFx web part in React reading lists through PnPjs — adding Microsoft Graph only if cross-Microsoft 365 data is actually required.
Example 4: external business system
Illustrative scenario, not a customer case. JavaScript calls an external business API with insecure embedded credentials. The modern direction is SPFx with Microsoft Entra authentication through a secure API layer to the business system — because secrets cannot safely hide in browser code, the credential handling is redesigned first.
Code example: PnPjs service in SPFx
A minimal, verified-shape service that reads SharePoint list items through PnPjs inside an SPFx solution. Align the PnPjs major version with the targeted SPFx release and verify current package names against official PnP documentation:
import { spfi, SPFx } from "@pnp/sp";
import "@pnp/sp/webs";
import "@pnp/sp/lists";
import "@pnp/sp/items";
import type { WebPartContext } from "@microsoft/sp-webpart-base";
export interface Announcement {
id: number;
title: string;
}
// Reads items through the current user's permissions.
// Call this from a React component; keep all data access here,
// out of the UI layer.
export async function getAnnouncements(
context: WebPartContext,
listTitle: string
): Promise<Announcement[]> {
const sp = spfi().using(SPFx(context));
try {
const items = await sp.web.lists
.getByTitle(listTitle)
.items.select("Id", "Title")();
return items.map((item) => ({ id: item.Id, title: item.Title }));
} catch (error) {
throw new Error(
`Could not read list "${listTitle}". Confirm the list exists ` +
`and the current user can access it.`
);
}
}
The service initializes PnPjs with the web part context (so calls run as the current user), selects only the fields the UI needs, maps results to a typed model, and converts failures into an actionable message without leaking internals. UI components call this function and manage loading, empty, and error states.
Before and after architecture
Example architecture, not mandatory for every customization. Before: a classic SharePoint page with a Script Editor running jQuery and custom JavaScript against SharePoint REST with hard-coded configuration. After: modern SharePoint with an SPFx web part in React, a service layer, and PnPjs, Graph, or REST reaching SharePoint and Microsoft 365 with environment configuration.
- Before — page, Script Editor, jQuery, REST, hard-coded values.
- After — modern page, SPFx web part, React, service layer, configured data access.
Migration and modernization connection
Critical Script Editor dependencies must never surface during cutover — they belong in migration assessment inventory, routed through business-requirement analysis into retire, replace, modernize, or rebuild on modern SharePoint. Depth: Migration Assessment & Readiness and the migration hub.
- SharePoint migration — content program with a customization inventory.
- Customization inventory — every script found and classified.
- Script Editor found — requirement analysis, not automatic porting.
- Business requirement analysis — the decision tree in this guide.
- Retire, replace, modernize, or rebuild — onto modern SharePoint.
Continue learning SPFx
Explore SPFx web parts, React, PnPjs, Microsoft Graph, extensions, APIs, deployment, and troubleshooting on the hub: Explore SPFx — work the full lifecycle in the SPFx complete guide, or build the UI layer with the SPFx React guide. For issue patterns in built solutions, see SPFx troubleshooting.
Modernization checklist
Discover: script locations, business owners, usage, dependencies, APIs, libraries identified; authentication reviewed; hard-coded URLs identified.
Assess: requirement validity, native capability, JSON formatting, Power Automate, Power Apps, and SPFx evaluated per script.
Design: UI, data-access, authentication, permissions, environment configuration, and error handling defined.
Build: modern implementation completed; dependencies updated; security, accessibility, and performance reviewed.
Validate: functional behavior, permissions, API integration, and migration environment tested; business users validate critical scenarios.
Common modernization mistakes
| Mistake | Corrective direction |
|---|---|
| Copying JavaScript directly into SPFx | Extract requirements and redesign; the old script is evidence, not design. |
| Rebuilding unused functionality | Retire undefended scripts before any build starts. |
| Using SPFx when native features suffice | Evaluate native, JSON formatting, and platform options first. |
| DOM manipulation against unsupported selectors | Route through supported extension points or redesign the requirement. |
| Leaving hard-coded URLs | Extract to environment configuration and re-test on target URLs. |
| Putting secrets in browser code | Move secrets to a secured backend; redesign the integration. |
| Requesting excessive Graph permissions | Request least privilege and justify each scope. |
| Ignoring error handling | Define loading, empty, failure, and logging behavior up front. |
| Ignoring accessibility | Design keyboard, focus, and screen-reader behavior from the first sprint. |
| Ignoring migration environment differences | Test against target URLs, permissions, and configuration. |
| Skipping business validation | Gate completion on owner testing of real scenarios. |
| Using outdated libraries without review | Compatibility-test dependencies against the targeted SPFx release. |
Troubleshooting modernized solutions
| Issue | Check | Direction |
|---|---|---|
| SPFx web part cannot access data | User permissions, API approvals, context initialization | Verify effective access and permission grants separately |
| Graph returns 401 | Authentication, consent, token acquisition | Fix sign-in and consent before code changes |
| Graph returns 403 | Requested versus approved scopes | Request least privilege and obtain approval |
| External API returns CORS error | API allowed origins, mediation layer | Fix server-side or add mediated backend |
| PnPjs request fails | Context init, select and expand clauses, batching | Reproduce as raw REST to isolate the layer |
| User has insufficient SharePoint permission | Effective access as that user | Correct access or handle denial gracefully |
| Works in development, not production | Environment config, URLs, approvals, versions | Diff environments systematically |
| Hard-coded source URL remains | Configuration extraction coverage | Move to config and re-test on target |
| API permission not approved | Admin center request state | Obtain tenant administrator approval |
| Package deployed but web part unavailable | Catalog, deployment scope, app installation | Complete the deployment chain end to end |
| Environment configuration incorrect | Per-environment values and properties | Externalize and verify per environment |
Deeper issue patterns: SPFx troubleshooting. Missing fix-level guides are tracked as future content rather than thin pages.
Modernizing legacy SharePoint JavaScript?
If you are assessing Script Editor solutions, custom JavaScript, legacy APIs, or complex SPFx modernization requirements, share the environment and technical challenge with nextM365: Discuss Your Modernization.
Continue with Explore SPFx, Explore SharePoint Migration, the classic to modern guide, assessment guide, and migration validation.
Related resources
Topics covered
Architecture · Governance · Security
Frequently asked questions
What replaces the Script Editor Web Part in Modern SharePoint?
Whatever the script does decides: retire it, use a native modern capability or JSON formatting for presentation, Power Platform for process needs, or SPFx where custom client-side code remains justified. No single automatic replacement exists.
Should Script Editor JavaScript be rewritten in SPFx?
Only after confirming the requirement survives and no simpler supported architecture fits. Extract requirements, data dependencies, and business logic first — then design the modern solution instead of porting the old script line by line.
Can SPFx run custom JavaScript?
SPFx solutions are TypeScript and React applications, so equivalent logic can be rebuilt properly inside them. But legacy scripts written against classic page DOM and old APIs need redesign, not relocation.
Should jQuery code be migrated to SPFx?
Migrate the requirement, not the library. jQuery is not inherently prohibited, but modern SPFx work favors React components, services, and TypeScript for maintainability — evaluate each script on architecture, not on its dependencies.
Can JSON formatting replace Script Editor?
For presentation-only needs such as column colors, icons, and conditional styling, usually yes. JSON formatting is declarative and cannot reproduce application logic, data writes, or integrations.
When should Power Automate replace custom JavaScript?
When the script performs process automation — notifications, approvals, item updates, scheduled work — redesigned around business requirements. Browser-side code is never moved into flows mechanically.
When should Power Apps be used instead of SPFx?
For business forms, data-entry applications, multi-screen and mobile experiences, and Dataverse-backed applications. Power Apps and SPFx solve different problem classes; the requirement decides.
Can SPFx call SharePoint REST APIs?
Yes — directly, through PnPjs, or through Microsoft Graph depending on the workload. Review existing REST logic first and choose the API that fits each operation rather than defaulting to one everywhere.
Can SPFx use Microsoft Graph?
Yes, through the Graph client with delegated permissions that a tenant administrator approves. Graph never has automatic access to everything — request least-privilege scopes and handle 401 and 403 distinctly.
Can SPFx call external APIs?
Yes, through Microsoft Entra authentication against secured endpoints, with a backend mediation layer wherever the API cannot authenticate the user directly.
Where should API secrets be stored when using SPFx?
Never in SPFx browser code — bundles are downloadable by design. Secrets belong in a secured backend or managed-identity flow that the solution calls.
Sources
Have a Microsoft 365 topic idea?
Share article suggestions, community session ideas, corrections, or real-world scenarios for future nextM365 learning notes.
Keep learning Microsoft 365
Explore more practical tutorials for SharePoint, Power Platform, Copilot Studio, migration, automation, governance, and security.
Continue learning
Related tutorials
Related questions
Related comparisons