Skip to content

Migration

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.

Suresh Girinathuni
Published
Reading time
18 min read
Script Editor to SPFx illustration showing legacy script assessment leading to a modern SPFx service and UI

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

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.

  1. Legacy JavaScript — as found, wherever it loads from.
  2. Understand requirement — the business outcome, not the code.
  3. Still required? — undefended customizations retire here.
  4. Supported native capability? — native first, always.
  5. JSON, Power Platform, or SPFx — simplest fitting architecture wins.
  6. 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:

CustomizationBusiness requirementDependenciesUsageModernization decision
Location and script, as foundOutcome the business defendsAPIs, libraries, data, authActive, rare, or abandonedRecorded 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).

  1. Legacy script — classified by function, not by file name.
  2. Classify function — formatting, data, logic, automation, integration.
  3. 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.

  1. Legacy customization — with usage evidence.
  2. Used? — no goes to retire.
  3. Business requirement still valid? — no goes to retire.
  4. 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:

  1. Business requirement — confirmed and owned.
  2. Native modern SharePoint capability? — yes goes native.
  3. Presentation or formatting only? — yes evaluates JSON formatting where appropriate.
  4. Workflow or automation? — yes evaluates Power Automate where appropriate.
  5. Business form or application? — yes evaluates Power Apps where appropriate.
  6. Custom SharePoint or Microsoft 365 client experience? — yes goes to SPFx.
  7. 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.

  1. Legacy JavaScript — presentation behavior isolated.
  2. 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.

  1. Legacy JavaScript automation — process extracted.
  2. Business process — redesigned, not transcribed.
  3. 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.

  1. Legacy custom form or application — requirements extracted.
  2. Business requirements — screens, data, rules, devices.
  3. 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.

  1. Modern SharePoint — the host and its supported surfaces.
  2. SPFx — web parts and extensions as the custom layer.
  3. 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.

  1. DOM manipulation — requirement extracted.
  2. Supported extension point? — yes goes to SPFx or supported API.
  3. 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.

  1. Requirement — classified by surface.
  2. Page component? — web part.
  3. Application-level extension? — Application Customizer.
  4. List or library command? — Command Set.
  5. 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:

LegacyModern SPFx
jQuery and inline HTMLReact components
Global variablesTypeScript, props, and state
Direct DOM manipulationDeclarative rendering and services
Inline everythingComponents, 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.

  1. React components — render props and state.
  2. Service layer — owns every external call and its errors.
  3. 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.

  1. JSOM call — operation identified.
  2. What data or operation? — stated plainly.
  3. Supported modern API — selected and verified.
  4. 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.

  1. SPFx — context and lifecycle.
  2. Service layer — typed functions per business operation.
  3. PnPjs — fluent SharePoint and Graph calls.
  4. 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.

  1. SPFx — user context and component lifecycle.
  2. Graph client — authenticated delegated calls.
  3. Microsoft Graph — the Microsoft 365 data surface.
  4. Microsoft 365 — users, groups, files, and services.

SharePoint REST versus PnPjs versus Graph

ApproachBest fitAdvantagesConsiderations
SharePoint RESTSharePoint-specific operationsPrecise control, no abstractionVerbose; manual batching and typing
PnPjsDeveloper convenience over supported APIsFluent, typed, batteries includedVersion alignment with the SPFx release
Microsoft GraphCross-Microsoft 365 dataOne surface, delegated permissionsNot 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.

  1. SPFx — user-context request.
  2. Microsoft Entra authentication — identity without embedded secrets.
  3. Secure API — mediated, authorized endpoint.
  4. 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:

  1. User — authenticated identity with SharePoint access.
  2. SPFx — solution with approved API permissions.
  3. SharePoint access plus API permission — both required.
  4. 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.

  1. Hard-coded configuration — inventoried per script.
  2. Extract — into configuration, never inline constants.
  3. Environment configuration — per source and target.
  4. 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:

  1. API request — issued through the service layer.
  2. Success — renders.
  3. Failure — classified by cause.
  4. Log appropriately — diagnostics without tokens or personal data.
  5. 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.

  1. Before — page, Script Editor, jQuery, REST, hard-coded values.
  2. 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.

  1. SharePoint migration — content program with a customization inventory.
  2. Customization inventory — every script found and classified.
  3. Script Editor found — requirement analysis, not automatic porting.
  4. Business requirement analysis — the decision tree in this guide.
  5. 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

MistakeCorrective direction
Copying JavaScript directly into SPFxExtract requirements and redesign; the old script is evidence, not design.
Rebuilding unused functionalityRetire undefended scripts before any build starts.
Using SPFx when native features sufficeEvaluate native, JSON formatting, and platform options first.
DOM manipulation against unsupported selectorsRoute through supported extension points or redesign the requirement.
Leaving hard-coded URLsExtract to environment configuration and re-test on target URLs.
Putting secrets in browser codeMove secrets to a secured backend; redesign the integration.
Requesting excessive Graph permissionsRequest least privilege and justify each scope.
Ignoring error handlingDefine loading, empty, failure, and logging behavior up front.
Ignoring accessibilityDesign keyboard, focus, and screen-reader behavior from the first sprint.
Ignoring migration environment differencesTest against target URLs, permissions, and configuration.
Skipping business validationGate completion on owner testing of real scenarios.
Using outdated libraries without reviewCompatibility-test dependencies against the targeted SPFx release.

Troubleshooting modernized solutions

IssueCheckDirection
SPFx web part cannot access dataUser permissions, API approvals, context initializationVerify effective access and permission grants separately
Graph returns 401Authentication, consent, token acquisitionFix sign-in and consent before code changes
Graph returns 403Requested versus approved scopesRequest least privilege and obtain approval
External API returns CORS errorAPI allowed origins, mediation layerFix server-side or add mediated backend
PnPjs request failsContext init, select and expand clauses, batchingReproduce as raw REST to isolate the layer
User has insufficient SharePoint permissionEffective access as that userCorrect access or handle denial gracefully
Works in development, not productionEnvironment config, URLs, approvals, versionsDiff environments systematically
Hard-coded source URL remainsConfiguration extraction coverageMove to config and re-test on target
API permission not approvedAdmin center request stateObtain tenant administrator approval
Package deployed but web part unavailableCatalog, deployment scope, app installationComplete the deployment chain end to end
Environment configuration incorrectPer-environment values and propertiesExternalize 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

Share this:

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.

Connect with me

Keep learning Microsoft 365

Explore more practical tutorials for SharePoint, Power Platform, Copilot Studio, migration, automation, governance, and security.

Continue learning