Frontend configuration
The frontend is the @mr-aybee/genie-engine-ui npm package. Install it (plus the React peer deps)
and configure the whole app in code — there are no XML/config files on the client:
npm install @mr-aybee/genie-engine-ui react react-domimport { createGenieApp } from "@mr-aybee/genie-engine-ui";import "@mr-aybee/genie-engine-ui/styles.css";
const GenieApp = createGenieApp({ apiBase: "/api/v1/genie", // where the Genie.Engine API is mounted routing: "browser", navbar: { source: "endpoint" }, modules: { auth: true, notifications: true }, theme: { mode: "system" },});createGenieApp mounts GenieRouter, which maps /table|form|view/{name} to useGenieTable /
useGenieForm and the matching component. The UI fetches structure (SQL-free schema) once and
values per record, then merges them client-side — so a view defined in *.view.xml renders with
no frontend code. Auth affordances adapt automatically to the server’s enabled features (read from
GET /api/v1/auth/config). See Object endpoints for the exact
contract, and Components for the rendered surface.
Core config
Section titled “Core config”| Key | Purpose |
|---|---|
apiBase |
Base path where the Genie.Engine JSON API is mounted, e.g. /api/v1/genie. |
routing |
"browser" (History API paths like /table/Orders?Status=Active) or "hash". Browser routing requires the host to serve index.html for unknown paths (SPA fallback). Auth/account pages always use the hash. |
basePath |
Base path the app is mounted under (e.g. /genie). Defaults to /. |
modules |
Which feature modules to include (see below). Omitted flags default per module. |
navbar |
Where the sidebar navbar comes from (see below). |
theme |
Theme overrides — see Theming. |
schemaCacheTtlSeconds |
Lifetime of the in-memory schema (metadata) cache. null (default) holds it until reload; 0 disables it. |
debugRequests |
Logs every API request to the console (delta, caller hint, ⇊dedup marker). Off by default. |
handlers |
Render-time extension points (see below). |
auth |
Customizes the auth screens’ brand panel (see below). |
preloader |
Replaces the built-in loading splash (see below). |
Modules
Section titled “Modules”modules toggles which feature modules are compiled in (unused ones tree-shake out). Defaults:
auth, navbar, tables, forms, wizard, notifications and multiTenant are on;
assistant and headerSearch are off.
createGenieApp({ apiBase: "/api/v1/genie", modules: { auth: true, navbar: true, tables: true, forms: true, wizard: true, notifications: true, // header bell — see /platform/notifications/ assistant: true, // AI assistant panel — see /assistant/overview/ headerSearch: true, // global search — see /platform/search/ multiTenant: false, },});Some modules are documented in their own sections: the AI assistant, the global header search, and the notification bell under Notifications.
Navbar source
Section titled “Navbar source”navbar selects where the sidebar tree comes from. There is no auto-fallback between modes —
an endpoint error stays an error, and static always uses items. Omit the whole block to use
the package-provided default nav items (the built-in System module).
// (a) Server-driven: fetch the permission-filtered tree from GET /navbarcreateGenieApp({ apiBase: "/api/v1/genie", navbar: { source: "endpoint" } });
// (b) Host-supplied static tree (no API call)createGenieApp({ apiBase: "/api/v1/genie", navbar: { source: "static", items: myNavItems } });modules.navbar toggles whether the rail/sidebar renders at all; navbar.source selects where its
contents come from — they’re independent.
All theming — mode, accent/accents, logo/monogram, custom colours — flows through the theme block
on createGenieApp. The palette and token detail live in Theming.
createGenieApp({ apiBase: "/api/v1/genie", theme: { mode: "system", accent: "orbyn" } });Auth brand panel
Section titled “Auth brand panel”The login / forgot / reset screens render in a split layout: a gradient brand panel beside the
form. Customize it via the auth block — no fork needed (brandPanel wins over brandContent when
both are set):
createGenieApp({ apiBase: "/api/v1/genie", auth: { // (a) Replace the whole panel with your own markup. The host owns everything in the // `.auth-brand` slot; style it with host CSS. brandPanel: <MyBrandPanel />,
// (b) …or keep the default panel chrome and only swap its copy: brandContent: { heading: "Inventory operations, unified.", tagline: "Stock, suppliers, and purchase orders — in one workspace.", features: ["Real-time stock levels", "Supplier management", "Purchase-order delivery"], }, },});The panel’s logo/wordmark follows theme.logo and its colours follow the theme tokens. For a
fully-custom panel see the Inventory sample’s InventoryBrandPanel.tsx.
Loading splash (preloader)
Section titled “Loading splash (preloader)”While the session resolves and the authenticated app-shell chunk downloads, Genie shows a built-in
loading splash (branded via theme.logo). Replace it wholesale with the top-level preloader slot —
the same visual then covers the session-resolve, the lazy-shell Suspense fallback, and the post-login
hand-off:
createGenieApp({ apiBase: "/api/v1/genie", preloader: <MyPreloader />, // any ReactNode; own markup + CSS. Omit to keep the built-in splash.});Request de-duplication
Section titled “Request de-duplication”GenieApiClient collapses redundant calls so a single view/form load makes one network round-trip
per logical request — important under React StrictMode (which double-invokes effects in dev) and at
production scale:
- Metadata is cached in-memory (
schemaCacheTtlSeconds; held until reload by default). - In-flight coalescing dedupes concurrent identical requests: every
GETplus the read-style POSTs (/table,/form,/view,/field-dataset,/sequence-number) share one fetch while it is pending. There is no caching beyond the in-flight window — once a request settles, a later navigation re-fetches fresh data. Mutations (submit/delete-row/upload) are never coalesced. - Set
debugRequests: trueto log each request (with a+Nmsdelta, a caller-stack hint, and a⇊dedupmarker for coalesced calls) while diagnosing — leave it off in production.
Render extension handlers
Section titled “Render extension handlers”Pass a handlers block to customize table/form rendering without forking the components. Every
table/form render invokes these (all fields optional):
createGenieApp({ apiBase: "/api/v1/genie", handlers: { // Extra functions available to EVERY client-side expression — table `StyleClassesExpression` // and form field Required/Disabled/Hidden/Value rules. Merged over the built-ins (If/IIF, // sumLines, …), so a host entry can override a built-in of the same name. expressionFunctions: { Money: (n) => new Intl.NumberFormat(undefined, { style: "currency", currency: "USD" }).format(Number(n) || 0), }, // Per-cell CSS class resolver, evaluated per row. Appended after the column's StyleClasses and // its evaluated StyleClassesExpression. cellClass: (column, row) => (column.Name === "Status" && row.Cells.Status === "Overdue" ? "gx-danger" : null), // Override class names for known render slots whose styling varies by project. classNames: { attachmentCell: "my-attachment-row" }, },});The built-in If(cond, a, b) (alias IIF) mirrors the DataColumn IIF syntax authors already know
and uses = for comparison — e.g. StyleClassesExpression="If(Status = 'Overdue', 'gx-danger', 'gx-success')".
See the <Column> attributes in Model Authoring and the field rules in
Components.