Skip to content

Custom Layouts

createFormView() is designed so you do not have to walk raw engine internals in app code.

  1. call createFormView()
  2. read snapshot.layout
  3. render nodes recursively
  4. use field.controller and report.controller
  5. update the existing DOM on subscribe()

For built-in controls inside an application-owned shell, use createPrimitiveAdapter() to mount each field or report in a stable slot. Create the shell once; replacing it on every snapshot also replaces the controls and loses their focus state.

Pseudo-code:

function renderNode(node, snapshot) {
switch (node.kind) {
case "section":
return renderSection(
node.title,
node.children.map((child) => renderNode(child, snapshot)),
);
case "group":
return renderGroup(
node.columns,
node.children.map((child) => renderNode(child, snapshot)),
);
case "field":
return renderField(snapshot.fields.find((field) => field.id === node.field));
case "report":
return renderReport(snapshot.reports.find((report) => report.id === node.report));
case "custom":
return renderApplicationRegion(node.id, node.fields);
}
}
  • field.state.visible comes from engine conditions
  • field.visibleInLayout comes from the active layout step or active tab

Use both in wizard-like or tabbed UIs.

Fastest path:

  • mlf-field-frame
  • mlf-report-frame

This keeps MLForm’s built-in field and report rendering.

Best when you need:

  • custom navigation
  • app-owned page chrome
  • tabs or disclosures
  • layout integrated into an existing dashboard
  • sidebar wizard
  • top tabs
  • mobile disclosure
  • review/summary page
  • split analyst workspace

Use mountForm() if:

  • the built-in UI already matches your needs
  • you do not want to own host rendering
  • you want the lowest maintenance surface