Table of Contents

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:

  1. The operator clicks a menu item. The React shell issues GET /api/v2/display/<pageKey>/<id> against the FrontEnd Server.
  2. DisplayWorkflow.GetRenderV2 resolves the page template, loads its component instances in parallel via Task.WhenAll, calls each component's MapSchema / MapData, and aggregates into a single {schema, data} response.
  3. The shell dispatches schema.components[i].type to either a built-in React widget (list, form, relatedList, …) or to a widget from a customer-supplied federated remote.
  4. The widget renders its schema (column / field definitions) using its data (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 Errored component 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 Order then by TemplateComponentKey (ordinal); ties always resolve the same way across environments.
  • Per-user permission filtering — components are filtered by IHideableComponent.ShouldHideComponent and per-mediatype Permission flags from the Permission Microservice.

Customization model

The CMS is framework-thin. Almost every customer-facing concern goes through one of three customization points:

  1. 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.
  2. C# customisation DLLs. Custom components, plugins, business rules — .dll assemblies 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.
  3. React federated remotes. Custom React widgets shipped as a separate Docker image whose remoteEntry.js the 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 key mib3_*, the CLI tool mib3dev) 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