UI Pages
UI Pages are custom dashboard pages composed of widgets. Unlike standard control pages, UI Pages provide complete flexibility to design custom layouts with charts, gauges, cameras, weather, and any custom visualization.
Overview
UI Pages enable:
- Custom Dashboards: Create purpose-built information displays
- Widget Composition: Combine multiple widgets on a single page
- Responsive Layouts: Widgets flow and resize based on screen size
- Live Preview: See changes in real-time while editing
- Page Templates: Copy and reuse successful page designs
The admin Dashboard is itself a UI page (admin_dashboard, seeded at boot). Its Customize button deep-links straight here (?page=admin_dashboard), so you reorder and configure the admin home dashboard the same way you edit any other UI page.
Page Management
The UI Pages interface is organized with selectors and action buttons at the top, followed by content areas.
Selectors
UI Page Selector
- Dropdown of all UI pages
- Select to view and edit page
- Shows page title and name
Actions
Rename Page
- Change page title and name
- Updates all references
Copy Page
- Duplicate entire page including all widgets
- Creates independent copy
- Useful for templates
Delete Page
- Remove page (confirmation required)
- Removes all page-widget assignments
- Widgets themselves are not deleted
Save Page
- Save title, enabled state, and override route changes
- Saving is unified: both Save buttons (the page toolbar and the Widget Configuration card header) save the page settings — including Columns — and the widget currently loaded in Widget Configuration, in that order. There is no way to lose an unsaved layout change by saving a widget.
Preview Page
- Opens live preview modal
- Shows actual rendering with theme
- Supports theme switching in preview
- Interactive widgets (if live mode enabled)
Page Configuration
Page Title
- Display name shown in navigation
- Appears in page tabs/selector
- User-friendly text
Page Enabled
- Toggle to show/hide page
- Disabled pages:
- Don't appear in navigation
- Cannot be accessed
- Configuration preserved
Override Route
- Optional Svelte route to override
- Advanced: Replace built-in page with custom page
- Example: Override
/controls/lightingwith custom lighting page - Leave blank for normal pages
Layout — Bento Grid
Every page is a bento grid: widgets fill equal-height grid cells in a fixed column count (default 3, configurable via the Columns field in Page Settings). In portrait, tablets render 2 columns while phones stack to a single full-width column — Column Span is ignored on phones, but Row Span still applies.
- Per-widget Column Span and Row Span (in Widget Configuration, Layout group) let a widget cover multiple cells; containers support the same spans.
- The grid cell defines each widget's size. Auto-size stays on by default and shrinks oversized content to fit the cell (turn it off per widget for natural-size content).
Pages created before the bento-grid unification (the classic percent-width flow layout) are converted automatically at server startup: the column count is inferred from the page's widget widths and spans are written for anything larger than one cell. The migration also removes the retired classic keys (percent/portrait sizing, margins, page flow settings) from stored configs — the grid is the only layout, with no revert path. Check the startup log for migrated ui_page to bento grid lines; pages flagged with check: had widths that didn't snap cleanly and are worth eyeballing, then adjusting via spans.
Widgets Section
Manage widgets assigned to the selected page.
Available Widgets (Left)
Dropdown selector showing all UI widgets:
- Shows widget name and type
- Search/filter capabilities
- Select widget to add to page
Current Page Widgets (Right)
Grid or list of widgets on this page:
- Widget name
- Widget type
- Container assignment
- Enabled state
- Sort order
Widget Actions
Add Widget to Page
- Select widget from dropdown
- Choose container (if page has multiple containers)
- Click Add
- Widget appears on page
Remove Widget from Page
- Select widget in current page widgets list
- Click Remove
- Widget removed from page (widget itself not deleted)
Reorder Widgets
- Drag boxes in the Layout wireframe below the page widgets list: drop on the left or right half of another box to place the widget before or after it. The bento grid flows widgets in sort order, so reordering is how a widget moves around the page.
- A container group drags as one unit; widgets inside a container drag to reorder within that container (drop zones follow the container's flow direction). Moving a widget into or out of a container is done with the Container select in Widget Configuration.
- The ▲ ▼ buttons do the same thing one step at a time, and remain the path on touch screens.
Configure Widget
- Widget-specific settings
- Grid spans (Column Span / Row Span)
- Container assignment
- Enabled state
Clicking a widget in Available Widgets edits the global widget definition; clicking one in Page Widgets (or the layout wireframe) edits that page's instance. A scope badge in the Widget Configuration header shows which one is loaded — Global — used on N pages (orange when the widget is placed anywhere) or Page instance. Saving a global definition that is in use asks for confirmation, since it updates the defaults on every page placing that widget; page instances that have been saved with their own config keep those values.
Widget config is edited through a labeled form rather than raw JSON. The Widget section at the top of the card holds the instance fields — Title, Name, Container assignment, and Enabled — with Save in the card header, and the form's setting groups are collapsible (click a group header to fold it). The card body scrolls internally when the form is long, so the layout wireframe beside it stays visible while editing. Even widgets that declare no settings of their own always show the common container fields — Column Span, Row Span, Show Container, Auto-size, CSS Class, Inline Style, Content Style, and Transition — so layout is editable for every widget. Config keys the schema doesn't declare (custom widget settings) still render as typed fields under an Other group, and a Raw JSON toggle remains for direct editing.
Inline Style vs. Content Style: Inline Style applies CSS to the widget's outer container, while Content Style applies CSS to the inner content area and overrides its default centering — e.g. justify-content: flex-start; align-items: flex-start to top/left-align a widget's content. Inline styles set here beat the scoped class rule, so any property you set wins. Both default to empty (no change).
For css_class, style, inner_style, and transition, blanking the field writes an explicit empty value so the cleared setting overrides the widget's merged-in default rather than falling back to it. Other fields fall back to their default when cleared.
Widget Containers
A widget container groups several widgets into one box that spans grid cells and is styled as a unit — resize the container's spans and the whole group follows. Widgets assigned to a container flow inside it per its Children settings. Containers are global: create one once and reuse it on any page (note the spans apply everywhere it's used). A widget with no container assignment simply occupies its own grid cells (the common case).
Creating Containers
Containers are created and edited on the Containers tab of the UI Pages editor:
- Click New and enter a title — the name is generated from it; both stay editable
- Configure layout in Container Config — a labeled form showing the options a container actually honors: Column/Row Span, CSS Class, and Inline Style. The Children group controls how assigned widgets flow inside the container — Direction, Gap, Align Items, Justify, Wrap, and Padding, the same flex controls (and icon buttons) as the Widget Designer's Section element. Containers saved before these options existed keep their original centered-row behavior until a value is changed. The form shares its catalog with the widget config form, and a live preview beside it shows the container at its configured spans on a simulated 3-column page with stand-in widget chips that follow the flow settings. Unknown keys are preserved and stay editable via the Raw JSON toggle.
- Click Save, then assign widgets to the container (below)
Assigning Widgets
Each page widget has a Container selector in its configuration. Pick a container to render the widget inside it; leave it empty to place the widget directly on the page. Widgets in the same container keep their own individual configs — the container only supplies the shared box and the flow of its children.
Layout Wireframe
Under the Page Widgets list, a live wireframe sketches the page layout as you work: one named box per spanned grid cell group, and containers drawn as outlined groups with their member widgets flowing inside per the container's Children settings.
- Click a box to select that widget for editing (the selected widget is outlined).
- The widget being edited follows your unsaved form values live — change a span, the Container assignment, or the Enabled toggle and the wireframe updates before you save.
- Disabled widgets appear ghosted (dashed, faded) — the rendered page hides them entirely.
- The Landscape / Tablet / Phone toggle previews each device class, and each button shows its column count — e.g. Landscape (3), Tablet (2), Phone (1). Landscape follows the page's Columns setting live (change the field and the count updates before saving), tablet portrait is always 2 columns (column spans still apply, clamped to the track count), and phone stacks a single column with every widget full-width. Row spans apply on every device. These breakpoints come from the runtime grid and are the same for all themes.
The wireframe is schematic — for a full render with real widget content, use the Preview button in the page toolbar.
Page Preview
The preview feature allows real-time visualization:
Opening Preview
Click Preview Page button to:
- Open modal with live page render
- Apply selected theme
- Show all widgets in their configured layout
- See actual sizing and positioning
Preview Controls
Page Link
- Shows the shareable deep-link for the selected page (
<origin>/?page=<name>) — the same URL the app navigates to when opening this page - Click the link or the copy button to copy it to the clipboard
- Paste it to bookmark the page or send it to another client
Theme Selector
- Dropdown of available themes
- Switch themes to see how page looks
- Applies the theme's CSS and its Layout & Presentation flags (state-dot rectangle buttons, scene chips) — the preview renders as that theme's UIs will
- Doesn't change actual UI theme, just preview
Interactive Mode
- Toggle to enable widget interactivity
- When enabled:
- Widgets load real data
- Controls are functional
- Charts update live
- When disabled:
- Static preview only
- Faster rendering
Close
- Close preview modal
- Returns to edit mode
Preview Use Cases
- Layout Testing: Verify widget arrangement
- Theme Compatibility: Test page with different themes
- Responsive Design: Resize browser to test different screen sizes
- Client Approval: Show clients the page before deploying
- Iteration: Make changes, preview, refine, repeat
Page Templates
Copying Pages
Duplicate successful page designs:
-
Select page to copy
-
Click Copy Page
-
Enter new page name
-
New page created with:
- All widgets copied
- Same layout
- Same configuration
- Independent from original
-
Customize new page as needed
Use Cases:
- Floor plan pages (copy layout, change zones)
- Dashboard templates (copy structure, change data sources)
- Client dashboards (copy template, customize branding)
Built-In Page Templates
GEM includes starter templates:
- Dashboard - Overview with status widgets
- Monitoring - Charts and graphs
- Security - Cameras and alarm status
- Energy - Power monitoring and usage
Copy and customize these templates rather than starting from scratch.
Assigning Pages to UIs
UI Assignment
Pages can be assigned to specific UIs:
- In page configuration, select Assign to UIs
- Choose which UIs should have this page
- Save
Options:
- All UIs - Page appears in every UI
- Specific UIs - Page appears only in selected UIs
- No UIs - Page exists but isn't accessible (draft state)
Navigation
Assigned pages appear in UI navigation:
- Page tabs (if UI has page navigation)
- Menu items
- Home screen tiles
Override Routes
Advanced feature to replace built-in pages:
Use Case
Replace standard /controls/lighting with custom page:
- Create UI page with custom lighting interface
- Set Override Route:
/controls/lighting - Enable page
- When users navigate to Lighting control, they see your custom page instead
Considerations
- Only override if custom page is truly better than built-in
- Maintain expected functionality in override
- Test thoroughly
- Document customizations
Widget Layout
Responsive Grid
Widgets are arranged in a bento grid:
- Desktop / landscape: the page's Columns setting (default 3), equal-height rows
- Tablet portrait: 2 columns
- Phone: single full-width column (Column Span ignored, Row Span still applies)
Widget Sizing
Each widget occupies grid cells:
- Column Span: how many columns wide (clamped to the page's column count)
- Row Span: how many rows tall
Example Layouts (3-column page):
Full Width Banner:
Column Span: 3
Row Span: 1
Featured Widget Beside Tiles:
Widget 1: Column Span 2, Row Span 2
Widgets 2-5: Column Span 1, Row Span 1
(Widget 1 dominates the top-left, tiles fill around it)
Even Tile Grid:
All widgets: Column Span 1, Row Span 1
(Three per row, uniform cells — the default)
Common Page Types
Status Dashboard
Monitor system at a glance:
Widgets:
- System status widget (online/offline devices)
- Security status widget (armed/disarmed, sensors)
- Climate widget (all temperatures)
- Energy widget (current usage)
- Weather widget
Layout (2-column page):
[ System Status ] [ Security ]
[ Climate ] [ Energy ] [ Weather ] (3-column page: span 1 each)
Camera Dashboard
Security camera monitoring:
Widgets:
- Multiple camera feed widgets
- Motion detection status
- Recording status
- PTZ controls
Layout:
[ Camera 1 - 50% ] [ Camera 2 - 50% ]
[ Camera 3 - 50% ] [ Camera 4 - 50% ]
The built-in Camera widget accepts a comma-delimited list of camera zone ids and renders them as a responsive grid — one widget placement covers the whole wall. The AI Assistant's create_camera_wall skill builds the entire composition (page + camera widget + placement + UI links) from a single request like "build me a Cameras page with every camera on the site".
Energy Monitoring
Power and energy analytics:
Widgets:
- Real-time power chart
- Daily usage chart
- Cost calculator
- Top consumers list
- Solar production (if applicable)
Floor Plan Dashboard
Interactive floor plan:
Widgets:
- Floor plan widget (custom)
- Zone status overlays
- Quick controls
- Room temperature display
Best Practices
-
Page Purpose: Each page should have a clear purpose
- Don't combine unrelated widgets
- Keep pages focused
-
Widget Count: Limit widgets per page
- Desktop: 4-8 widgets
- Tablet: 3-5 widgets
- Mobile: 2-3 widgets
-
Layout Balance: Distribute screen space appropriately
- Important widgets get more space
- Support widgets use less space
- Avoid one widget dominating
-
Performance: Consider widget update frequency
- Too many live-updating widgets can slow page
- Stagger update intervals
- Use static widgets where appropriate
-
Responsive Design: Test on multiple screen sizes
- Preview with different window sizes
- Ensure widgets remain readable when stacked
- Test on actual target devices
-
Color Coordination: Widgets should complement theme
- Use theme colors in custom widgets
- Avoid clashing colors
- Consider dark/light theme variants
-
Loading States: Widgets should handle loading gracefully
- Show loading indicators
- Don't block entire page
- Fail gracefully if data unavailable
Troubleshooting
Page Not Appearing in UI
Check:
- Page Enabled: Page must be enabled
- UI Assignment: Page must be assigned to UI
- User Permissions: User role allows access
- Navigation: Verify page appears in UI's navigation
Widget Not Showing on Page
Check:
- Widget Enabled: Widget must be enabled
- Page Assignment: Widget must be added to page
- Container: Widget assigned to correct container
- Size: Widget has reasonable size (not 0%)
- Widget Errors: Check widget itself works
Preview Not Loading
Check:
- Page Selected: Select a page first
- Widgets: Page has widgets assigned
- Theme: Theme is valid and loaded
- Browser: Try different browser
- Cache: Clear browser cache
Copy Failed
Check:
- Name Unique: New page name must be unique
- Permissions: User has permission to create pages
- Widgets Exist: All widgets on source page still exist
Override Not Working
Check:
- Route Format: Must match existing route exactly
- Page Enabled: Override page must be enabled
- Page Content: Page has appropriate widgets/content
- Cache: Clear browser cache and reload
Advanced Topics
Dynamic Page Loading
Pages can load different widgets based on:
- User role
- Time of day
- System mode
- Screen size
Requires custom page implementation or JavaScript in theme.
API Integration
Display external data in widgets:
- Weather data from API
- Stock prices
- News feeds
- Calendar events
- Smart home integrations
Configure in individual widgets.
Custom Page Components
For maximum flexibility, create custom page components:
- Develop Svelte component in
src/routes/ - Add as override route
- Full control over layout and behavior
Related Documentation
- UI Widgets - Creating and configuring widgets
- UIs - Managing user interfaces
- UI Themes - Page styling and theming
- UI Controls - Adding pages to navigation