Documentation/Building Your App/Dynamic UI
Dynamic UI
How Supero turns your schema into working tables, forms, and detail views automatically, and the real UI-schema mechanism you use to customize labels, view modes, per-field renderers, and conditional form fields.
Overview
Every entity you define gets a working admin UI for free: a searchable, sortable data table, create/edit forms with validation, and a detail view — all generated directly from your schema, with no frontend code to write or maintain.
The generation is live, not a one-time scaffold. Add a field to a schema and it shows up as a new form field, table column, and detail-view row the next time the app loads — because the UI is built at runtime from the same field list your schema serves through the CRUD API, not from a generated-once template.
json
{
"name": "Product",
"parent_type": "tenant",
"attributes": [
{ "name": "price", "type": "float", "mandatory": true },
{ "name": "in_stock", "type": "boolean", "default-value": true },
{ "name": "status", "type": "string", "values": ["draft", "active", "discontinued"], "default-value": "draft" }
]
}With nothing else declared, price becomes a number input and a numeric table column, in_stock becomes a checkbox, and status becomes a dropdown populated from values — covered in full in the next section. This page documents that default mapping, then the schema you author when you want more control than the default gives you.
Generated UI Components
Every schema you define is rendered through the same four building blocks in the generated admin console.
| Component | Driven By | Behavior |
|---|---|---|
| Data table | Every field on the schema | Sortable columns, a free-text search box that matches across every field, a status filter, and incremental "load more" paging. |
| Create / edit form | Fields + their required flags | A modal form scoped to the record type. Required fields block submission client-side; validation errors returned by the API are shown inline on the corresponding field. |
| Detail view | Fields + related records | A read-only layout of every field (image and file fields get their own hero/gallery treatment instead of a text row), plus any related child records you wire up. |
| Dashboard / Kanban | Numeric fields, or an explicit board view | Stat cards and simple charts computed by summing or averaging a numeric field across a schema’s records, and a drag-friendly board when a record type’s view is set to kanban. |
ℹ️ Schema-driven
None of these four are separately configured components — they all read the same field list your schema exposes. Adding a field to the schema is enough to get a new column, a new form field, and a new detail row, with zero UI code.
Field Type to Default Control
Every field declares a type, and each type maps to a default control with no configuration required. For the full set of primitive types and the aliases accepted for each, see Schema Reference. Two composite forms also get their own control: a named type (a field whose type points at a separate type-kind schema, e.g. "type": "Address") and an inline enum (a string field with a values list).
| Schema Type | What It Declares | Default Control |
|---|---|---|
| string | Free-form text | Text input (a field whose name contains "description" is rendered as a multi-line textarea automatically). |
| integer | Whole numbers | Number input. |
| float | Decimal numbers | Number input with a decimal step — used for money, quantities, measurements. |
| boolean | A true/false flag | Checkbox. |
| Named type, e.g. "type": "Address" | An embedded value structure defined as its own type-kind schema | A structured editor for that type — see Named & System Types below. |
| Enum: "type": "string", "values": [...] | A fixed set of allowed values | Dropdown select, populated from the values list. |
That table covers the types with a distinctive control, not the complete set of primitives. date and datetime fields get a date or date-time picker, and json fields get a structured key-value editor. Multi-value fields aren’t a separate type — set list: true (or IsList: true) on any field to store an array of values. And references between objects (references[]) are a distinct schema construct, not a field type. See Schema Reference for the full primitive list and for how references are declared and queried.
Named & System Types
Beyond the primitive types, a field’s type can name a type-kind schema you defined yourself, or one of the platform’s built-in system types. Several of the built-in system types map to a dedicated editor rather than falling back to a plain text input.
| System Type | Typical Use | Editor |
|---|---|---|
| Address | A street/city/postal-code structure | address_editor |
| Currency | A money amount | currency_editor |
| PhoneNumber | A phone number | phone_editor |
| An email address | email_editor | |
| GeoLocation | A latitude/longitude point | geo_point_editor |
| URL | A web link | url_editor |
| Image | A photo attachment (add list: true for a gallery) | image_upload_editor |
| File | A document attachment | file_upload_editor |
A type-kind schema you define yourself — a composite structure like a custom LineItem or Address variant made of your own fields — is edited as structured, nested data rather than a single input; give it its own field editor (see Field Editors & Conditional Display below) if the generic structured editor isn’t what you want.
Customizing With UI Schemas
The default table, form, and detail view work with zero configuration. When you want a specific label, icon, section grouping, alternate view mode, or per-field display formatting, you author a second schema alongside your data schema — a UI schema — rather than annotating the data schema’s fields directly.
A UI schema is marked with its own schema_type, "ui". Like a type schema, it can use extends and it never gets CRUD endpoints or system fields of its own — it’s configuration for the runtime, not a record you create. In practice, you extend the platform’s base record UI (extends: "supero_ui:base_record_ui") and point it at your data schema with ui_for.
json
{
"name": "order_admin_ui",
"namespace": "shop",
"schema_type": "ui",
"parent_type": "tenant",
"extends": "supero_ui:base_record_ui",
"display_name": "Orders Admin UI",
"attributes": [
{ "name": "ui_for", "type": "string", "default": "shop:order" },
{ "name": "surface", "type": "string", "default": "admin_panel" },
{ "name": "label", "type": "string", "default": "Orders" },
{ "name": "icon", "type": "string", "default": "shopping-bag" },
{ "name": "section", "type": "string", "default": "Sales" },
{ "name": "subtitle", "type": "string", "default": "All orders across every channel" },
{ "name": "default_view_mode", "type": "string", "default": "table" },
{ "name": "available_view_modes", "type": "string", "list": true, "default": ["table", "kanban", "detail"] },
{ "name": "header_actions", "type": "list", "default": [
{ "label": "+ New Order", "variant": "primary", "action_id": "create_record" }
]},
{ "name": "field_renderer__status", "type": "dict", "default": {
"renderer": "status_pill",
"config": { "color_map": { "pending": "amber", "shipped": "blue", "delivered": "green", "cancelled": "red" } }
}},
{ "name": "field_renderer__total_amount", "type": "dict", "default": { "renderer": "currency" } },
{ "name": "detail_extras", "type": "list", "default": [
{ "child_schema": "line_item", "label": "Line Items", "limit": 20 }
]},
{ "name": "featured_fields", "type": "string", "list": true, "default-value": ["status", "total_amount", "customer_name"] }
]
}| Key | Purpose |
|---|---|
| ui_for | The qualified data-schema name this UI schema configures, e.g. "shop:order". |
| surface | Which surface this configuration applies to: admin_panel, live_app_authed, live_app_public, mobile_admin, or mobile_public. |
| label / icon / section / subtitle | Navigation label, icon, grouping section, and a short description shown under the section title. |
| default_view_mode / available_view_modes | Which view loads by default, and which of the platform’s view modes (list, table, grid, detail, card, form, kanban, time_grid, calendar, seat_grid, map, cart, timeline, thread, metric_card, inline_edit, gallery, comparison) this record type supports. |
| header_actions | Buttons shown above the list — each is a { label, variant, action_id } entry. |
| featured_fields | An ordered list of field names to surface prominently in cards and compact views. |
| detail_extras | Related child schemas to show inline on the detail view, e.g. line items under an order. |
| field_renderer__<field_name> | A display-time renderer override for one field: { "renderer": "...", "config": {...} }. |
Field Editors & Conditional Display
Create/edit forms are customized one field at a time with a field editor entry. Anything you don’t override keeps its default control from the field-type table above.
| Key | Purpose |
|---|---|
| editor_type | The editor to use for this field, e.g. text, number, currency, date, boolean, enum, reference, address, image_upload, file_upload. |
| field_name | The data-schema field this editor binds to. |
| label / tooltip / help_text / placeholder | Form copy shown with the field. |
| required / readonly / autofocus | Overrides the schema’s own required flag for this form context, locks the field, or focuses it on open. |
| icon / width_hint | An icon shown with the field, and how many columns it spans: narrow, normal, wide, or full. |
| depends_on | Sibling field names this editor re-evaluates against whenever one of them changes. |
| visible_when_field_equals | A condition — a JSON object mapping a sibling field name to the value it must equal — for this field to be shown at all. |
json
{
"editor_type": "date",
"field_name": "delivery_date",
"label": "Delivery date",
"required": true,
"depends_on": ["fulfillment_method"],
"visible_when_field_equals": "{\"fulfillment_method\": \"shipping\"}"
}In this example, the delivery_date field only appears once fulfillment_method is set to shipping — the same conditional-field idea a schema author needs, expressed through the real key the platform reads.
💡 Editor and renderer vocabulary
Common editor_type / renderer_type values: text, textarea, number, currency, percentage, boolean, date, time, email, phone, url, password, rating, color_picker, code, markdown, rich_text, json, key_value, tag_input, multi_select, enum, reference, address, geo_point, image_upload, and file_upload.
Surfaces & Visibility
Supero doesn’t generate one layout and reflow it for every context — it generates distinct surfaces, and a UI schema configures each independently for the same record type.
| Surface | What Renders There |
|---|---|
| admin_panel | The tenant admin console — full CRUD, every field, every record. |
| live_app_authed | The signed-in customer-facing app. |
| live_app_public | The public, unauthenticated storefront or landing pages. |
| mobile_admin | The admin experience on mobile. |
| mobile_public | The public/customer experience on mobile. |
A separate customer_surface value controls how a record type shows up specifically in the signed-in customer app:
- •browse — a read-only catalog tab.
- •mine — an owner-scoped "My X" tab, create-enabled.
- •shop — a catalog that feeds cart/checkout.
- •hidden — never gets its own tab (picker-only entities, or back-office records like an internal coupon list).
If you omit customer_surface, the runtime classifies the record automatically based on whether tenant users can create it and whether it looks like a transactional entity — set it explicitly whenever the automatic guess isn’t what you want.
Next steps
Related pages:
- •Schema Reference — the full fields[]/references[] format, primitive types, and how extends inheritance works.
- •Anatomy of a Supero App — how schemas, seed data, and the generated UI fit together in a Supero app.
- •Managing Data — creating, reading, updating, and deleting records through the API, SDKs, and admin console.
On this page