SquareLine Studio for ESP32 LVGL: Visual GUI Design Without Hand-Coded Widgets
LVGL is the graphics library behind most of the custom touchscreen dashboards on this site, from the Cheap Yellow Display wall panel to the round GC9A01 smart display, but hand-coding every button, slider, and screen transition in C gets tedious fast once a UI has more than a couple of screens. SquareLine Studio is Espressif-recommended drag-and-drop editor for LVGL: you lay out widgets visually, wire up basic behavior with its event system, and export a ready-to-compile C project that drops straight into an Arduino sketch, PlatformIO project, or ESP-IDF component. This guide covers getting a project running, understanding what the exported code actually looks like, and wiring it into firmware you write yourself.
Setting up a project
When you create a new project, SquareLine Studio asks for a target: a specific display resolution, color depth, and the LVGL version to target (v8.x and v9.x behave differently enough that picking the wrong one for your installed LVGL library will cause compile errors). Match this to whatever display and LVGL version your firmware already uses — if you're driving an ILI9341 or ST7789 panel with TFT_eSPI or LovyanGFX and LVGL 8.3, say so at project creation, not after you've built out ten screens.
- Board/display resolution and orientation
- Color depth (16-bit is standard for most SPI TFTs)
- LVGL version (check what your project's lv_conf.h is actually built against)
- Export target: Arduino, ESP-IDF, or a generic C project
Building screens without writing widget code
The editor canvas mirrors LVGL's own object tree: everything is a widget nested inside a container, positioned either with fixed coordinates or flex/grid layout (the same layout engine LVGL uses at runtime, so what you see in the editor matches what runs on hardware far more closely than older LVGL GUI tools managed). Drop in panels, labels, buttons, sliders, bars, switches, arcs, and meter/gauge widgets from the component panel, then use the properties panel to set size, color, fonts, and states (pressed, checked, disabled) per widget — including separate styling per state, which is normally one of the more tedious parts of hand-written LVGL code.
Multi-screen projects are managed as named screens in a project tree, with a built-in simulator that renders the UI at your target resolution right in the editor so you can check layout and screen transitions before touching real hardware.
Events: where you actually write code
SquareLine Studio does not pretend to replace your firmware logic, and that's the right design call. Widgets get named, and you attach event triggers (pressed, value changed, screen loaded) to callback functions that are stubbed out in a generated ui_events.c/ui_events.h pair. The exported project leaves these stubs empty on every re-export, so your actual application code — reading a sensor, toggling a GPIO, publishing an MQTT message — lives safely outside the generated files and survives re-exports when you tweak the layout later.
Exported project structure
FileWhat's in itDo you edit it? ui.h / ui.cTop-level init function, screen and widget declarationsNo — regenerated every export ui_ScreenName.cPer-screen widget creation codeNo — regenerated every export ui_events.c / ui_events.hEmpty callback stubs you fill inYes — this is your code ui_helpers.c / ui_helpers.hShared animation/loading helpersNo images/ , fonts/Converted binary image and font dataNo — managed through the editor's asset panelWiring it into firmware
- Copy the exported ui folder into your Arduino sketch folder or PlatformIO lib/src directory.
- In your existing display setup code (the part that already initializes TFT_eSPI/LovyanGFX and calls lv_init()), call ui_init() once after LVGL itself is initialized.
- Keep your display flush callback and input device (touch) registration exactly as it already was — SquareLine Studio doesn't touch your driver layer, only the widget tree above it.
- Fill in the stub functions in ui_events.c with the actual behavior for each widget interaction.
- Re-export whenever you change layout in the editor; your event code in ui_events.c is preserved as long as you don't rename the underlying widgets.
Free vs. paid, and when it matters
SquareLine Studio ships a free Community edition that covers the core workflow described above: unlimited widgets, the simulator, and code export to Arduino/ESP-IDF/PlatformIO. The paid Pro tier adds things aimed at teams and larger products — Figma import for handing a polished design straight to firmware, additional export targets, and collaboration features. For a one-off maker dashboard or product prototype, the free tier is enough; if you're iterating from a designer's Figma mockups or working across a team, the Pro features start paying for themselves.
Practical tips
- Keep custom fonts and images small — every embedded asset eats into flash, and a handful of unoptimized full-color PNGs can blow past an ESP32's available flash partition fast. Convert photos to indexed/compressed formats where the editor allows it.
- Boards with PSRAM (ESP32-S3 with octal PSRAM, for example) make larger displays and smoother animations far more forgiving; a plain ESP32 or ESP32-C3 driving a high-resolution round display will struggle with frame buffer size long before SquareLine Studio's layouts become the bottleneck.
- Name your widgets meaningfully before wiring events — ui_Button3 six screens deep is a nightmare to maintain compared to ui_BtnStartPrint.
- Test layout changes in the built-in simulator first; a full flash-and-reboot cycle on real hardware is a slow way to catch a misaligned label.
For anyone who has already hand-built an LVGL dashboard the hard way, the time savings are immediate: what used to be an afternoon of fiddling with pixel offsets and style structs becomes a few minutes of dragging widgets and clicking through state previews, leaving the actual firmware logic as the only part left to write by hand.