Files
Calendink/Provider/tdd/frontend_technology_choices.md

7.0 KiB

Frontend Technology Choices for ESP32-S3 Provider

Authored by Claude Opus 4.6 Date: 2026-03-02


1. Goal

Create a lightweight web frontend for the ESP32-S3 Provider that displays system information and provides a reboot control. The frontend must compile to static files small enough to live in ESP32 flash memory (~4-8kB gzipped).

2. Chosen Stack

Technology Version Role
Svelte 5 UI framework (compiles away at build time)
Vite Latest Build tool and dev server
TailwindCSS 4 Utility-first CSS (JIT, tree-shaken at build time)
vite-plugin-singlefile Latest Inlines all JS+CSS into one HTML file
gzip Compresses final output for ESP32 flash

3. Why Svelte Over Other Frameworks

Svelte is a compile-time framework. Unlike React or Vue, it does not ship a runtime to the browser. The compiler transforms .svelte components into highly optimized vanilla JavaScript at build time. This means:

  • No virtual DOM overhead — direct DOM manipulation in the compiled output.
  • Tiny bundle size — a simple Svelte app compiles to a few kilobytes of JS, compared to React's ~40kB runtime or Vue's ~30kB.
  • Ideal for constrained devices — the ESP32-S3 has limited flash (typically 4-16MB shared with firmware) and serves files over a potentially slow connection.

Other frameworks considered:

Framework Runtime Size Why Not
React ~42kB min+gzip Ships a runtime; too heavy for ESP32
Vue ~33kB min+gzip Ships a runtime; unnecessary for a simple dashboard
Vanilla JS 0kB No tooling, harder to maintain, no component model
Svelte ~2kB min+gzip Chosen — compiles away, smallest output

Vanilla JS was considered but Svelte provides a component model, reactivity, and maintainability with effectively zero runtime cost.

4. Why TailwindCSS Over Vanilla CSS

TailwindCSS v4 uses a Just-In-Time (JIT) compiler that scans source files and generates only the CSS classes actually used. For a simple dashboard page, this produces under 5kB of CSS (before gzip).

Benefits over vanilla CSS:

  • Guaranteed small output — unused styles are never generated (no manual purging needed).
  • Faster development — utility classes directly in markup, no switching between files.
  • Consistent design system — spacing, colors, typography are constrained to a scale.
  • Dark mode — built-in dark: variant with zero extra effort.

TailwindCSS is a build-time tool only. It produces a plain .css file — no runtime, no JavaScript.

5. Why Single-File Build

The vite-plugin-singlefile plugin inlines all compiled JavaScript and CSS directly into index.html. The ESP32 serves one file instead of multiple.

Benefits for ESP32:

  • One HTTP request — avoids multiple round-trips over a slow embedded server.
  • No filesystem overhead — SPIFFS/LittleFS have per-file overhead; one file is optimal.
  • No filename length issues — LittleFS has a 32-character filename limit; Vite normally generates hashed filenames like assets/index-a1b2c3d4.js that can exceed this.
  • Simpler backend — the HTTP server only needs one route to serve the frontend.

6. Why Not ESP32-SvelteKit

ESP32-SvelteKit is a full-featured framework that bundles SvelteKit + TailwindCSS + DaisyUI with an ESP32 backend. It was evaluated and rejected for the following reasons:

Incompatible Build System

ESP32-SvelteKit requires PlatformIO with the Arduino framework. Our project uses native ESP-IDF with CMake. Adopting ESP32-SvelteKit would require rewriting all backend code (connect.cpp, led_status.cpp, main.cpp) to use Arduino APIs. This is not acceptable.

Unnecessary Complexity

ESP32-SvelteKit includes features we do not need:

  • MQTT client
  • JWT-based user authentication
  • WiFi provisioning UI (we already have our own wifi/ethernet logic in connect.cpp)
  • DaisyUI theming system
  • PsychicHttp web server
  • ArduinoJson

These add weight to both the firmware and the frontend bundle without benefit for our use case (a simple system info dashboard).

Licensing Concern

The ESP32-SvelteKit backend code is licensed under LGPL v3, which has copyleft implications for linked code. Our custom setup has no such constraints.

What We Borrow From It

Although we don't use the framework, we adopt the same proven patterns:

  • Svelte + TailwindCSS + Vite as the frontend toolchain
  • Gzipped static files served from ESP32 flash
  • The concept of embedding frontend into firmware binary for OTA (future task)

7. Build Pipeline

Source Files                Build Steps                     Output
─────────────              ───────────                     ──────
src/App.svelte    ─┐
src/app.css        ├─→  Vite + Svelte Compiler  ─→  dist/index.html  (~15-25kB)
src/lib/api.js    ─┘     + TailwindCSS JIT               │
                          + vite-plugin-singlefile         ▼
                                                    gzip compression
                                                          │
                                                          ▼
                                                    dist/index.html.gz  (~4-8kB)
                                                          │
                                                          ▼
                                                    ESP32 Flash (SPIFFS/LittleFS)
                                                    or embedded in firmware binary

8. Environment-Based API URL

The frontend calls the ESP32's REST API. The base URL depends on the environment:

Environment VITE_API_BASE Reason
Development (PC) http://<ESP32_IP> Frontend on PC, API on ESP32 over network
Production (ESP32) (empty string) Frontend and API on same device, relative URLs

This is handled via Vite's .env.development and .env.production files. The value is baked in at compile time — zero runtime overhead.

9. OTA Considerations (Future)

When OTA updates are implemented, the frontend will be embedded into the firmware binary as a C header array:

  1. npm run build:esp32dist/index.html.gz (~4-8kB)
  2. A script converts the gzipped file to a C const uint8_t[] array
  3. The array is compiled into the firmware binary
  4. OTA flashes one binary that includes both firmware and frontend

This avoids needing a separate SPIFFS partition for the frontend and ensures the UI always matches the firmware version.

10. Summary

We chose Svelte + TailwindCSS + Vite because they are build-time tools that produce the smallest possible static output, ideal for ESP32 constraints. We build to a single gzipped HTML file of ~4-8kB. We did not adopt ESP32-SvelteKit because it requires PlatformIO/Arduino, which is incompatible with our native ESP-IDF project, and includes unnecessary complexity for our simple dashboard.