# ESPUI puts a web interface on an ESP32 with no server, and the memory budget is the real constraint

> A library that serves a Skeleton CSS interface with zepto.js over web sockets from the microcontroller's own memory or flash, with Arduino style calls that create and update controls. The default mode works out of the box, and the LittleFS path exists because the default one eats your program memory.

**s00500/ESPUI** — A simple web user interface library for ESP32 and ESP8266

- Repository: https://github.com/s00500/ESPUI
- Website: https://valencia.lbsfilm.at/midterm-presentation/
- Stars: 1,135 · Forks: 207
- Language: C++
- License: NOASSERTION
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/s00500-espui

## Everything is served from the chip, which is the whole point

ESPUI is a library for making a web-based user interface for your projects using the ESP8266 or the ESP32, and it uses web sockets so you can create and update elements on your GUI through multiple devices like phones and tablets.

The key property is that there is no server. The README puts it plainly: it is your simple solution user interface without the need of internet connectivity or any additional servers, and it can be used in standalone access point mode with all resources loaded directly from the ESP's memory.

That is the design decision that distinguishes this from every other approach to putting a screen on a microcontroller. A common alternative is a small local web server, but that means something has to be listening, and a device on a home network is not always reachable from a phone. ESPUI runs an access point on the chip itself, serves the page and the assets from the chip, and holds the connection open with a web socket for the state changes.

The frontend is based on Skeleton CSS and on zepto.js, which the README describes as a jQuery-like lightweight library for handling events. The heart of the library is ESPAsyncWebserver, and the communication between the ESP and the browser works using web sockets.

The library runs on any kind of ESP8266 and ESP32, with NodeMCU and AI Thinker given as examples, and the primary language is C++. The repository metadata reports the licence as one GitHub could not classify, so the LICENSE file at the root is the authoritative text, and the README's own summary of the library's licence status is worth reading before you ship a product with it in it.

## Two ways to store the assets, and one of them costs you program memory

ESPUI serves several files to the browser to build up its web interface, and this can be done in two ways, PROGMEM or LITTLEFS.

When `ESPUI.begin()` is called the default is serving files from memory, and ESPUI should work out of the box. That is the reason you can get a working interface with almost no setup, and it is also the reason the alternative exists: if this causes your program to use too much memory, you can burn the files into the LITTLEFS filesystem on the ESP.

There are two ways to do the filesystem route. You can use the ESP file upload tool, or you can use the library function `ESPUI.prepareFileSystem()`. The recommended path is the simple one: open the example sketch called prepareFileSystem, run it on the ESP, give it up to 30 seconds and watch the status on the Serial Monitor, and the library will create all the needed files.

The payoff comes after that. From now on you only need to do this again when there is a library update, or when you want to use another chip. When you upload your normal sketch and do not call the `ESPUI.prepareFileSystem()` function, the compiler will strip out all the unnecessary strings that are already saved in the chip's filesystem, and you have more program memory to work with.

That is the mechanism worth understanding. The interface assets are the same either way, but in PROGMEM mode they are linked into your program and count against the flash you have for code. In LITTLEFS mode they live in a filesystem partition and the compiler drops them, so you get the space back.

The dependency list explains the file system requirement. For the ESP32 the library uses lorol's LittleFS_esp32, and the AsyncTCP library, while for the ESP8266 it uses ESPAsyncTCP. The other two dependencies are ESPAsyncWebServer, which is the core, and ArduinoJson, which was last tested with version 6.10.0.

That last note is a compatibility warning in practice, since ArduinoJson's major versions have changed APIs.

## Controls are created with one call and update through the same object

The API is Arduino style in the sense that you call a method on the library object and pass a callback.

In the arduino `setup()` routine the interface can be customised by adding UI elements, and this is done by calling the corresponding library methods on the library object `ESPUI`. The example given is `ESPUI.button("button", &myCallback)`, which creates a button in the interface that calls the `myCallback(Control *sender, int eventname)` function when changed.

The event model has one subtlety that catches everyone the first time. All buttons and items call their callback whenever there is a state change from them, which means a button calls the callback when it is pressed and again when it is released. To separate different events, an integer with the event name is passed to the callback, and the documentation says to handle it in a `switch` and `case` statement.

So a single callback function serves every control of a given type, and the integer tells you which control and which event. That is a compact scheme, and it means the callback signature is uniform across the whole interface.

There is an extended callback form with three parameters, `myCallback(Control *sender, int eventname, void * UserParameter)`, where the UserParameter is provided as part of the `ESPUI.addControl` method set and allows the user to define contextual information that is presented to the callback in an unmodified form. It is also possible to use a lambda function in the callback parameter, which the README describes as allowing the user to define, in a more C++ way, contextual information in any form.

The element list is what you get. Label, Button, Switch, Control pad, Slider, Text Input, Date, Time, Colour and Password Input, Number input, Option select, Separator, Time, Graph with a partial implementation, and File Display.

The controls are grouped into the core set and the advanced features, which are Dynamic Visibility, Inline Styles, Disabling Controls, Grouped controls, Wide controls, the experimental Graph, and a Captive Portal. The Captive Portal one is worth noting for the standalone access point mode, because it is what makes a device show a configuration page when you connect to it with no network at all.

## Advanced features cover visibility, grouping and the captive portal

The advanced features section is where the library goes beyond a form, and the captive portal is the one worth understanding first.

Dynamic visibility means a control can be shown or hidden at runtime, which is how a single page adapts to the state of the device rather than presenting every control at once. Inline styles let you set the appearance of an element from the Arduino side without shipping a second stylesheet, and the README credits inline CSS styles to a specific contributor along with the transport layer rework that made it practical.

Grouped controls and wide controls are layout options, so a set of related inputs can be presented as a group and a control can span the full width of the layout rather than sitting in a grid cell. Separators let you divide a long control list into sections, which matters on a phone screen where scrolling a single undifferentiated column is a usability problem rather than a cosmetic one.

Disabling controls is listed in the advanced features and is also on the roadmap, which tells you the state of that particular feature honestly.

The graph is marked experimental and the roadmap says fully implement graphs, so the graph element exists in a partial state. The partial implementation note in the element list is consistent with that, and it is worth knowing before you design a dashboard around it.

The other two roadmap items are expanding the number input features to cover floats, and support for enabling and disabling controls. Both are ordinary, well-understood requests, and the fact that they are still open tells you the project is maintained at a steady pace rather than in bursts.

## The repository is a library with an Arduino manifest and a build helper

The layout shows the two installation paths in the repository itself. There is a `library.json` and a `library.properties` and a `keywords.txt`, which are the files the Arduino IDE library manager needs, and a `src/` directory with the C++ implementation.

There is a `data/` directory and a `lang/` directory for the localisation of the interface strings, and an `ESPUI_blocks.js` file at the root, which is the JavaScript side of the blocks used by the interface.

The examples directory has six entries, and each one isolates a feature rather than demonstrating the whole thing. There is `completeExample/`, `completeLambda/` for the lambda callback form, `gui-generic-api/` for the generic API, `gui/`, `prepareFilesystem/` for the flash preparation step, and `tabbedGui/` for the tabs feature.

There is also a `pio_examples/` directory, which is the PlatformIO counterpart, and a `tools/` directory, plus a `.clang-format` at the root for the C++ formatting.

The installation documentation recommends both PlatformIO and the Arduino IDE. With PlatformIO you add the library to `lib_deps`.

```
lib_deps =
    ESPUI
    ESP Async WebServer
    ESPAsyncTCP # (or AsyncTCP on ESP32)
    LittleFS_esp32 # (ESP32 only)
```

The comment on the LittleFS line, ESP32 only, is the dependency list again, and PlatformIO does not resolve it for you the way the Arduino library manager does.

For the Arduino IDE, you go to Sketch, then Include Library, then Library Manager, search for ESPUI and install it. If you cannot use the Library Manager, you can download the repository archive and follow the instructions to manually install libraries.

The contributed features section at the top of the README attributes the additions to individuals, which is a good sign about how the project is maintained. Tabs, the generic API, min and max on the slider and the OptionList are credited to @eringerli, inline CSS styles, separators, grouped and wide controls, the transport layer rework, the time control and vertical controls to @iangray001, the time, date, password and colour input types to @pcbbc, and delayed response support, fragmented control transfer, the extended callback and the file display element to @MartinMueller2003.

The last release was 2.2.4 on 2024-07-16, after 2.2.3 and 2.2.2 in August 2023, and the last push to the repository was on 2026-04-30 with the repository not archived.

## Conclusion

Adopt ESPUI if you are building an ESP32 or ESP8266 project that needs a browser interface on a phone or tablet, and you want the whole thing served by the microcontroller with no server. Do not adopt it if your device cannot spare the flash and RAM, since the default PROGMEM mode carries the interface assets in program memory, and the LittleFS route adds a filesystem you have to maintain. Verify first which of the two storage modes you are using, that your ArduinoJson version is compatible since it was last tested with 6.10.0, and that the controls you need exist, since the roadmap still lists support for enabling and disabling controls as unfinished.

## FAQ

### What is ESPUI and what hardware does it run on?

ESPUI is a C++ library for making a web-based user interface on ESP8266 and ESP32 boards, using web sockets to create and update controls from phones and tablets. It needs no internet connectivity and no additional server, and can run in standalone access point mode with all resources loaded from the chip's memory.

### What dependencies does ESPUI need?

ESPAsyncWebServer, ArduinoJson which was last tested with version 6.10.0, ESPAsyncTCP on ESP8266, AsyncTCP on ESP32, and lorol's LittleFS_esp32 on ESP32. All of them must be installed alongside the library.

### What is the difference between PROGMEM and LITTLEFS in ESPUI?

By default ESPUI.begin() serves the interface files from memory, which works out of the box but counts against your program memory. Burning the files into LITTLEFS with the prepareFileSystem example, or the ESP file upload tool, lets the compiler strip out those strings, giving you more program memory to work with.

### How do ESPUI callbacks distinguish between controls?

Every control calls the same callback on any state change, so a button fires on press and again on release, and the callback receives an integer event name to separate them in a switch statement. An extended callback form adds a void pointer UserParameter for contextual information, and lambdas are accepted as callbacks too.

### How do I install ESPUI?

With PlatformIO, add ESPUI and its dependencies to lib_deps. With the Arduino IDE, use Sketch, Include Library, Library Manager and search for ESPUI, or download the repository archive and install it manually following the Arduino instructions.

## Sources

- [Issues](https://github.com/s00500/ESPUI/issues)
- [Project website](https://valencia.lbsfilm.at/midterm-presentation/)
- [README](https://github.com/s00500/ESPUI/blob/master/README.md)
- [Releases](https://github.com/s00500/ESPUI/releases)
- [s00500/ESPUI on GitHub](https://github.com/s00500/ESPUI)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/s00500-espui
