Skip to content
PYD ENTERPRISES

Migration guide

ASP.NET WebForms to React: pull rules out of ViewState, validators and code-behind

WebForms pages hide state in ViewState, validation in server controls and business logic in click handlers. This guide shows how to separate the three so a React front end and an API can be built against a reviewed contract.

ASP.NET WebFormsReact
DIFFICULTY
Moderate
OUTPUT
OpenAPI / YAML
SYNTAX TRAPS
5 covered
WORKFLOW
6 steps

By Pranav Kulkarni, Head of Language Engineering · Updated 15 Oct 2026

Technical syntax challenges

Where ASP.NET WebForms migrations go wrong.

01

ViewState carries business state

ViewState["Tier"] = GetCustomerTier(...) on first load stores a server-computed value in a hidden field that round-trips through the browser. On postback the handler trusts it. In the target, that value becomes a server-side lookup or a signed token, and the spec must show which handlers depend on it.

02

Validators live in markup

RangeValidator with Type="Currency", RegularExpressionValidator and RequiredFieldValidator are declared in the .aspx file, and CustomValidator adds server-side checks in code-behind. All of them, plus the Page.IsValid gate, form the request schema.

03

Page lifecycle order matters

Page_Init, Page_Load (with or without IsPostBack), control change events, the button click event and PreRender run in a fixed order. Logic that sets a value in Load and reads it in Click is a dependency the React component and the API both need to respect.

04

SQL in markup

SqlDataSource controls put SELECT and UPDATE statements in the .aspx file, bound to GridViews with inline edit. Those are data-access contracts, and sometimes business rules in WHERE clauses, outside any C# file.

05

UpdatePanels and Session

UpdatePanel partial postbacks still run the whole page lifecycle on the server. Session[...] values set on one page and read on another create multi-page flows that need explicit API resources.

Extraction workflow

6 steps from source to signed-off spec.

The sequence a PYD ENTERPRISES Discovery Scan runs for this stack. Every output links back to source file and line range.

  1. 1

    Ingest the web project

    Provide .aspx, .ascx, .master, code-behind, web.config and referenced class libraries, so markup controls and code resolve to one symbol table.

  2. 2

    Bind markup controls to code-behind symbols

    PYD ENTERPRISES links each server control ID to its usages in code-behind and records validators, data sources and event wiring declared in markup.

  3. 3

    Model the page lifecycle per request

    First-load and postback paths are traced through lifecycle events, with ViewState and Session reads and writes recorded as state dependencies.

  4. 4

    Separate UI logic from business rules

    Slicing from each displayed result and each database write isolates calculations from control manipulation; calculations become DMN tables.

  5. 5

    Build request schemas from validators

    Declarative validators, CustomValidator server code and IsValid gates merge into JSON Schema constraints per operation.

  6. 6

    Export OpenAPI and screen-flow specs

    Candidate API operations become OpenAPI 3.1, and Session-based flows become a flow diagram for the React team, all linked to source.

Legacy in, specification out

The source, and what PYD ENTERPRISES extracts from it.

Left: the ASP.NET WebForms construct as it sits in production. Right: the reviewed artifact your React engineers build against. PYD ENTERPRISES produces the spec and the tests; your team writes the service.

LEGACY SOURCE

Quote.aspx.csC#
1// Quote.aspx: <asp:RangeValidator ControlToValidate="txtCoverage"
2// Type="Currency" MinimumValue="10000" MaximumValue="500000" />
3protected void Page_Load(object sender, EventArgs e)
4{
5 if (!IsPostBack)
6 {
7 ViewState["Tier"] = GetCustomerTier(Request.QueryString["cid"]);
8 }
9}
10
11protected void btnQuote_Click(object sender, EventArgs e)
12{
13 if (!Page.IsValid) return;
14 decimal coverage = decimal.Parse(txtCoverage.Text);
15 string tier = (string)ViewState["Tier"];
16 decimal rate = tier == "GOLD" ? 0.m : 0.m;
17 if (chkSmoker.Checked) rate += 0.m;
18 lblPremium.Text = Math.Round(coverage * rate, 2).ToString("C");
19}

EXTRACTED ARTIFACT

quotes.openapi.yamlYAML
1paths:
2 /quotes:
3 post:
4 operationId: createQuote
5 x-pyd-source: Quote.aspx.cs:3-19
6 x-pyd-decision: QUOTE-RATE-02
7 requestBody:
8 content:
9 application/json:
10 schema:
11 type: object
12 required: [customerId, coverage]
13 properties:
14 customerId:
15 type: string
16 description: was query string cid
17 coverage:
18 type: number
19 minimum: 10000
20 maximum: 500000
21 smoker: { type: boolean, default: false }
22 responses:
23 "200":
24 content:
25 application/json:
26 schema:
27 properties:
28 premium:
29 type: number
30 x-pyd-rounding: half-even, 2 dp
31# QUOTE-RATE-02: tier GOLD 0.0042, any other 0.0055;
32# smoker +0.0030. Tier was read from ViewState.

Architectural best practices

Build the React side to the spec, not to the old shape.

  • Never move ViewState-held values into client state in React; resolve them on the server from authoritative data.
  • Make the API schema the source of validation and generate client-side checks from it.
  • Name the rounding of every displayed amount; WebForms code often used Math.Round's default half-even without anyone choosing it.
  • Turn Session-based multi-page flows into explicit draft resources with their own identifiers.
  • Move SQL from SqlDataSource markup into reviewed data-access contracts before rebuilding grids.

In depth

Field notes from real scans.

Two products from one page

A WebForms page becomes two things in the target: a React screen and one or more API operations. Splitting them well requires knowing which code is presentation (show this panel, disable that button) and which is business behavior (the rate, the eligibility check, the write). PYD ENTERPRISES makes that split with program slicing and shows it beside the code-behind lines, so the React and API teams agree on the boundary before either starts.

PYD ENTERPRISES does not generate React components or ASP.NET Core controllers. It produces the OpenAPI contract, the decisions and fixtures, and a screen-flow diagram.

What the example reveals

The quote handler has a rule (rate by tier, smoker surcharge), a validation (coverage 10,000 to 500,000), and two review items. First, the tier is computed from a query string value on first load and then trusted from ViewState on postback; if ViewState MAC validation were ever disabled, the client could choose its own tier. Second, Math.Round(x, 2) rounds half to even, so a premium of 105.125 displays as 105.12. Whether the new service keeps that is a business decision, and the spec records it either way.

FAQ

ASP.NET WebForms to React questions

Does PYD ENTERPRISES read .aspx markup or only code-behind?

Both. Markup is parsed so server controls, validators, data sources and event wiring are part of the analysis alongside code-behind and referenced libraries.

How are user controls and master pages handled?

User controls (.ascx) and master pages are parsed and linked to the pages that use them, including properties and events they expose, so shared logic is extracted once.

Can it help with VB.NET code-behind?

Yes. VB.NET and C# code-behind are both supported, and pages mixing the two through shared libraries resolve into one symbol graph.

Get the spec for your ASP.NET WebForms estate.

Request a codebase scan and review extracted rules, contracts and fixtures for your own ASP.NET WebForms estate, inside your VPC.