Back to blog

UISampleSpark: Seven UI Paradigms, One Backend

February 5, 202613 min read

Most tutorial projects demonstrate one way to build a web interface. SampleMvcCRUD asks a different question — what if we demonstrated all of them? Seven radically different frontend approaches, the same backend API, the same data model, compared side by side.

UISampleSpark: Seven UI Paradigms, One Backend

Part 4 of the UISampleSpark Series — The UI Showcase

The Question That Changed Everything

Most tutorial projects demonstrate one way to build a web interface. SampleMvcCRUD asks a different question: What if we demonstrated all of them?

The same backend API. The same Employee and Department data model. The same CRUD operations — create, read, update, delete. But seven radically different approaches to rendering the user interface, each with its own philosophy, its own tradeoffs, and its own community of advocates.

This wasn't an academic exercise. In the real world, development teams face this decision on every new project: do we go with server-rendered pages or a single-page application? React or Vue? Should we minimize JavaScript or embrace it? Is there a middle ground?

SampleMvcCRUD answers these questions by letting developers compare the approaches side by side, running against the same data, in the same application.

The Seven Implementations

1. Traditional MVC (
/MvcEmployee
)

The oldest and most straightforward implementation. The

MvcEmployeeController
follows the classic ASP.NET MVC pattern with separate action methods for each CRUD operation:
Index
,
Create
,
Edit
,
Delete
. Each action renders a dedicated Razor view, and form submissions trigger full-page postbacks.

A user clicks "Edit" on an employee row. The browser navigates to

/MvcEmployee/Edit/42
. The server loads the employee, renders the edit form, and sends the complete HTML page. The user modifies fields, clicks "Save", and the form posts back for a full page load.

The tradeoff: Maximum simplicity and SEO friendliness at the cost of user experience. Every interaction requires a server round-trip and full page refresh. For internal tools and content-heavy sites, this is often perfectly acceptable.

  • Lines of code: ~200
  • JavaScript required: Minimal (DataTables for enhanced table display)

2. Razor Pages (
/EmployeeRazor
)

Razor Pages offer a page-centric alternative to MVC. Instead of controllers with multiple actions, each page has its own

.cshtml
file paired with a code-behind
.cshtml.cs
file containing handler methods (
OnGet
,
OnPost
).

Each page is self-contained, with its model, handlers, and view in one logical unit. For CRUD operations where each page maps naturally to a database operation, this model feels intuitive.

The tradeoff: Razor Pages reduce the cognitive overhead of MVC's controller-action-view triangle. The limitation is the same as traditional MVC: full page reloads on every interaction.

  • Lines of code: ~300
  • JavaScript required: Moderate (form enhancement)

3. jQuery AJAX with Partial Views (
/Employee
)

One of the project's original approaches, dating back to April 2019. jQuery and AJAX load HTML fragments from the server without full page reloads. Bootstrap modals handle create, edit, and delete operations.

When a user clicks "Edit", jQuery fires an AJAX request to the server, which renders a partial view and returns the HTML fragment. jQuery injects this into a Bootstrap modal. On save, another AJAX request submits the form data and the employee list partial is re-fetched and swapped into the DOM.

The tradeoff: More responsive user experience than full-page MVC without requiring a modern JavaScript framework. The server still renders all HTML. The downside is jQuery's imperative programming model — managing DOM state manually becomes unwieldy as complexity grows.

  • Lines of code: ~500
  • JavaScript required: jQuery + inline scripts

4. React SPA (
/EmployeeReact
)

The React implementation brings a modern component-based architecture. React 18 is loaded via CDN with Babel for in-browser JSX compilation, avoiding build toolchain complexity while demonstrating React's core concepts.

A single

EmployeeTable
component manages the entire interface. React Hooks (
useState
,
useEffect
,
useMemo
) handle state management. The Fetch API communicates with the REST backend. Client-side sorting, filtering, and pagination operate on data already loaded into component state.

const EmployeeTable = () => {
  const [employees, setEmployees] = React.useState([]);
  const [search, setSearch] = React.useState('');

  const filtered = React.useMemo(() => {
    return employees.filter(emp =>
      emp.name.toLowerCase().includes(search.toLowerCase())
    );
  }, [employees, search]);

  return <Table data={filtered} />;
};

The tradeoff: React's virtual DOM and declarative model make complex UIs manageable. State changes automatically trigger re-renders. But the CDN approach means Babel compiles JSX in the browser, adding latency on first load.

  • Lines of code: 737
  • JavaScript required: React 18 + Hooks

5. Vue SPA (
/EmployeeVue
)

Vue 3 provides a reactive SPA implementation using the Composition API loaded via CDN. Two-way data binding and fine-grained reactivity offer an alternative philosophy to React.

ref()
creates reactive variables,
computed()
derives auto-updating values, and
watch()
triggers side effects on state changes. Templates use Vue's directive syntax (
v-model
,
v-for
,
v-if
,
@click
) for declarative DOM binding.

const { createApp, ref, computed, watch } = Vue;
createApp({
  setup() {
    const employees = ref([]);
    const searchTerm = ref('');

    const filtered = computed(() => {
      if (!searchTerm.value) return employees.value;
      return employees.value.filter(e =>
        e.name.toLowerCase().includes(searchTerm.value.toLowerCase())
      );
    });

    watch(searchTerm, () => { currentPage.value = 1; });

    return { employees, searchTerm, filtered };
  }
}).mount('#vue-root');

The tradeoff: Vue's two-way binding with

v-model
simplifies form handling compared to React's controlled components. The template syntax feels closer to HTML than JSX, lowering the learning curve. The tradeoff is a smaller ecosystem compared to React.

  • Lines of code: 616
  • JavaScript required: Vue 3 Composition API

6. htmx — Hypermedia-Driven (
/EmployeeHtmx
)

The htmx implementation represents a fundamentally different philosophy: instead of shipping JavaScript frameworks to the browser and returning JSON from APIs, htmx extends HTML with attributes that enable AJAX, CSS transitions, and server-sent events directly in markup. The server returns HTML fragments, not data.

<input type="text" name="search"
       hx-get="/EmployeeHtmx/Table"
       hx-trigger="keyup changed delay:500ms"
       hx-target="#employee-table"
       hx-include="#pageSize"
       hx-swap="innerHTML" />

Search happens with a 500ms debounce. The server filters employees, renders a table partial with pagination controls, and returns the HTML. htmx swaps it into the target element. No JavaScript was written.

The controller uses

HX-Trigger
response headers to coordinate events. After a save operation, the server responds with an empty body and a
refreshTable
trigger, which htmx translates into a DOM event that reloads the table partial.

The tradeoff: Near-zero JavaScript (~20 lines in the entire implementation). Server-rendered HTML is SEO-friendly and cacheable. The server maintains all state. But every interaction requires a server round-trip, and complex client-side behaviors (drag-and-drop, real-time validation) are harder to implement.

  • Lines of code: 685 (view + partials + controller)
  • JavaScript required: ~20 lines (htmx handles the rest)

7. Blazor Server (
/EmployeeBlazor
)

The Blazor implementation eliminates JavaScript entirely by running C# on the server and pushing UI updates to the browser through a persistent SignalR WebSocket connection.

The component injects

IEmployeeService
directly — no HTTP API, no JSON serialization, no Fetch calls. Button clicks trigger C# methods on the server. Blazor diffs the rendered component tree and pushes minimal DOM changes over the WebSocket.

@inject IEmployeeService EmployeeService

@code {
    private List<EmployeeDto> employees = new();

    protected override async Task OnInitializedAsync()
    {
        employees = (await EmployeeService.GetEmployeesAsync(
            new PagingParameterModel(), cts.Token)).ToList();
    }

    private async Task HandleSave()
    {
        await EmployeeService.SaveAsync(formEmployee, cts.Token);
        await LoadData();
    }
}

The tradeoff: Zero JavaScript for CRUD logic. C# developers build interactive UIs without learning a JavaScript framework. Direct service injection bypasses the HTTP API, reducing latency. But every UI event travels over WebSocket to the server, creating latency for users on slow connections.

  • Lines of code: 626
  • JavaScript required: 0 (C# only, plus the Blazor framework script)

The Comparison Matrix

ImplementationParadigmJS NeededServer TripsState LocationBest For
MVCMulti-viewMinimalEvery actionServerContent sites, simple CRUD
Razor PagesPage-centricModerateEvery actionPage modelPage-focused workflows
jQuery AJAXModal CRUDjQueryAPI callsClient variablesLegacy modernization
ReactComponent SPAReact HooksAPI only
useState
Complex interactive UIs
VueReactive SPAComposition APIAPI only
ref()
+
computed()
Form-heavy applications
htmxHypermedia~20 linesEvery interactionServerSEO, progressive enhancement
Blazor ServerC# components0Every UI eventComponent state.NET-only teams

What Developers Learn by Comparing

State Management Spectrum

The seven implementations span the entire spectrum of state management:

  • Server state (MVC, Razor Pages, htmx): The server is the source of truth. Each request retrieves fresh data. Simplicity at the cost of latency.
  • Client state (React, Vue, jQuery): Data is loaded once and managed in the browser. Fast interactions, but state can drift from the server.
  • Hybrid state (Blazor): State lives on the server but UI updates push to the client over WebSocket. The speed of client-side rendering with the authority of server-side state.

JavaScript Complexity Spectrum

From zero JavaScript (Blazor) to framework-heavy (React, Vue), the implementations demonstrate that JavaScript isn't always the answer — and isn't always avoidable:

  • Blazor proves you can build interactive UIs without touching JavaScript
  • htmx proves you can add interactivity with HTML attributes instead of JavaScript code
  • jQuery proves that imperative DOM manipulation works but doesn't scale
  • React and Vue prove that declarative frameworks manage complexity better for rich UIs

The Performance Conversation

Each approach handles performance differently:

  • SPA approaches (React, Vue) load all data upfront, making subsequent interactions instant but initial load heavier
  • Server-rendered approaches (MVC, htmx) deliver fast initial loads but require round-trips for every interaction
  • Blazor occupies a unique position: fast initial load (thin client) but every click requires a WebSocket round-trip

Cloud-Native Aspirations: The .NET Aspire Experiment

SampleMvcCRUD's UI diversity wasn't the only expansion. The project also explored cloud-native architecture through .NET Aspire, Microsoft's framework for building distributed, observable applications.

In early 2024, an AspireHost project explored decomposing the monolithic application into orchestrated services with service discovery, OpenTelemetry instrumentation, and multi-project orchestration. By May 2024, the Aspire artifacts were removed from the main solution — the experiment succeeded in demonstrating cloud-native concepts, but keeping the artifacts risked confusing developers who came for straightforward MVC examples.

This decision exemplified the project's philosophy: demonstrate advanced concepts, but don't let them overwhelm the fundamentals. The Aspire influence persisted in the application's health check infrastructure at

/health
and
/status
.

The Navigation Experience

All seven CRUD implementations are accessible from a unified dropdown menu:

Employees (dropdown)
├── Single Page (DataTables SPA)
├── Employee (jQuery AJAX)
├── MVC Employee (Traditional)
├── Employee Razor (Page-centric)
├── React
├── htmx
├── Vue
└── Blazor

The consistent navigation makes comparison effortless — click "React", try the CRUD operations, click "htmx", try the same operations, and observe the differences in behavior, responsiveness, and browser network activity.

The Educational Impact

The seven-paradigm showcase transforms SampleMvcCRUD from a code reference into a decision-making tool. A team evaluating frontend approaches for a new project can:

  1. Clone the repository and run it locally
  2. Try each implementation with the same employee data
  3. Open browser developer tools and compare network traffic
  4. Examine the source code to understand implementation complexity
  5. Make an informed architectural decision based on hands-on experience

The tradeoffs aren't theoretical — they're visible, tangible, and running on localhost.

Additional Resources


UISampleSpark Series

This is part 4 of a five-part series tracing the evolution of SampleMvcCRUD from a simple CRUD tutorial to a comprehensive web UI exploration platform.

  1. A Developer's Swiss Army Knife — The founding philosophy and core architecture
  2. Seven Years of .NET Modernization — Navigating the annual .NET upgrade cycle
  3. Constitution-Driven Development — How governance and AI transformed project quality
  4. Seven UI Paradigms, One Backend (this article) — Comparing seven frontend approaches side by side
  5. Modern DevOps as a Living Reference — Containerization, CI/CD, and cloud deployment

Project Links: GitHub | Live Demo | Docker Hub