FrontEnd Server — Overview
The FrontEnd Server is the Backend-for-Frontend (BFF) that drives the Media-iBox CMS operator UI. It is the orchestration tier between the React shell the operator sees and the Data API + microservices that store the content. This page is the mental model — read this first, then jump into the deeper docs in the sidebar.
Just the BFF. This server does not host the React shell. The shell is a separate Docker container served by nginx out of the MibFrontEnd repo. The FrontEnd Server only emits the page-level
{schema, data}JSON the shell consumes.
Two-tier topology
flowchart LR
subgraph Browser
SPA["React shell<br/>apps/cms · MibFrontEnd"]
end
subgraph FEServer["FrontEnd Server pod"]
BFF[".NET 8 BFF<br/>DisplayWorkflow.GetRenderV2"]
CUST["Customer customisation DLLs<br/>(any number of overlays)"]
end
subgraph Remotes["Federated React remotes (nginx)"]
R1["customComponents<br/>(customer-supplied widgets)"]
R2["customComponents2 / 3 ..."]
end
SPA -- "/api/v2/display/<page>/<id>" --> BFF
SPA -- "GET remoteEntry.js" --> R1
SPA -- "GET remoteEntry.js" --> R2
BFF --> CUST
BFF --> DataAPI["MibApi<br/>(Data API)"]
BFF --> AuthMS["Authorization MS<br/>(OAuth + users)"]
BFF --> PermMS["Permission MS"]
BFF --> ConcMS["Concurrency MS"]
BFF --> EditMS["EditHistory MS"]
style BFF fill:#dde,stroke:#557
style SPA fill:#ded,stroke:#575
Each render walks this path:
- The operator clicks a menu item. The React shell issues
GET /api/v2/display/<pageKey>/<id>against the FrontEnd Server. DisplayWorkflow.GetRenderV2resolves the page template, loads its component instances in parallel viaTask.WhenAll, calls each component'sMapSchema/MapData, and aggregates into a single{schema, data}response.- The shell dispatches
schema.components[i].typeto either a built-in React widget (list,form,relatedList, …) or to a widget from a customer-supplied federated remote. - The widget renders its
schema(column / field definitions) using itsdata(values).
For the full RenderV2 mental model, see System Overview — the RenderV2 request, end-to-end.
What you actually configure
Two complementary surfaces:
1. Data-model configuration (no code)
Pages, templates, menus, translations, themes — these are stored in the FrontEnd DB and the dictionary system, edited via SQL migrations or admin tools. No C# code, no React code.
| Concern | Doc |
|---|---|
| Pages and templates (the slot layout of a page) | Pages & Templates |
| Left-side navigation | Menus |
| Translation dictionaries | Localization |
| Colors, logo, theme | Theming & CSS |
2. Custom component development (code)
When the stock built-in components aren't enough — when an editor
needs a widget that does something the framework doesn't provide —
you ship code. The unit of work is one C# class plus one React
component, joined by a component-type string resolved from
MIB3UX_TEMPLATE_COMPONENTS.COMPONENT_VIEW_TYPE, IComponentTypeProvider,
or the class name minus the Component suffix.
| Concern | Doc |
|---|---|
| Step-by-step build from zero (Docker stack + migrations + custom backend + custom React) | Hands-On Tutorial |
| End-to-end recipe (C# + React) | Component Authoring |
| Buttons, form fields, tables, toasts — the curated antd wrapper | UI Primitives — @agilecontent/ui |
| The shell's React hooks and event surface custom widgets can call | React Hooks & Events |
| How custom JS reaches the browser (federated remote bundling) | Shipping Custom JavaScript |
3. Built-in components and plugins
Ten Built-in Components handle the
common patterns — lists, forms, related-record sets, content-criteria
expressions, etc. — without writing any code. Each is configured per
page via rows in MIB3UX_PAGE_COMPONENT_CONFIGURATIONS.
For server-side hooks into the save / display pipeline that don't warrant a new component, use a Plugin:
- Custom Validation — field-level validation rules.
- Custom Business Rule — gate save / persist operations.
- Image URL Resolver — rewrite image URLs at render time.
- Page Persistence — generic save-pipeline hook.
How a request actually reaches a widget
For a custom widget on a page key articles_edit:
Operator clicks → React shell sends:
GET /api/v2/display/articles_edit/12345
│
▼ (handled by DisplayController in MibServer3)
DisplayWorkflow.GetRenderV2
│
▼ (parallel per component)
ComponentEvaluator.EvaluateAsync(component)
├── instance.ShouldHideComponent(ctx) ← optional
├── instance.MapSchema(viewData, ct) ← required
└── instance.MapData(viewData, ct) ← optional (null = legal)
│
▼
Aggregate, sort by Order + TemplateComponentKey
│
▼ (single JSON response)
{ pageKey, schema: {components[]}, data: {components[]} }
│
▼ (React shell)
buildComponents(schema.components)
├── 'form' / 'list' / 'relatedList' / ... → built-in widget
└── 'my_custom_widget' → federated remote
│
▼
<MyCustomWidget schema={...} data={...} setDirty={...} />
The framework guarantees:
- Parallel evaluation — slow components don't slow each other down.
- Failure isolation — a single
Erroredcomponent is dropped, the rest of the page still renders. Failures emit a[Render-ComponentFailure]log line; see System Overview — observability. - Deterministic ordering — components are sorted by their
configured
Orderthen byTemplateComponentKey(ordinal); ties always resolve the same way across environments. - Per-user permission filtering — components are filtered by
IHideableComponent.ShouldHideComponentand per-mediatypePermissionflags from the Permission Microservice.
Customization model
The CMS is framework-thin. Almost every customer-facing concern goes through one of three customization points:
- DB-level configuration. Page templates, component instances,
menu entries, dictionaries — rows in
MIB3UX_*tables and dictionary files. Updated via migrations from a customer's customisation repository. - C# customisation DLLs. Custom components, plugins, business
rules —
.dllassemblies overlaid into/app/on top of the customer base image. See System Overview — backend overlay system for how a deploy stitches DLLs together at image build time. - React federated remotes. Custom React widgets shipped as a
separate Docker image whose
remoteEntry.jsthe shell loads at runtime. Up to three federation slots (customComponents,customComponents2,customComponents3).
There is no framework code change required to add a custom
component. The framework's React shell merges every remote's exported
component map at startup; the framework's C# BFF reads
MIB3UX_COMPONENTS rows from the database to learn which assembly +
class to load on demand for each component type. See
Component Authoring — Reflection-based instantiation
for the exact mechanism.
Mapping doc → typical task
Use this map when you arrive at a problem:
| You want to … | Read |
|---|---|
| Install / deploy the BFF on a new env | Installation |
| Run the BFF locally for component dev | Local Development |
| Add a new page to a customer's UI | Pages & Templates |
| Add a menu entry that opens that page | Menus |
| Change the operator-facing language strings | Localization |
| Apply customer branding (logo, colors) | Theming & CSS |
| Build a new widget that doesn't yet exist | Component Authoring |
| Drive shell state from a custom widget | React Hooks & Events |
| Add a custom action button on a related list | React Hooks & Events |
| Validate a field at save time | Plugin — Custom Validation |
| Reject a save based on cross-field state | Plugin — Custom Business Rule |
| Configure a stock List on a page | Built-in: List Component |
| Configure a stock Form on a page | Built-in: Form Component |
| Configure a Content Criteria expression | Built-in: Content Criteria |
| Show edit history on a page | Audit Trail |
| Show who else is editing a record | Concurrency |
Maintain an older customer still on ~/Display/ URLs |
Legacy MVC — IIS Installation, Legacy Component Model, Legacy JavaScript API |
Naming & version notes
- "MIB3" is the historical product name. In the modern (React)
era you'll see it in code (
MIB3UX_*table prefix, the page keymib3_*, the CLI toolmib3dev) but the operator-facing product is just Media-iBox CMS. This documentation section is titled FrontEnd Server without the (MIB3) qualifier going forward. - ".NET Framework" referred to the legacy IIS install path. The modern path is .NET 8 on Linux containers, documented in Installation. The legacy path is preserved in Legacy MVC — IIS Installation for customers who haven't migrated yet.
- MibFrontEnd is the React shell repo (separate codebase, separate Docker image). When this docs section says "the React shell", that's what it means.
What's deprecated
Anything documented under the Legacy MVC (deprecated) section is end-of-life. New code should not target it. Migration paths:
| Legacy | Modern replacement |
|---|---|
IRenderableComponent / IRefreshableComponent / IPersistableComponent C# interfaces |
IRenderableComponentV2<TConfig, TData, TSchema> — see Component Authoring |
~/Display/<pageKey> URL routing |
~/app/<pageKey> via the React shell |
@Html.DelayedScript inline-script helper |
JS inside a federated remote — see Shipping Custom JavaScript |
MIB.Template.Manager.OnCollectData / OnPersistDeliverResponses JS API |
useContentEdition().onSave(...) hook — see React Hooks & Events |
MIB.Selection.Open(...) aside-selector global |
useSelection().open(...) hook |
MIB.Alerts.* global alerts |
useNotification() hook |
<page>/<config>=CUSTOM_JAVASCRIPT=url per-page JS embed |
Federated remote |
Razor views (Views/...cshtml) for components |
React widgets in a federated remote |
The legacy docs are linked from each migration row above and remain in the sidebar under Legacy MVC.
See also
- System Overview — the LLM-entrypoint doc for the whole CMS; this FrontEnd Server doc is a subset focused on the BFF + customization.
- About — high-level architecture — original Media-iBox container topology.
- Built-in Components — the ten stock widgets that handle most pages without code.