How We Built a Drag‑Drop Electronic Menu Builder with SVG & React
This article details the design and implementation of a customizable electronic menu system that evolved from static images to an H5‑based, SVG‑driven, React-powered solution, covering business requirements, technology selection, data binding, rendering, store isolation, and offline stability.
Background
To help readers understand what an electronic menu is, we first show the evolution of the Guming menu from paper to the current electronic version, from static images to H5 links.
Note: The image‑based electronic menu can also be reproduced 1:1 by dragging materials in the site builder.
Business Situation
Before the H5 version, the electronic screen displayed a single image. With 8,000 stores and multiple brands, several problems emerged:
Designers had to create 100‑200 menu images for each update, with no guarantee of accuracy.
Image menus could not reflect real‑time product availability, leading to out‑of‑stock orders.
Promotions could not be synchronized, reducing order efficiency and customer experience.
These issues motivated the creation of a rapid design and delivery system for brand designers.
Requirement Collection
Stakeholder demands included:
Department A: full menu with top‑new items, region‑specific updates.
Department B: simplified menu for new‑store activities.
Department C: promotion display and strike‑through pricing for mini‑program traffic.
Department D: real‑time product status and activity tags on the menu.
Conclusion: Fixed layouts cannot satisfy these varying needs.
Design Plan
Feature Preview
Designers are accustomed to Sketch or Photoshop. We built a drag‑and‑drop canvas that matches their workflow, allowing free‑form layout.
Implementation Principle
Technology Selection
For drag‑drop, zoom, and custom node configuration we considered Canvas and SVG. With moderate node counts (<100), SVG proved superior, leading us to choose Antv‑X6.
Canvas event handling is similar to raw JavaScript but lacks a proper event model, suffers from collision detection complexity, and cannot easily distinguish drag vs click.
SVG nodes are easier to customize; foreignObject enables embedding HTML/React components directly.
Custom Node Creation
Using foreignObject we can embed any HTML, for example:
<svg xmlns="http://www.w3.org/2000/svg">
<foreignObject width="120" height="50">
<body xmlns="http://www.w3.org/1999/xhtml">
<p>Text.</p>
</body>
</foreignObject>
</svg>Each dragged item becomes a foreignObject containing a React, Vue, or plain HTML component. We use a stateless functional React component where View = Fn(props) , driven by props from a side panel.
Data‑Driven View Binding
Our backend uses Ant Design forms. By listening to onValuesChange and shouldUpdate, we propagate form changes to node props, achieving data‑driven view updates. Global state is managed with useContext and useReducer only.
Page Information Protocol
The exported JSON describes each node’s position, size, type, and props. A shortened example:
{
"cells": [
{
"position": {"x":0,"y":0},
"size": {"width":1920,"height":1080},
"view": "react-shape-view",
"shape": "background-node",
"data": {"backColor":"#fff","backType":"backColor"},
"id": "wrapper-node",
"zIndex": 0,
"children": ["aeb782ee-..."]
},
{
"position": {"x":610,"y":330},
"size": {"width":430,"height":134},
"view": "react-shape-view",
"shape": "custom-react-node",
"data": {"componentType":"list-product","type":"product"},
"id": "aeb782ee-...",
"parent": "wrapper-node"
}
]
}Other & Outlook
Language‑level performance can be improved with WebAssembly, allowing C++ code to run as binary in the browser, boosting load speed threefold.
Hardware‑level acceleration via WebGL enables GPU‑driven rendering for smoother graphics.
Electronic Menu Rendering
Pre‑questions
How to display the built template on the electronic screen?
How to isolate content per store?
How to ensure stable menu presentation?
Displaying Templates on Screens
The menu runs inside a third‑party Android app using a WebView. The native host provides store code, cache control, and version info. After the WebView loads, it calls a predefined JS entry point with these parameters to start the rendering workflow.
Node data includes position.x and position.y, which determine where each component is drawn, reproducing the exact design created in the builder.
Store‑Level Content Isolation
Different brands (e.g., Guming Tea, goottt) and store‑specific promotions require separate content. We introduce a “version” concept: a template is “finalized” into a hash‑named bundle containing both material JSON and component code. Stores bind to a specific hash, guaranteeing that even if the source template changes, the deployed version remains unchanged.
Ensuring Menu Stability
For stores with poor connectivity we package the menu as a PWA. A Service Worker intercepts requests, caching API responses in IndexedDB and static assets in CacheStorage, allowing offline or weak‑network operation without disrupting ordering.
Conclusion
As the number of stores grows, digitalization becomes critical. The project improves operational efficiency and customer experience by letting designers create menus without code, while the technical stack (React, Antv‑X6, SVG, PWA) ensures flexibility, scalability, and stability.
Technology should serve business needs, not the other way around. Low‑code solutions are valuable when non‑technical users need to build complex interactive interfaces, as demonstrated by this electronic menu system.
Goodme Frontend Team
Regularly sharing the team's insights and expertise in the frontend field
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
