Web Technologies

Chapter 6
Front-End Frameworks and State Management

Bachelors Program in AI & ML
Web Technologies   •   Bachelor

Front-End Frameworks and State Management

Introduction

Modern web applications increasingly behave like software products rather than collections of independent documents. Users expect immediate feedback, smooth navigation, responsive interfaces, offline tolerance, and consistent data across many screens. These expectations are difficult to satisfy with manually coordinated DOM manipulation and loosely organized scripts. Front-end frameworks address this problem by providing abstractions for components, rendering, routing, data flow, and application structure.

A front-end framework does not replace HTML, CSS, or JavaScript. Instead, it organizes their use. Frameworks such as React, Angular, Vue, and Svelte provide different approaches to representing user interfaces and updating them when application data changes. In this chapter, React terminology is used frequently because it is widely adopted, but the underlying principles apply across contemporary frameworks.

State management is central to this topic. State is the information that can change during the lifetime of an application: authentication status, shopping-cart contents, form values, selected filters, server responses, or the current route. Poorly managed state causes inconsistent screens, unnecessary network requests, difficult debugging, and security problems. Well-designed state management makes an application predictable, testable, and maintainable.

The chapter also examines rendering strategies. Client-side rendering (CSR), server-side rendering (SSR), and static site generation (SSG) make different trade-offs among performance, interactivity, scalability, search visibility, and implementation complexity.

Learning Objectives

By the end of this chapter, a student should be able to:

1. Explain the purpose and architecture of component-based front-end frameworks.

2. Distinguish properties, local state, derived state, and server state.

3. Describe reactive rendering and the role of a virtual or compiled view representation.

4. Design reusable components with appropriate data-flow boundaries.

5. Implement conceptual routing and protected navigation.

6. Compare local state, context, centralized stores, and server-state libraries.

7. Select among CSR, SSR, SSG, and hybrid rendering strategies.

8. Analyze performance, accessibility, security, and maintainability consequences.

9. Apply front-end design principles to a realistic application scenario.

1. The Framework Model

1.1 From pages to applications

Traditional multi-page websites request a new HTML document for many navigation actions. The browser loads the document, parses its resources, and displays the page. A single-page application (SPA), by contrast, usually loads an application shell once and changes the visible view through JavaScript and client-side routing. The browser remains on the same document while the application changes its component tree.

An SPA commonly contains the following layers:

  • View layer: Components that describe what users see.
  • Interaction layer: Event handlers responding to clicks, typing, gestures, and keyboard input.
  • State layer: Local, shared, and remotely fetched data.
  • Routing layer: Mapping URLs to views and navigation behavior.
  • Data-access layer: Functions for calling APIs and interpreting responses.
  • Build and delivery layer: Bundling, code splitting, asset optimization, and deployment.

The framework supplies conventions for connecting these layers. It does not automatically produce good architecture; developers must still establish boundaries and responsibilities.

1.2 Components

A component is a cohesive unit of interface behavior and presentation. In a React-like model, a component is commonly a function that receives properties and returns a description of the interface:

```jsx

function Greeting({ name }) {

return <h1>Hello, {name}</h1>;

}

```

Here, `Greeting` is reusable because its output depends on the `name` property rather than on a hard-coded value. Components can be composed:

```jsx

function ProfilePage({ user }) {

return (

<main>

<Greeting name={user.displayName} />

<AccountSummary user={user} />

</main>

);

}

```

Componentization should follow meaningful responsibility, not arbitrary file size. A component is a good candidate for extraction when it has a clear conceptual role, is reused, has independent state or behavior, or can be tested separately.

1.3 Properties and state

Properties, often called props, are inputs supplied by a parent component. They should normally be treated as immutable by the receiving component. State is data owned by a component or a broader store and may change over time.

For example, a `ProductCard` may receive a product as a property, while its `isExpanded` value may be local state. A parent owns the product selection, whereas the card owns whether its description is currently expanded. This distinction supports one-way data flow: data moves downward through properties, while events are communicated upward through callbacks or actions.

A common design error is storing information that can be calculated from existing state. If `firstName` and `lastName` already exist, `fullName` is usually derived data rather than independent state. Duplicating it creates a risk that the two representations will disagree.

1.4 Declarative and reactive rendering

Imperative DOM programming instructs the browser how to change individual elements: find a node, alter its text, add a class, and remove another node. Declarative rendering describes the desired interface for a given state. The framework determines how to update the real DOM or another rendering target.

Conceptually, a view can be represented as:

\[

V = f(S, P)

\]

where `V` is the rendered view, `S` is component or application state, `P` is the set of properties, and `f` is the rendering function. This is a conceptual model rather than a performance equation. It assumes that rendering is deterministic for the supplied inputs and that side effects are handled outside the pure rendering calculation.

When state changes, a reactive framework identifies the affected portion of the interface and applies the necessary update. React commonly compares successive virtual representations; compiled frameworks may transform reactive expressions into targeted updates. The implementation differs, but the design principle is similar: developers express relationships between data and views rather than manually synchronizing every DOM operation.

Rendering functions should be as pure as practical. Network requests, timers, logging, and direct DOM manipulation are side effects and belong in appropriate lifecycle mechanisms or service modules.

2. Reusable Component Design

2.1 Cohesion, coupling, and composition

A reusable component should have high cohesion: its code should support one understandable responsibility. It should also have low coupling: it should not depend unnecessarily on unrelated application details. Composition is generally preferable to inheritance for interface construction. A layout component can receive a header, navigation region, and content region as children rather than knowing the internal implementation of each region.

For example:

```jsx

function Panel({ title, children, actions }) {

return (

<section className="panel">

<header>

<h2>{title}</h2>

<div>{actions}</div>

</header>

<div>{children}</div>

</section>

);

}

```

This component is more reusable than one that directly imports a particular page's data and action buttons.

2.2 Controlled and uncontrolled inputs

A controlled input obtains its displayed value from application state and reports changes through an event handler. This is useful for validation, conditional fields, and synchronized forms. An uncontrolled input maintains its value in the browser's DOM and is read when needed, often through a reference. Controlled inputs provide stronger visibility of state but can require more code and careful performance design for very large forms.

A form should distinguish between input state, validation state, submission state, and server-response state. Treating all of these as one undifferentiated object makes error handling difficult.

2.3 Keys and identity

When rendering a collection, frameworks need a stable identity for each item. A key allows the framework to determine whether an item was retained, inserted, removed, or reordered. A database identifier is usually better than an array index. Index keys can cause incorrect input values or component state to appear attached to the wrong item after insertion or sorting.

2.4 Accessibility as a component responsibility

Reusable components must preserve accessibility. Buttons should be actual buttons rather than clickable `div` elements. Form controls require labels, focus must be visible, keyboard interaction must be supported, and dynamic updates may need appropriate announcements. A component library that produces visually attractive but inaccessible widgets creates systematic defects across the application.

3. Routing and Application Navigation

Routing maps a URL to an application view. A client-side router typically observes navigation events, selects a route, and renders the corresponding component without requesting a complete document. Routes may contain parameters, such as `/products/:productId`, or query values, such as `/products?category=books`.

A route configuration can be conceptualized as:

```text

Route pattern Component Data requirement

/ Home Featured content

/products ProductList Product collection

/products/:id ProductDetails One product

/account Account Authenticated user

```

Good routing design treats URLs as meaningful, bookmarkable application state. Filters, pagination, and tabs that users may wish to share should often be represented in query parameters rather than hidden only in memory.

3.1 Nested and protected routes

Nested routes allow a shared layout to remain visible while a child view changes. For example, an account layout may contain persistent navigation while rendering profile, orders, and security subviews.

Protected routes require more than hiding a link. The server must authorize the requested resource. Client-side guards improve user experience by redirecting unauthenticated users, but they are not a security boundary because users can modify client-side code or send requests directly to an API. Authorization must therefore be enforced by the backend for every protected operation.

3.2 Navigation states

Navigation should account for loading, success, empty, error, and unauthorized states. A route transition that displays a blank screen while data loads is poor interaction design. Skeleton interfaces, progress indicators, meaningful error messages, and retry actions improve resilience.

4. State Management

4.1 Categories of state

State is easier to manage when classified according to ownership and lifetime.

  • Local UI state: A dialog's open status, an input's temporary value, or a selected tab.
  • Shared client state: Theme preference, authenticated-user information, or a multi-step workflow.
  • URL state: Path parameters, search parameters, and route position.
  • Server state: Data owned by a backend, such as products, orders, or messages.
  • Derived state: Values calculated from other state, such as a filtered list or total price.

Server state is not merely global client state. It has additional concerns: caching, freshness, retries, synchronization, pagination, optimistic updates, and invalidation after mutations. A specialized server-state library can manage these concerns more reliably than manually copying every response into a general-purpose store.

4.2 Choosing a state mechanism

Use the least powerful mechanism that correctly represents the requirement.

  • Use component state for data needed by one component or a small subtree.
  • Lift state to the nearest common ancestor when several related components need it.
  • Use context for relatively stable cross-cutting values such as locale, theme, or dependency access.
  • Use a centralized store when many distant components need coordinated client state, complex transitions, or time-travel-style debugging.
  • Use a server-state solution for cached API data and synchronization.
  • Use the URL for shareable navigation state.

A global store is not automatically better. It can make dependencies invisible, increase re-rendering, and encourage unrelated features to share mutable structures.

4.3 Reducers and predictable transitions

A reducer models state transitions as a function of current state and an action:

\[

S_{t+1} = R(S_t, A_t)

\]

where `S_t` is the current state, `A_t` is an action at time `t`, and `R` is a reducer. The model assumes that the reducer is deterministic and does not directly perform side effects. For example:

```js

function cartReducer(state, action) {

switch (action.type) {

case "addItem":

return { ...state, items: [...state.items, action.item] };

case "clear":

return { ...state, items: [] };

default:

return state;

}

}

```

Immutable updates do not mean that every value must be deeply copied on every action. They mean that changed portions receive new references while unchanged portions can be reused. This supports change detection and reduces accidental mutation.

4.4 Asynchronous state and effects

Fetching data introduces intermediate states. A useful request model distinguishes at least `idle`, `loading`, `success`, and `error`. Applications should also consider cancellation, stale responses, duplicate requests, and race conditions. For instance, if a user changes a search query rapidly, an earlier request may finish after a later request. Without protection, stale results can overwrite current results.

Effects should be tied to explicit dependencies and cleaned up when necessary. Subscriptions, timers, and event listeners that are not removed can cause memory leaks or updates to unmounted components.

4.5 Optimistic updates

An optimistic update changes the interface before the server confirms the operation. It produces a fast experience but requires a rollback strategy. It is appropriate when failures are uncommon and the operation can be reversed or reconciled. It is less appropriate for irreversible actions or transactions requiring authoritative validation.

5. Rendering Strategies

5.1 Client-side rendering

With CSR, the browser receives a relatively small HTML shell and JavaScript that constructs the interface. CSR is well suited to highly interactive authenticated applications. It can provide fast transitions after the initial bundle has loaded, but the first meaningful display may be delayed by downloading, parsing, and executing JavaScript. Search-engine visibility and sharing previews may also require additional treatment.

5.2 Server-side rendering

With SSR, a server renders HTML for a request and sends it to the browser. The browser can display meaningful content before the client application becomes fully interactive. Hydration then attaches event handling to the server-generated markup.

SSR can improve initial content visibility, but it increases server work and introduces hydration constraints. The server and client must produce compatible output. Browser-only APIs cannot be used during server rendering without guards. Data-fetching and caching strategies also become more complex.

5.3 Static site generation

SSG produces HTML during a build process rather than per request. It is effective for documentation, marketing pages, and content that changes relatively infrequently. A content update generally requires a rebuild or a revalidation mechanism. Static output can be distributed efficiently through a content delivery network.

5.4 Hybrid and incremental strategies

Real systems commonly combine strategies. Public product pages may use SSR or static generation, while account dashboards use CSR. Some platforms support incremental regeneration, where generated pages are refreshed after a defined policy or an explicit content event. The choice should be based on content volatility, personalization, traffic patterns, SEO requirements, infrastructure, and acceptable latency—not on fashion.

A useful decision framework asks:

1. Must the content be visible before JavaScript executes?

2. Is it personalized for each user?

3. How frequently does it change?

4. Can the organization operate rendering servers?

5. Is the route highly interactive after loading?

6. Performance and Quality Engineering

Front-end performance includes more than download size. Important concerns include time to first meaningful content, interaction responsiveness, layout stability, network efficiency, and perceived progress. Large JavaScript bundles increase transfer and execution costs, especially on mobile devices.

Common techniques include code splitting by route, lazy loading below-the-fold features, image optimization, caching, minimizing unnecessary renders, and avoiding expensive work during input events. Memoization can help when a calculation is expensive and inputs are stable, but indiscriminate memoization adds complexity and memory use.

Performance measurement should use real evidence. Browser developer tools, automated audits, real-user monitoring, and controlled tests provide different kinds of information. A synthetic test may reveal a slow bundle, while field data may reveal poor performance on a particular device or network.

Testing should operate at several levels:

  • Unit tests: Test pure functions, reducers, and small utilities.
  • Component tests: Test rendering and interaction in isolation.
  • Integration tests: Test routing, data access, and component collaboration.
  • End-to-end tests: Test realistic user journeys in a browser.
  • Accessibility tests: Combine automated checks with keyboard and assistive-technology evaluation.

7. Security, Privacy, and Governance

Front-end code is delivered to users and must be treated as observable and modifiable. Secrets such as private API keys must not be placed in client bundles. Authentication tokens require careful storage and transport decisions. Cookies with appropriate `HttpOnly`, `Secure`, and `SameSite` settings can reduce some risks, but no single setting solves all authentication problems.

Applications must defend against cross-site scripting by escaping untrusted content and avoiding unsafe HTML injection. Content Security Policy can reduce the impact of certain injection attacks. Cross-site request forgery protection may be required depending on the authentication mechanism and request design.

Privacy requires data minimization, clear consent where applicable, appropriate retention, and avoidance of unnecessary tracking. Accessibility and inclusive design are also governance concerns. Depending on jurisdiction and sector, organizations may need to comply with data-protection, accessibility, records, and consumer-protection requirements.

8. Illustrative Case: University Course Registration Portal

Background

A university has an existing course-registration portal. Each action reloads a page, search results are slow, mobile use is difficult, and students lose form entries when validation fails. The university wants a responsive application that supports course search, timetable construction, registration, and wait-list management.

Problem analysis

The portal contains several kinds of state. Search filters belong in the URL so that results can be shared. The student's timetable is shared by search results, course details, and a timetable view. Course availability is server state because it changes as other students register. Temporary confirmation dialogs and form fields are local UI state. Authentication and enrollment permissions must be enforced by the server.

The system also has different rendering needs. Public course descriptions should be quickly visible and indexable, while the authenticated registration workspace requires high interactivity. A single rendering strategy would not serve every route equally well.

Proposed approach

The team builds reusable components including `SearchBox`, `CourseCard`, `CapacityBadge`, `ScheduleGrid`, `RegistrationDialog`, and `ErrorMessage`. A route configuration maps public course pages and authenticated workspace routes to their respective views. Search parameters are synchronized with the URL. A server-state library caches course queries and invalidates availability after a successful registration.

The timetable is managed through a reducer because adding, dropping, and replacing courses involve explicit transitions and conflict checks. The reducer remains pure; API calls occur in an application service. The interface displays loading and error states and disables duplicate submissions while a request is pending.

Public course pages use static generation with periodic revalidation. The registration workspace uses CSR after authentication. A server-rendered shell provides the initial authenticated layout where appropriate, but authorization remains a backend responsibility.

Outcome

Students can navigate between search results and course details without full-page reloads. Invalid schedules are explained before submission, while final availability is checked by the server. URL-based filters improve sharing and browser navigation. The system is easier to test because scheduling rules are expressed as pure reducer and validation functions.

Learning points

The case demonstrates that state classification should precede tool selection. It also illustrates why routing, rendering strategy, component boundaries, and backend authorization must be designed together. A framework can simplify implementation, but it cannot decide which data is authoritative, which state should be shareable, or what security guarantees are required.

9. Contemporary Developments and Best Practices

Contemporary frameworks increasingly support server components, streaming SSR, partial hydration, resumability, and fine-grained reactivity. These approaches attempt to reduce client-side JavaScript while preserving rich interaction. They also introduce new boundaries: developers must know which code runs on the server, which data can cross that boundary, and how serialization and authorization are handled.

AI-assisted development can help generate component scaffolding, tests, and documentation, but generated code requires review. AI tools may introduce insecure dependencies, inaccessible markup, excessive re-renders, or incorrect assumptions about framework APIs. Teams should use code review, automated testing, dependency scanning, and licensing checks.

Recommended practices include:

1. Define state ownership before choosing a state library.

2. Keep rendering logic declarative and side effects explicit.

3. Prefer composition and stable component interfaces.

4. Keep server authorization independent of client route guards.

5. Represent shareable state in the URL.

6. Model loading, empty, error, and success states deliberately.

7. Use stable keys and avoid mutation of shared state.

8. Measure performance on realistic devices and networks.

9. Build accessibility into reusable components.

10. Select CSR, SSR, SSG, or hybrid rendering according to route requirements.

11. Keep dependencies current and audit them for security and licensing risk.

12. Document data ownership, cache invalidation, and failure behavior.

Challenges and Limitations

Framework abstraction can conceal expensive operations and make debugging unfamiliar execution models difficult. Rapid ecosystem change can create migration costs and fragmented practices. Global state stores may become accidental databases, while excessive component decomposition can make control flow difficult to follow. SSR and hydration can improve first display but increase infrastructure and debugging complexity. CSR can provide excellent interaction after loading but may perform poorly on constrained devices. No rendering strategy is universally superior.

The most important limitation is organizational rather than technical: a framework cannot compensate for unclear requirements, weak domain modeling, inadequate testing, or poor security practice. Effective front-end engineering combines framework knowledge with sound software design and an understanding of users and operating conditions.

Chapter Summary

Component-based front-end frameworks organize interfaces into reusable units whose output depends on properties and state. Reactive rendering allows developers to describe the desired interface while the framework manages updates. One-way data flow, stable identity keys, composition, accessibility, and clear component responsibilities improve maintainability.

State should be classified as local UI state, shared client state, URL state, server state, or derived state. Local mechanisms are preferable for local concerns; centralized stores and server-state tools should be introduced when their specific benefits are needed. Reducers provide deterministic state transitions, while asynchronous effects require explicit handling of loading, errors, cancellation, and stale data.

Routing connects URLs to views and supports nested, shareable, and protected navigation. Client-side route guards improve experience but do not provide authorization. CSR, SSR, SSG, and hybrid strategies offer different trade-offs involving interactivity, first display, search visibility, personalization, infrastructure, and freshness. Performance, testing, accessibility, security, privacy, and governance must be considered throughout the application lifecycle.

Key Terms

Component: A cohesive, reusable unit of interface structure and behavior.

Property (prop): An input passed from a parent component to a child component.

State: Mutable information that influences an application's future behavior or rendered output.

Reactive rendering: Updating an interface automatically when relevant data changes.

Declarative UI: An approach that describes the desired interface for a given state.

Single-page application: An application that changes views without routinely requesting complete new documents.

Routing: Mapping URLs and navigation events to application views.

Client-side rendering: Constructing the primary interface in the browser using client-side code.

Server-side rendering: Generating HTML on a server in response to a request.

Static site generation: Producing HTML during a build process before requests occur.

Hydration: Attaching client-side behavior to server-rendered HTML.

Local state: State owned by one component or a small component subtree.

Server state: Data whose authoritative source is a remote service or backend.

Derived state: A value calculated from other state rather than stored independently.

Reducer: A deterministic function that calculates next state from current state and an action.

Context: A mechanism for providing values to descendant components without passing them through every intermediate component.

Code splitting: Dividing application code into separately loaded bundles or chunks.

Optimistic update: Updating the interface before confirmation from the server.

Hydration mismatch: A disagreement between server-generated markup and client-rendered output.

Progressive enhancement: Designing a functional baseline that is enhanced when browser capabilities permit.

Review Questions

Short Answer Questions

1. What is the difference between a component property and component state?

2. Why are stable keys important when rendering a collection?

3. Name four categories of application state.

4. What is the purpose of a reducer?

5. Why cannot a client-side route guard replace backend authorization?

Long Answer Questions

1. Explain how declarative reactive rendering differs from imperative DOM manipulation. Include the conceptual relationship between state and view.

2. Compare local state, context, centralized stores, and server-state libraries. Give an appropriate use case for each.

3. Explain CSR, SSR, and SSG, including their main benefits and limitations.

4. Describe how component composition, one-way data flow, and accessibility contribute to maintainable front-end systems.

5. Discuss the security and privacy responsibilities of a front-end development team.

Analytical / Application Questions

1. A news website contains public articles, personalized recommendations, and an interactive saved-articles dashboard. Propose rendering and state-management strategies for each area and justify your decisions.

2. A search interface displays results from multiple requests. Users often type quickly, and older responses sometimes replace newer results. Analyze the cause and design a solution.

3. Design the state ownership and routing model for an online shopping cart. Identify local, URL, shared client, derived, and server state, and explain how a failed checkout should be handled.

Multiple Choice Questions with Answers

Question 1

Which statement best describes a component property?

A. A value passed from a parent component to a child component

B. A private database record stored on the server

C. A browser event that always changes the DOM directly

D. A compiled JavaScript bundle

Correct Answer: A. A value passed from a parent component to a child component

Question 2

Why is an array index often a poor key for a rendered list?

A. Indexes cannot be represented in JavaScript

B. An index may no longer identify the same item after insertion or reordering

C. Indexes prevent all list items from rendering

D. An index automatically exposes private data

Correct Answer: B. An index may no longer identify the same item after insertion or reordering

Question 3

Which type of state is usually most appropriate for a shareable product filter?

A. Only a local dialog state

B. A reducer action with no URL representation

C. URL query parameters

D. A CSS custom property

Correct Answer: C. URL query parameters

Question 4

What is a key characteristic of a pure reducer?

A. It performs network requests during every transition

B. It deterministically returns next state from current state and an action

C. It directly modifies the browser DOM

D. It stores authentication secrets in the client bundle

Correct Answer: B. It deterministically returns next state from current state and an action

Question 5

Which rendering strategy generates HTML during a build process?

A. Client-side rendering

B. Server-side rendering per request

C. Static site generation

D. Hydration only

Correct Answer: C. Static site generation

Question 6

What is hydration in an SSR application?

A. Compressing an image before upload

B. Attaching client-side behavior to server-generated HTML

C. Deleting server-rendered markup

D. Moving all state into a database

Correct Answer: B. Attaching client-side behavior to server-generated HTML

Question 7

Which statement about client-side route guards is correct?

A. They are sufficient authorization for protected API operations

B. They improve navigation behavior but must be supported by backend authorization

C. They eliminate the need for authentication

D. They prevent users from inspecting client-side code

Correct Answer: B. They improve navigation behavior but must be supported by backend authorization

Question 8

Which is an appropriate reason to use a server-state library?

A. To replace semantic HTML

B. To manage caching, freshness, retries, and invalidation for remote data

C. To guarantee that a server cannot fail

D. To store CSS styles globally

Correct Answer: B. To manage caching, freshness, retries, and invalidation for remote data

Question 9

What is a likely problem with an optimistic update that has no rollback strategy?

A. The interface may show success even when the server rejects the operation

B. The browser cannot render any components

C. The URL becomes permanently unavailable

D. Static pages cannot be generated

Correct Answer: A. The interface may show success even when the server rejects the operation

Question 10

Which practice best protects a front-end application from exposing private credentials?

A. Embedding private API keys in the JavaScript bundle

B. Putting secrets in CSS variables

C. Keeping private credentials on trusted server infrastructure

D. Hiding credentials with an HTML comment

Correct Answer: C. Keeping private credentials on trusted server infrastructure

Web Technologies
Chapter 6 of 14