SkillAgentSearch skills...

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/Svelteesp32

README

svelteesp32 image

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.

Changelog

<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=31536000

ETags for instant 304s, gzip for smaller transfers, no-cache for HTML so updates are always picked up, and 1-year caching for content-hashed JS/CSS assets.


What's New

  • v2.3.0--cachetime-html and --cachetime-assets for per-type cache control (e.g. no-cache for 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.0BREAKING: PsychicHttpServer V2 is now the default psychic engine. The psychic2 engine has been removed. Dry run mode, C++ identifier validation, improved MIME type warnings
  • v1.16.0 — Size budget constraints (--maxsize, --maxgzipsize)
  • v1.15.0--basepath for 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

View on GitHub
GitHub Stars166
CategoryDevelopment
Updated4h ago
Forks14

Languages

TypeScript

Security Score

85/100

Audited on Mar 29, 2026

No findings