Introduction

One of the things I initially found quite convenient while working with ASP.NET Web Forms was that the controls on a page could remember their values even after submitting the form. For example, if we have a page with a Label, TextBox, DropDownList and GridView, ASP.NET can preserve certain properties of these controls across postbacks without requiring us to write additional code. Coming from a background of learning C# and basic web development, this behaviour was quite interesting. HTTP, as we know, is a stateless protocol. Every request sent from the browser to the web server is independent. So how does ASP.NET remember the state of controls between requests?

The answer, at least in part, is ViewState.

While working with Web Forms, I realised that ViewState is one of those features which makes development easier but can also introduce performance and security concerns if we do not understand how it works. In this article, I would like to explore how ViewState works internally, how ASP.NET serializes control state, and what we should consider while using it in web applications.

1. What Exactly Is ViewState?

ViewState is a state management mechanism provided by ASP.NET Web Forms to preserve page and control property values between postbacks. Consider a simple example:

<asp:Label
    ID="lblMessage"
    runat="server"
    Text="Welcome">
</asp:Label>

<asp:Button
    ID="btnSubmit"
    runat="server"
    Text="Submit"
    OnClick="btnSubmit_Click" />

Now suppose we change the Label text in the button click event:

protected void btnSubmit_Click(object sender, EventArgs e)
{
    lblMessage.Text = "Form submitted successfully";
}

When the page is submitted again, ASP.NET can preserve the updated Label text through ViewState, provided that ViewState is enabled and the control is not overwritten during page processing. Without ViewState, we may need to assign the value again or retrieve it from another source. However, one important point is that ViewState does not preserve every value on a web page. For example, a TextBox normally receives its value from the submitted HTTP form data. This means the TextBox can retain the user's entered value even when its ViewState is disabled. Understanding this distinction helps us avoid enabling ViewState unnecessarily.

2. Where Is ViewState Stored?

Unlike Session State, which can be maintained on the server, ViewState is generally stored on the client side. ASP.NET includes a hidden field in the HTML generated for the page. For example:

<input type="hidden"
       name="__VIEWSTATE"
       id="__VIEWSTATE"
       value="/wEPDwUKMT..." />

The actual value is usually much longer. When the browser submits the form, this hidden field is sent back to the server along with the other form values. ASP.NET then processes the submitted ViewState information and restores the relevant control properties. This leads to an important observation. Although ViewState is managed by the ASP.NET framework, its data travels between the server and browser. It is therefore not the same as storing information privately in server memory. We should be careful about the type and amount of information we allow to be stored in ViewState.

3. How Does ViewState Serialization Work?

To understand ViewState properly, we need to understand serialization. Serialization is the process of converting an object or its state into a format that can be stored or transferred. Deserialization is the reverse process, where the stored representation is converted back into information that the application can use. ASP.NET uses a serialization mechanism to represent ViewState data. The framework commonly uses ObjectStateFormatter for formatting state information in ASP.NET 2.0 and later versions. The resulting data is encoded using Base64 before being included in the HTML page. A simplified representation of the process is:

Server-side control state → State serialization → Base64 encoding → Hidden field in HTML → HTTP postback → Decoding and deserialization → Restored control state

Base64 is an encoding mechanism, not encryption. This is an important distinction because Base64-encoded information can be decoded. For example, consider the following C# code:

string message = "Hello ASP.NET";

byte[] bytes =
    System.Text.Encoding.UTF8.GetBytes(message);

string encoded =
    Convert.ToBase64String(bytes);

string decoded =
    System.Text.Encoding.UTF8.GetString(
        Convert.FromBase64String(encoded));

The encoded string is:

SGVsbG8gQVNQLk5FVA==

We can easily convert it back to the original message. Actual ViewState serialization is more complex than this example, but the principle remains that encoding does not provide confidentiality. This is why sensitive information should not be placed in ViewState merely because the hidden field appears unreadable.

4. How ASP.NET Tracks Changes in Control State

Another interesting aspect of ViewState is that ASP.NET does not always need to store every property of every control. Controls maintain state information using a StateBag, which is exposed through the ViewState property. ASP.NET tracks changes to relevant properties after state tracking has started. During the page lifecycle, controls are initialised, ViewState tracking begins, and subsequent property changes may be saved for the next postback. For example:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        lblMessage.Text = "Welcome Employee";
    }
}

The value assigned during the initial request can be saved and restored during later postbacks. However, if we assign a new value during every Page_Load, the restored value may be overwritten. This is one of the reasons why understanding the Page Lifecycle is important while working with ViewState. We should remember that ViewState is closely connected with the lifecycle of the controls.

5. ViewState and the ASP.NET Page Lifecycle

ViewState is restored during the page processing lifecycle, before the Page_Load event. A simplified sequence for a postback is:

  1. Page and control initialisation
  2. Restoration of ViewState
  3. Processing of posted form data
  4. Execution of Page_Load
  5. Postback control events
  6. PreRender processing
  7. Saving of state
  8. Rendering of the HTML response

There are additional events and processing steps, but this sequence helps explain the relationship between ViewState and postback handling. Suppose we have a DropDownList populated from the database:

protected void Page_Load(object sender, EventArgs e)
{
    LoadDepartments();
}

If LoadDepartments() clears and binds the list every time, it may interfere with the selected value restored during postback processing. A better starting approach is:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        LoadDepartments();
    }
}

Now the initial list is populated only on the first request. During subsequent postbacks, ASP.NET can restore the necessary control information, assuming the control structure and state management settings support it. This is a simple example, but it demonstrates how lifecycle decisions can influence application behaviour.

6. ViewState and ControlState

While exploring ViewState, I also came across another concept called ControlState. ControlState was introduced in ASP.NET 2.0. It is used by controls to preserve information that is essential for their proper functioning. The main difference is that ViewState can be disabled, while ControlState cannot be disabled through the normal EnableViewState setting. For example, certain controls require essential state information to maintain their behaviour across postbacks. If this information were stored only in ViewState, disabling ViewState could break the control. ControlState provides a separate mechanism for maintaining such essential information. Both ViewState and ControlState can be persisted as part of the page's state information. This distinction is particularly useful when developing custom server controls.

7. The Performance Cost of ViewState

One of the major concerns with ViewState is the additional size it can introduce into a web page.

Since ViewState is stored in a hidden field, the data is included in the HTML response sent to the browser.

During a postback, the browser sends the state information back to the server.

This creates additional data transfer in both directions.

Consider an application used by employees across multiple office locations.

If a page contains a large GridView with several columns and ViewState is enabled, the hidden state information may become quite large.

Suppose the ViewState field adds 100 KB to a page.

For 1,000 postbacks, this represents approximately 100 MB of additional request data transferred to the server, excluding HTTP overhead and other form data.

The initial and subsequent HTML responses may also contain ViewState data.

The actual size depends on the controls, their properties, and how much state is being saved.

Although 100 KB may not seem significant for a single request, the additional transfer can become noticeable when many users access the application or when network connectivity is slow.

In 2012, this is particularly relevant for applications accessed over slower office networks, remote connections, or mobile internet.

How Can We Measure ViewState Size?

One simple method is to inspect the generated HTML using the browser's View Source option.

We can search for:

id="__VIEWSTATE"

The length of the value gives us an indication of the amount of state being transferred.

For a more systematic check, we can inspect the response using browser developer tools or an HTTP debugging utility such as Fiddler.

We should also remember that ASP.NET can split large ViewState values across multiple hidden fields, depending on the configuration.

Therefore, measuring only one field may not always represent the complete state payload.

8. Reducing Unnecessary ViewState

ASP.NET provides several ways to control ViewState usage.

Disabling ViewState for Individual Controls

For example:

<asp:Label
    ID="lblHeading"
    runat="server"
    Text="Employee Information"
    EnableViewState="false" />

If a Label always displays the same heading, there is usually no need to preserve its changing state.

Similarly, a read-only GridView that is rebound whenever required may not need ViewState.

<asp:GridView
    ID="gvEmployees"
    runat="server"
    EnableViewState="false">
</asp:GridView>

However, we need to test functionality such as sorting, paging, editing, and selection before disabling state.

Disabling ViewState at Page Level

We can also disable ViewState for an entire page:

<%@ Page
    Language="C#"
    EnableViewState="false" %>

This should be done only after understanding which controls depend on ViewState.

Using ViewStateMode in ASP.NET 4.0

ASP.NET 4.0 provides the ViewStateMode property, which offers more flexibility.

We can disable ViewState at the page level and enable it only for selected controls.

For example:

<%@ Page
    Language="C#"
    ViewStateMode="Disabled" %>

<asp:Label
    ID="lblStatus"
    runat="server"
    ViewStateMode="Enabled" />

This approach is useful when most controls do not require ViewState, but a few controls do.

It allows us to manage state more selectively instead of enabling it throughout the page.

9. Security Considerations

Because ViewState is stored on the client, we should consider its security implications.

One concern is whether a user can modify the ViewState value before submitting the page.

ASP.NET provides a Message Authentication Code, commonly called MAC, to help protect ViewState integrity.

When ViewState MAC validation is enabled, the server can detect unauthorised modifications to the protected state data.

The relevant configuration is associated with enableViewStateMac.

It is important to keep MAC protection enabled.

Disabling it can expose the application to ViewState tampering risks.

ViewState Encryption

ASP.NET also supports ViewState encryption.

We can configure encryption using the ViewStateEncryptionMode property.

For example:

<%@ Page
    Language="C#"
    ViewStateEncryptionMode="Always" %>

Encryption helps protect the confidentiality of state information.

However, it can introduce additional processing overhead and does not make ViewState an appropriate storage location for passwords or other highly sensitive information.

A better approach is to avoid placing such information in client-side state.

ViewStateUserKey

Another useful property is ViewStateUserKey.

It allows an application to associate ViewState integrity protection with a user-specific value, helping reduce certain cross-site request forgery risks involving ViewState.

For example, in an application using ASP.NET Session State:

protected void Page_Init(object sender, EventArgs e)
{
    if (Session != null)
    {
        ViewStateUserKey = Session.SessionID;
    }
}

The value should be assigned during Page_Init, before ViewState is loaded.

This is only a simplified example. In a real application, we need to consider how session identifiers are managed, especially around authentication.

We should also use appropriate request validation, output encoding, authorisation checks, and other security controls.

ViewState protection is only one part of application security.

10. ViewState Versus Other State Management Techniques

ASP.NET provides multiple mechanisms for managing state.

Each mechanism has its own advantages and limitations.

MechanismStorage locationTypical use
ViewStateBrowser hidden fieldPreserving page control properties
ControlStatePage state hidden fieldEssential control state
Session StateServer or configured session storeUser-specific information across requests
Application StateServer application memoryShared application-wide information
CookiesBrowserSmall client-side values
Query StringsURLNavigation parameters and identifiers
CacheServer memoryFrequently accessed reusable data

For example, suppose we have an employee management application.

The selected tab or a control's display property may be maintained using ViewState.

The current employee identifier may be passed through a Query String, provided that the application validates it and performs the required authorisation checks.

User-specific information needed across multiple pages may be stored in Session State.

Frequently accessed department information may be cached to reduce database calls.

The important point is that we should select a state management mechanism based on the actual requirement.

Using ViewState for every piece of information is neither necessary nor efficient.

11. A Practical Example: Employee Search Page

Let us consider a simple employee search page.

The page contains:

  • A TextBox for entering an employee name
  • A Search button
  • A GridView for displaying matching employees
  • A Label for displaying the number of records found

When the user clicks Search, the application retrieves matching records from the database.

A possible implementation is:

protected void btnSearch_Click(object sender, EventArgs e)
{
    string searchText = txtEmployeeName.Text.Trim();

    EmployeeService service = new EmployeeService();

    DataTable results =
        service.SearchEmployees(searchText);

    gvEmployees.DataSource = results;
    gvEmployees.DataBind();

    lblCount.Text =
        results.Rows.Count.ToString() +
        " employees found";
}

Here, the EmployeeService represents a separate business or data access component.

If the GridView is used only to display search results, we can consider disabling its ViewState.

However, doing so means that we must manage data binding appropriately on later postbacks.

For example, if the GridView supports paging, we may need to execute the search again when the page index changes.

protected void gvEmployees_PageIndexChanging(
    object sender,
    GridViewPageEventArgs e)
{
    gvEmployees.PageIndex = e.NewPageIndex;

    BindSearchResults();
}

The BindSearchResults() method would retrieve the required records and bind the GridView again.

This approach may reduce ViewState size, but it can increase database queries.

Therefore, we need to consider both network transfer and database processing when making the decision.

There is no single setting that will improve performance in every situation.

12. Lessons Learned While Working with ViewState

After spending some time understanding ViewState, I have started looking at it differently.

Earlier, I considered it a feature that automatically preserved control values.

Now I understand that it is part of the overall state management design of an ASP.NET application.

A few practical lessons stand out.

First, ViewState is not free. It simplifies development but introduces additional data transfer and processing.

Second, ViewState is not the same as Session State. ViewState travels with the page, whereas Session State is maintained through the configured server-side session mechanism.

Third, Base64 encoding is not encryption. We should not assume that hidden state information is confidential.

Fourth, the Page Lifecycle matters. Creating controls, binding data, and assigning properties at the wrong stage can cause unexpected behaviour.

Fifth, performance optimisation requires measurement. Disabling ViewState may reduce page size, but it can also require additional database calls.

Finally, state management should be a conscious design decision rather than something we leave entirely to the framework.

Conclusion

ASP.NET ViewState is a useful feature that helps Web Forms provide an event-driven programming experience over the stateless HTTP protocol.

It allows controls to preserve relevant property values across postbacks, reducing the amount of code developers need to write.

However, understanding its internal behaviour is important.

Serialization, Base64 encoding, control state tracking, page lifecycle processing, security validation, and network transfer all influence how ViewState behaves.

As someone still learning ASP.NET development, I find that understanding these internal concepts makes debugging much easier.

Instead of simply adding IsPostBack checks or disabling ViewState whenever a page becomes slow, we can investigate why the problem is happening and select a suitable solution.

My main takeaway is that convenience provided by a framework should not replace understanding of how the framework works.

ViewState is a good example of this.

Used correctly, it can simplify web application development. Used without consideration, it can contribute to unnecessary page size, confusing postback behaviour, and security risks.

As I continue working with ASP.NET Web Forms, I intend to pay more attention to the state maintained by each control and how it affects the overall application.

ViewState Internals: Serialization, Performance, Security and State Management