Svelteesp32
Embed Any Web App in Your ESP32 — One Binary, Zero Filesystem Hassle Turn your Svelte, React, Angular, or Vue frontend into a single C++ header file. Serve beautiful web interfaces directly from ESP32/ESP8266 flash memory with automatic gzip compression, ETag caching, and seamless OTA updates.
Install / Use
/learn @BCsabaEngine/Svelteesp32README
svelteesp32 
Embed Any Web App in Your ESP32 — One Binary, Zero Filesystem Hassle
Turn your Svelte, React, Angular, or Vue frontend into a single C++ header file. Serve beautiful web interfaces directly from ESP32/ESP8266 flash memory with automatic gzip compression, ETag caching, and seamless OTA updates.
<p align="center"> <img src="svelteesp32.png" alt="svelteesp32" /> </p>Why SvelteESP32?
The problem: Traditional approaches like SPIFFS and LittleFS require separate partition uploads, complex OTA workflows, and manual compression. Your users end up managing multiple files, and your CI/CD pipeline becomes a mess.
The solution: SvelteESP32 compiles your entire web application into a single C++ header file. One firmware binary. One OTA update. Done.
Key Benefits
- Single Binary OTA — Everything embedded in firmware. No partition juggling, no separate uploads.
- Automatic Optimization — Build-time gzip compression with intelligent thresholds (>1KB, >15% reduction).
- Smart Caching — Built-in SHA256 ETags deliver HTTP 304 responses, slashing bandwidth on constrained devices.
- CI/CD Ready — Simple npm package that slots into any build pipeline.
- Zero Runtime Overhead — Data served directly from flash. No filesystem reads, no RAM allocation.
- 4 Web Server Engines — PsychicHttpServer V2, ESPAsyncWebServer, Arduino WebServer, and native ESP-IDF supported.
SvelteESP32 vs Traditional Filesystem
| Feature | SvelteESP32 | SPIFFS / LittleFS | | --------------------- | --------------------------------- | ---------------------------------------- | | Single Binary OTA | ✓ Everything in firmware | ✗ Separate partition upload required | | Gzip Compression | ✓ Automatic at build time | Manual or runtime compression | | ETag Support | ✓ Built-in SHA256 + 304 responses | Manual implementation required | | CI/CD Integration | ✓ One npm command | Complex upload_fs tooling | | Memory Efficiency | Flash only (PROGMEM/const arrays) | Filesystem partition + overhead | | Performance | Direct byte array serving | Filesystem read latency | | Setup Complexity | Include header, call one function | Partition tables, upload tools, handlers |
Best for: Single-binary OTA, CI/CD pipelines, static web UIs that ship with firmware.
Consider SPIFFS/LittleFS for: User-uploadable files, runtime-editable configs, dynamic content.
Quick Start
npm install -D svelteesp32
After building your frontend (Vite/Rollup/Webpack):
npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=true
Include in your ESP32 project:
#include <PsychicHttp.h>
#include "svelteesp32.h"
PsychicHttpServer server;
void setup() {
server.listen(80);
initSvelteStaticFiles(&server);
}
That's it. Your entire web app is now embedded and ready to serve.
Just want production-safe defaults?
npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h \ --etag=true --gzip=true --cachetime-html=0 --cachetime-assets=31536000ETags for instant 304s, gzip for smaller transfers,
no-cachefor HTML so updates are always picked up, and 1-year caching for content-hashed JS/CSS assets.
What's New
- v2.3.0 —
--cachetime-htmland--cachetime-assetsfor per-type cache control (e.g.no-cachefor HTML, 1-year for content-hashed JS/CSS) - v2.2.0 — SPA routing catch-all (
--spa) for client-side routers on all four engines - v2.1.0 — New Arduino WebServer engine (
-e webserver), dependency updates - v2.0.0 — BREAKING: PsychicHttpServer V2 is now the default
psychicengine. Thepsychic2engine has been removed. Dry run mode, C++ identifier validation, improved MIME type warnings - v1.16.0 — Size budget constraints (
--maxsize,--maxgzipsize) - v1.15.0 —
--basepathfor multiple frontends (e.g.,/admin,/app) - v1.13.0 — npm package variable interpolation in RC files
- v1.12.0 — RC file configuration support
- v1.11.0 — File exclusion patterns
- v1.9.0 — Native ESP-IDF engine
Requirements
- Node.js >= 20
- npm >= 9
Installation & Usage
Install
npm install -D svelteesp32
Generate Header File
Choose your web server engine:
# PsychicHttpServer (recommended for ESP32)
npx svelteesp32 -e psychic -s ./dist -o ./esp32/svelteesp32.h --etag=true
# ESPAsyncWebServer (ESP32 + ESP8266)
npx svelteesp32 -e async -s ./dist -o ./esp32/svelteesp32.h --etag=true
# Arduino WebServer (ESP32, synchronous, no dependencies)
npx svelteesp32 -e webserver -s ./dist -o ./esp32/svelteesp32.h --etag=true
# Native ESP-IDF
npx svelteesp32 -e espidf -s ./dist -o ./esp32/svelteesp32.h --etag=true
Build Output
Watch your files get optimized in real-time:
[assets/index-KwubEIf-.js] ✓ gzip used (38850 -> 12547 = 32%)
[assets/index-Soe6cpLA.css] ✓ gzip used (32494 -> 5368 = 17%)
[favicon.png] x gzip unused (33249 -> 33282 = 100%)
[index.html] x gzip unused (too small) (472 -> 308 = 65%)
[roboto_regular.json] ✓ gzip used (363757 -> 93567 = 26%)
5 files, 458kB original size, 142kB gzip size
../../../Arduino/EspSvelte/svelteesp32.h 842kB size
Automatic optimizations:
- Gzip level 9 compression when beneficial (>1KB, >15% size reduction)
- Duplicate file detection via SHA256 hashing
- Smart skip of pre-compressed files (.gz, .br) when originals exist
ESP32 Integration
PsychicHttpServer V2 (Recommended)
#include <PsychicHttp.h>
#include "svelteesp32.h"
PsychicHttpServer server;
void setup() {
server.listen(80);
initSvelteStaticFiles(&server); // One line. Done.
}
ESPAsyncWebServer
#include <ESPAsyncWebServer.h>
#include "svelteesp32.h"
AsyncWebServer server(80);
void setup() {
initSvelteStaticFiles(&server);
server.begin();
}
Arduino WebServer (built-in, no dependencies)
#include <WebServer.h>
#include "svelteesp32.h"
WebServer server(80);
void setup() {
initSvelteStaticFiles(&server);
server.begin();
}
void loop() {
server.handleClient();
}
Native ESP-IDF
#include <esp_http_server.h>
#include "svelteesp32.h"
httpd_handle_t server = NULL;
void app_main() {
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
httpd_start(&server, &config);
initSvelteStaticFiles(server);
}
Working examples with LED control via web interface: Arduino/PlatformIO | ESP-IDF
What Gets Generated
The generated header file includes everything your ESP needs:
//engine: PsychicHttpServer V2
//config: engine=psychic sourcepath=./dist outputfile=./output.h etag=true gzip=true cachetime=0 espmethod=initSvelteStaticFiles define=SVELTEESP32
//
#define SVELTEESP32_COUNT 5
#define SVELTEESP32_SIZE 468822
#define SVELTEESP32_SIZE_GZIP 145633
#define SVELTEESP32_FILE_INDEX_HTML
#define SVELTEESP32_HTML_FILES 1
#define SVELTEESP32_CSS_FILES 1
#define SVELTEESP32_JS_FILES 1
...
#include <Arduino.h>
#include <PsychicHttp.h>
#include <PsychicHttpsServer.h>
const uint8_t datagzip_assets_index_KwubEIf__js[12547] = {0x1f, 0x8b, 0x8, 0x0, ...
const uint8_t datagzip_assets_index_Soe6cpLA_css[5368] = {0x1f, 0x8b, 0x8, 0x0, 0x0, ...
const char * etag_assets_index_KwubEIf__js = "387b88e345cc56ef9091...";
const char * etag_assets_index_Soe6cpLA_css = "d4f23bc45ef67890ab12...";
// File manifest for runtime introspection
struct SVELTEESP32_FileInfo {
const char* path;
uint32_t size;
uint32_t gzipSize;
const char* etag;
const char* contentType;
};
const SVELTEESP32_FileInfo SVELTEESP32_FILES[] = {
{ "/assets/index-KwubEIf-.js", 38850, 12547, etag_assets_index_KwubEIf__js, "text/javascript" },
{ "/assets/index-Soe6cpLA.css", 32494, 5368, etag_assets_index_Soe6cpLA_css, "text/css" },
...
};
const size_t SVELTEESP32_FILE_COUNT = sizeof(SVELTEESP32_FILES) / sizeof(SVELTEESP32_FILES[0]);
...
// File served hook - override with your own implementation for metrics/logging
extern "C" void __attribute__((weak)) SVELTEESP32_onFileServed(const char* path, int statusCode) {}
void initSvelteStaticFiles(PsychicHttpServer * server) {
server->on("/assets/index-KwubEIf-.js", HTTP_GET, [](PsychicRequest * request, PsychicResponse * response) {
if (request->hasHeader("If-None-Match") &&
request->header("If-None-Match").equals(etag_assets_index_KwubEIf__js)) {
response->setCode(304);
SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 304);
return response->send();
}
response->setContentType("text/javascript");
response->addHeader("Content-Encoding", "gzip");
response->addHeader("Cache-Control", "no-cache");
response->addHeader("ETag", etag_assets_index_KwubEIf__js);
response->setContent(datagzip_assets_index_KwubEIf__js, 12547);
SVELTEESP32_onFileServed("/assets/index-KwubEIf-.js", 200);
return response->send();
});
// ... more routes
}
Supported Web Server Engines
| Engine | Flag | Best For | Platform |
| ------------------------ | -------------- | ---------------------------- | --------------- |
| PsychicHttpServer V2 | -e psychic | Maximum performance | ESP32 only |
| ESPAsyncWebServer | -e async | Cross-platform compatibility | ESP32 + ESP8266 |
| Arduino WebServer | -e webserver | No dependencies, simplicity | ESP32 only |
| Native ESP-IDF | -e espidf | Pure ESP-IDF projects | ESP32 only |
Recommendation: For ESP32-only projects, use PsychicHtt
