End-to-end demo: the shop
demo/mc-sui-shop-spring-demo is a full Spring Boot CRUD application against Postgres —
the most complete worked example of semantic-ui in action.
Run it
cd semantic-ui/demo/mc-sui-shop-spring-demo
cp .env.docker.example .env.docker
./start-postgres.sh
mvn spring-boot:run
# then open http://localhost:8080
What it demonstrates
- Product list with search + pagination + per-row actions.
- View / edit / delete via Post/Redirect/Get.
- SSR mode (no JS) and SPA mode (load the bootstrap script), switchable from a header dropdown — the same backend code serves both.
- Three themes (light, dark, SBB), swappable via the same dropdown.
- A toast after every save, a dialog for inline-edit, and a customer tab showcasing the tab section.
Everything above comes from one set of controllers returning UiPage trees —
there is no separate SPA codebase.
Tests as a spec
The smoke tests in PageRenderSmokeTest lock the markup behaviour of every
interaction, so the demo doubles as an executable specification of how the
renderers should behave.
Client-only variant
demo/mc-sui-shop-client-demo is the mirror image: the same shop shape with
no backend at all. It is a static site — the build assembles a self-contained
folder (the compiled core renderer + stylesheets, plus an index.html and an
app.js of plain UiNode literals) that you can serve from any static host.
Open the client-only shop ↗ — running right here on this documentation site, which is the point: it needs nothing but a static host. The Postgres variant above cannot be published this way.
To run it yourself:
mvn -f semantic-ui/core/mc-semantic-ui-core/pom.xml install -DskipTests
mvn -f semantic-ui/demo/mc-sui-shop-client-demo/pom.xml process-resources
cd semantic-ui/demo/mc-sui-shop-client-demo/target/dist
python3 -m http.server 8085 # then open http://localhost:8085
Every interaction is a browser-only trigger: products live in
an in-memory array (persisted to localStorage), and there is no fetch.
INVOKE— search, the product detail dialog, delete and reset call JS handlers registered withbus.registerClientHandler(...), each returning aUiPatch(orUiPage) the bus applies just like a server response.PATCH— the "catalog" list fills a detail pane with zero round-trip: each item carries its finishedUiPatchinline in the trigger, so clicking it needs no handler and no server.- field
onChange— a checkbox enables/disables the delivery-address field by firing anINVOKEtrigger that reads its state and patches the field. UiUpload— a drag-and-drop drop zone whoseonUploadis anINVOKEtrigger: the dropped image'sFileis read viactx.filesand previewed with an object URL, again with no server.
It is the same node vocabulary and the same renderer as the Spring version — proof that the tree is the only contract, and the backend is optional.