Documentation

adrs/040-html-first-ui-architecture.md

ADR 040: HTML-First UI Architecture with HTMX

Status: Accepted
Date: 2025-11-13
Decision Makers: Daniel Castonguay
Tags: ui, architecture, htmx, hateoas, frontend

Context

The current AssetsTrack UI is built with React/Next.js, resulting in a complex frontend architecture with significant technical debt:

  • 167,539 lines of code (excluding node_modules and generated clients)
  • 33,031 lines of JSX and 31,957 lines of JavaScript
  • Complex state management across client and server
  • Build complexity (webpack, babel, TypeScript compilation)
  • Dual rendering strategies (SSR + CSR) with hydration complexity
  • Large JavaScript bundles impacting initial load time
  • Framework lock-in (React ecosystem)

While React excels at highly interactive, real-time applications, 95% of Dynaplex UI needs are traditional CRUD operations and business workflows - a use case where React's complexity provides diminishing returns.

The web component proof-of-concept using HTMX has demonstrated a fundamentally simpler approach that aligns better with our application requirements.

Decision

We will adopt an HTML-first, hypermedia-driven architecture for future UI development in Dynaplex, using:

  1. HTMX - For progressive enhancement of HTML with AJAX capabilities
  2. Hyperscript - For simple client-side behaviors (when needed)
  3. Fluid.NET (Liquid templates) - For server-side HTML rendering
  4. Tailwind CSS - For styling (consistent with current UI)
  5. Standard REST principles - HTML as the hypermedia format

This represents a return to web fundamentals enhanced with modern progressive enhancement techniques.

Architectural Principles

1. HATEOAS (Hypermedia as the Engine of Application State)

The server controls application state and sends hypermedia (HTML) that describes available actions. The client displays HTML and knows how to request more HTML.

Example:

<!-- Server includes only permitted actions -->
<div class="region-row">
  <span>North America</span>

  <!-- Rendered only if user has permission -->
  <button hx-get="/regions/123/edit"
          hx-target="#region-123"
          hx-swap="outerHTML">
    Edit
  </button>

  <!-- Rendered only if user has permission and no dependencies exist -->
  <button hx-delete="/regions/123"
          hx-target="#region-123"
          hx-swap="outerHTML swap:500ms"
          hx-confirm="Delete this region?">
    Delete
  </button>
</div>

Business logic (permissions, validation, available actions) lives on the server. The client simply renders what it receives.

2. Progressive Enhancement

All functionality works with standard HTML. HTMX enhances the experience when JavaScript is available.

Example:

<!-- Works without JavaScript (standard form POST) -->
<!-- Enhanced with JavaScript (AJAX POST with HTML response) -->
<form action="/regions" method="POST"
      hx-post="/regions"
      hx-target="#region-list"
      hx-swap="beforeend">
  <input name="name" required />
  <button type="submit">Create</button>
</form>

This ensures:

  • Accessibility (screen readers, keyboard navigation)
  • SEO (search engines see real HTML)
  • Resilience (works even if JavaScript fails to load)
  • Testing (can test with standard HTTP tools)

3. Server-Side Rendering (SSR) by Default

Every request renders HTML on the server. No client-side rendering, no hydration, no virtual DOM.

Data Flow:

Browser → HTTP Request → Server → Query DB → Render Liquid Template → HTML → Browser → Display

vs. React/SPA:

Browser → HTTP Request → Server → JSON → Browser → Parse JSON → Update State → Render Virtual DOM → Reconcile → Update Real DOM → Display

One step vs. six steps.

4. Locality of Behavior

Behavior is co-located with markup using HTMX attributes and Hyperscript.

HTMX Example:

<button hx-delete="/regions/123"
        hx-confirm="Delete this region?"
        hx-target="#region-123"
        hx-swap="outerHTML">
  Delete
</button>

Everything about this button's behavior is visible in the HTML. No hunting through JavaScript files for event handlers.

Hyperscript Example:

<div _="on click toggle .expanded on me">
  Click to expand
</div>

Simple behaviors expressed in readable, inline scripts.

5. Minimal JavaScript

JavaScript is used sparingly, only when necessary:

  • HTMX library (~14KB gzipped) for AJAX enhancements
  • Hyperscript library (~10KB gzipped) for simple behaviors
  • Tailwind CSS (optional, can be pre-processed)

Total JavaScript: ~25KB vs. ~500KB+ for typical React apps

Consequences

Positive

  1. Simplicity

    • Fewer files (templates + API handlers vs. components + services + state management)
    • Single language for business logic (C#)
    • No build step for templates (Liquid files parsed at runtime)
    • Simpler mental model (request/response vs. reactive state)
  2. Performance

    • Smaller initial payload (~25KB JS vs. ~500KB+)
    • Faster time-to-interactive (HTML is immediately renderable)
    • Less client-side processing (no virtual DOM reconciliation)
    • Better perceived performance (progressive enhancement)
  3. Developer Experience

    • See changes immediately (edit template, refresh browser)
    • Easier debugging (view source shows actual DOM)
    • Less context switching (stay in C#)
    • Fewer dependencies to manage (no npm, webpack, babel)
  4. Maintainability

    • Less code to maintain
    • Simpler architecture to understand
    • Standard HTTP/HTML (works with all tooling)
    • No framework lock-in
  5. SEO & Accessibility

    • Real HTML from the start (no client-side rendering delay)
    • Works without JavaScript (progressive enhancement)
    • Better for screen readers (semantic HTML)
    • Standard browser behaviors (back button, bookmarks, etc.)

Negative

  1. Full Page Refreshes for No-JS Users

    • Mitigation: This is acceptable for our B2B application
    • Most business users have JavaScript enabled
    • Graceful degradation ensures functionality
  2. Learning Curve

    • Team needs to learn HTMX, Hyperscript, and Liquid
    • Mitigation: All three are simpler than React ecosystem
    • Documentation and patterns provided
  3. Not Suitable for All Use Cases

    • Real-time collaboration (Google Docs-style): Still use React
    • Heavy client-side computation: Still use React
    • Offline-first requirements: Still use React
    • Mitigation: 95% of our UI is CRUD operations - HTMX is ideal
  4. Server Load

    • More server-side rendering (vs. client-side rendering in SPA)
    • Mitigation: Servers are cheap, developer time is expensive
    • Modern servers easily handle HTML rendering
    • Can add caching if needed
  5. Migration Effort

    • Existing React UI can't be migrated immediately
    • Mitigation: Incremental migration, not big-bang
    • Current UI continues with pattern updates (19 pages remain)
    • New features can be built with HTMX

Neutral

  1. Different Development Patterns
    • Need to think in hypermedia, not state machines
    • Server controls UI flow, not client
    • This is a mental model shift, not a limitation

Implementation Strategy

Phase 1: Complete assettrak-ui Pattern Migration (Q4 2024)

Finish migrating the 19 remaining pages in the React UI to modern patterns:

  • Hierarchical permissions
  • API factory pattern
  • Status code response handling
  • Direct property accessors
  • Hard deletes

Rationale: Ship functional product by end of year

Timeline: ~15-19 days of focused work (already tracked, proven patterns)

Phase 2: HTMX Feature Parity (Q1 2025)

Build out the web component to feature parity with assettrak-ui:

  • Core CRUD pages (regions, locations, asset types, assets)
  • Authentication/authorization fully integrated
  • Permission system working
  • Common UI components (tables, forms, modals)

Deliverable: Parallel HTMX UI that can handle core workflows

Phase 3: Gradual Cutover (Q2 2025)

Start directing users to HTMX UI for new features:

  • New features built in HTMX only
  • Gradually deprecate React pages as HTMX equivalents stabilize
  • Maintain React UI for complex pages if needed

Phase 4: React Retirement (Q3-Q4 2025)

Migrate remaining React pages or sunset entirely:

  • Evaluate which pages still need React (if any)
  • Migrate remaining CRUD pages to HTMX
  • Remove React infrastructure

Technology Choices

HTMX (HTML Extensions)

Why: Adds AJAX capabilities to HTML declaratively

  • Mature (v2.0, actively maintained)
  • Small (~14KB gzipped)
  • No build step required
  • Works with any backend
  • Well-documented

Example:

<button hx-delete="/api/regions/123"
        hx-confirm="Are you sure?"
        hx-target="#region-123"
        hx-swap="outerHTML">
  Delete
</button>

Hyperscript (Client-Side Behaviors)

Why: Simple, readable scripting for UI interactions

  • Natural language syntax
  • Event-oriented (designed for DOM events)
  • Inline (co-located with markup)
  • Small (~10KB gzipped)

Example:

<div _="on click toggle .open on #sidebar">
  Toggle Sidebar
</div>

Fluid.NET (Liquid Templates)

Why: Safe, designer-friendly templating for C#

  • No arbitrary code execution (safe)
  • No compilation needed (fast iteration)
  • Well-known syntax (Liquid is popular)
  • Good for designers (simple syntax)

Example:

<div class="region-list">
  {% for region in regions %}
    <div class="region-row">
      <span>{{ region.name }}</span>
      {% if user.can_edit %}
        <button hx-get="/regions/{{ region.id }}/edit">Edit</button>
      {% endif %}
    </div>
  {% endfor %}
</div>

Tailwind CSS (Styling)

Why: Consistent with existing UI, utility-first CSS

  • Already familiar to team
  • No context switching for styling
  • Can be pre-processed (no runtime cost)

Risks & Mitigations

Risk: Team unfamiliarity with HTMX

Mitigation:

  • Comprehensive documentation created (architecture guide, patterns, migration guide)
  • Start with simple pages (low risk)
  • Gradual adoption (learn as we go)

Risk: HTMX limitations discovered

Mitigation:

  • Proof-of-concept already validates core use cases
  • Can still use React for edge cases
  • HTMX is not an all-or-nothing decision

Risk: Performance concerns with server rendering

Mitigation:

  • Benchmark early
  • Add caching if needed (response caching, fragment caching)
  • Modern servers easily handle HTML rendering
  • Can optimize specific hot paths

Risk: Migration taking longer than expected

Mitigation:

  • Not rushing migration
  • Incremental, not big-bang
  • React UI continues to work during transition

Measuring Success

Key Metrics

  1. Code Reduction

    • Target: 70% reduction in frontend code
    • Measurement: Lines of code (excluding libraries)
  2. Bundle Size

    • Target: <50KB total JavaScript
    • Measurement: Network tab in browser DevTools
  3. Time to Interactive

    • Target: <1 second on 3G
    • Measurement: Lighthouse scores
  4. Developer Velocity

    • Target: 50% faster feature development
    • Measurement: Time from request to production
  5. Bugs

    • Target: 30% reduction in frontend bugs
    • Measurement: Issue tracker

Success Criteria

After 6 months of HTMX usage (Q2 2025):

  • Bundle size reduced by 90%+
  • Time to interactive improved by 60%+
  • New feature development 40%+ faster
  • Frontend bug count reduced by 25%+
  • Developer satisfaction increased (survey)

Alternatives Considered

Alternative 1: Continue with React, Improve Patterns

Pros:

  • No learning curve
  • No migration effort
  • Familiar to team

Cons:

  • Doesn't address fundamental complexity
  • Still shipping large JavaScript bundles
  • Still dealing with React ecosystem churn
  • Pattern improvements only reduce technical debt, don't eliminate it

Rejected because: Doesn't solve the root problem (unnecessary complexity for CRUD apps)

Alternative 2: Vue.js

Pros:

  • Simpler than React
  • Better documentation
  • Less complex build setup

Cons:

  • Still a SPA framework (same fundamental complexity)
  • Still shipping large JavaScript bundles
  • Still managing client-side state
  • Just trades one framework for another

Rejected because: Doesn't fundamentally simplify the architecture

Alternative 3: Svelte

Pros:

  • Compile-time framework (smaller bundles)
  • Simpler syntax than React
  • Less boilerplate

Cons:

  • Still client-side rendering
  • Still managing client-side state
  • Less mature ecosystem
  • Team learning curve

Rejected because: Still adds complexity where simplicity is possible

Alternative 4: Server Components (React)

Pros:

  • Leverage existing React knowledge
  • Server-side rendering with React
  • Progressive enhancement possible

Cons:

  • Very new (unstable)
  • Still requires React knowledge
  • Still complex build setup
  • Ties us deeper into React ecosystem

Rejected because: Too new, still complex, doesn't address root issues

References

Internal Documentation

Conclusion

The HTML-first approach with HTMX represents a strategic simplification of Dynaplex's UI architecture. By embracing hypermedia and progressive enhancement, we align our technology choices with our actual requirements: building robust, maintainable business applications for CRUD operations and workflows.

This is not anti-JavaScript or anti-React. It's pro-simplicity and pro-right-tool-for-the-job. React excels at highly interactive applications. HTMX excels at server-driven applications. Dynaplex is fundamentally server-driven.

We will:

  1. Ship the current React UI by end of year (pattern migration complete)
  2. Build the HTMX UI in parallel during Q1 2025
  3. Gradually transition users to the HTMX UI
  4. Retire the React UI when HTMX reaches feature parity

The future of Dynaplex UI is HTML-first, server-driven, and dramatically simpler.


Signed: Daniel Castonguay
Date: 2025-11-13
Status: Accepted