Skip to content

Base Architecture: MPA with vite-ssg

This is an analysis of the architecture implemented in an internal private project, which combines vite-ssg for static site generation with the Nitro backend (Kotlin with Spring Boot at work). The goal is to create a high-performance web application that feels instantaneous to users, even on slow networks.

Executive Summary

The implemented architecture transforms the traditional Single-Page Application (SPA/CSR) concept into an MPA (Multi-Page Application) with superpowers. By leveraging a static build with vite-ssg, an ultra-fast HTML base is generated. A custom Nitro backend intercepts requests to "enrich" this static HTML with dynamic state before serving it. On the client, the application hydrates using this injected state, eliminating the need for initial client-side API calls. The result is a near-instant initial load, characteristic of an MPA, combined with fluid client-side navigation, characteristic of an SPA, thanks to the vue-router.

The Rendering and Loading Flow

The process is divided into three key phases:

1. Build Time (SSG)

  • Static Generation: vite-ssg pre-renders the pages into plain HTML files.
  • Initial Loading State: The static HTML includes a loading indicator (e.g., a progress bar) visible by default, as the application's initial isReady state is false at build time.
  • Asset Optimization: All assets are generated and optimized: critical CSS (beasties), metric-compatible font fallbacks to prevent CLS (fontless), and pre-compressed versions (.gz, .br) of JS/CSS files.

2. Page Request (Server-Side - Nitro)

  • Interception: A user request (e.g., to /some-page) is captured by a Nitro middleware.
  • Data Fetching: The Nitro handler executes the necessary business logic on the server. This includes verifying the user's session and fetching page data from the database, for example.
  • State Injection: The fetched data is collected, serialized into a JSON object, and injected into the static HTML (read from disk) inside a <script> tag, typically window.__INITIAL_STATE__.
  • Optimized Delivery: The server sends the "enriched" HTML to the browser, along with Link: rel=preload headers for critical assets.

3. Hydration (Client-Side - Vue)

  • Instant Render: The browser receives the HTML and immediately displays the page structure and the loading indicator. The LCP is extremely fast. This is a masterful level of perceived performance optimization. We're a step ahead of LCP and INP, and we're optimizing for Time to First Meaningful Paint and the user's sense of speed.
  • Resilient Hydration Logic:
    • The Vue application hydrates. The state store (e.g., Pinia) initializes and checks for the existence of window.__INITIAL_STATE__.
    • Happy Path: If __INITIAL_STATE__ exists, the store is populated with this data synchronously. The isReady condition is met, components render the data, and the loading indicator disappears. There is no client-side API roundtrip.
    • Fallback Path: If the state injection failed for any reason and __INITIAL_STATE__ is not found, the store detects its absence and triggers a fetch call to the backend API to get the data. The application then behaves like a traditional SPA, guaranteeing functionality at all times.

Key Principles and Benefits

This architecture combines multiple strategies to achieve an elite result:

  • Extreme Loading Performance: Achieves minimal TTFB and LCP by serving an enriched static file and preloading critical assets.
  • Efficient Hydration (No Roundtrip): Eliminating the first client-side API call is the single biggest performance optimization for a data-rich application.
  • Resilience: The fallback-to-fetch mechanism ensures the application remains functional even if the server-side injection process fails.
  • Hybrid Architecture ("MPA with Superpowers"): Delivers the benefits of an MPA (instant initial load) and a SPA (fluid client-side navigation without page reloads), creating a best-in-class user experience.

Released under the MIT License.